keycloak-aplcache
Changes
examples/kerberos/kerberosrealm.json 4(+0 -4)
examples/ldap/ldaprealm.json 16(+0 -16)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientResource.java 37(+37 -0)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientScopeResource.java 8(+4 -4)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientScopesResource.java 10(+5 -5)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java 33(+30 -3)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientScopeAdapter.java 128(+11 -117)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java 36(+15 -21)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClientScope.java 60(+3 -57)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java 32(+24 -8)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRole.java 6(+0 -6)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java 17(+6 -11)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientTemplateEvent.java 3(+3 -0)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java 37(+18 -19)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java 8(+4 -4)
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java 20(+13 -7)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticatedClientSessionAdapter.java 38(+0 -38)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticationSessionAdapter.java 22(+5 -17)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java 28(+0 -28)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java 30(+9 -21)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java 2(+0 -2)
model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java 1(+0 -1)
model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java 2(+0 -2)
model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java 4(+0 -4)
model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java 2(+0 -2)
model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java 4(+0 -4)
model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java 3(+1 -2)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeClientMappingEntity.java 148(+148 -0)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeRoleMappingEntity.java 49(+24 -25)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/DefaultClientScopeRealmMappingEntity.java 148(+148 -0)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentClientScopeEntity.java 64(+32 -32)
model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentProtocolMapperEntity.java 137(+0 -137)
model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserConsentClientScopeEntity.java 64(+32 -32)
model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserConsentProtocolMapperEntity.java 137(+0 -137)
server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java 64(+20 -44)
server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java 1(+0 -1)
server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java 258(+124 -134)
server-spi-private/src/main/java/org/keycloak/storage/client/AbstractReadOnlyClientStorageAdapter.java 20(+3 -17)
services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java 7(+6 -1)
services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java 49(+18 -31)
services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java 21(+4 -17)
services/src/main/java/org/keycloak/protocol/oidc/mappers/ScriptBasedOIDCProtocolMapper.java 2(+0 -2)
services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java 33(+16 -17)
services/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java 5(+2 -3)
services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java 2(+1 -1)
services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java 9(+4 -5)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientScopesClientRegistrationPolicy.java 85(+64 -21)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ClientScopesClientRegistrationPolicyFactory.java 44(+29 -15)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java 28(+0 -28)
services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java 10(+0 -10)
services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateResource.java 289(+289 -0)
services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java 135(+135 -0)
services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java 2(+1 -1)
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java 22(+11 -11)
services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java 34(+17 -17)
services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory 2(+1 -1)
testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java 28(+10 -18)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java 11(+11 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java 40(+11 -29)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java 31(+31 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java 1(+1 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java 18(+18 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java 6(+6 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java 29(+13 -16)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java 4(+0 -4)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java 4(+2 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UserStorageConsentTest.java 16(+6 -10)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java 4(+0 -4)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java 8(+4 -4)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeProtocolMapperTest.java 73(+35 -38)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java 494(+494 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientTemplateTest.java 419(+0 -419)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java 9(+4 -5)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java 14(+6 -8)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java 3(+0 -3)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialexport/PartialExportTest.java 21(+16 -5)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java 162(+56 -106)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java 5(+4 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java 1(+0 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java 1(+0 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathWithoutGroupClaimPolicyTest.java 1(+0 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java 1(+0 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java 2(+0 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java 3(+0 -3)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java 4(+2 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java 157(+68 -89)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java 175(+96 -79)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/kerberos/AbstractKerberosTest.java 1(+0 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java 27(+1 -26)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java 61(+52 -9)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java 1(+1 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java 89(+28 -61)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java 145(+60 -85)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthProofKeyForCodeExchangeTest.java 4(+2 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthScopeInTokenResponseTest.java 135(+33 -102)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java 56(+40 -16)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java 20(+9 -11)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java 1(+0 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java 4(+4 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCScopeTest.java 583(+583 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java 4(+3 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java 38(+24 -14)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientManager.java 19(+18 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java 18(+5 -13)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java 5(+0 -5)
testsuite/integration-arquillian/tests/other/springboot-tests/src/test/java/org/keycloak/testsuite/springboot/OfflineTokenSpringBootTest.java 4(+2 -2)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPMultipleAttributesTest.java 6(+4 -2)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java 2(+2 -0)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/helper/adapter/SamlAdapterTestStrategy.java 6(+3 -3)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java 1(+0 -1)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java 153(+147 -6)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClusterInvalidationTest.java 28(+14 -14)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ImportTest.java 313(+2 -311)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java 128(+33 -95)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java 128(+33 -95)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java 22(+5 -17)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java 31(+9 -22)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java 21(+9 -12)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java 29(+6 -23)
testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java 95(+0 -95)
testsuite/integration-deprecated/src/test/resources/broker-test/realm-with-oidc-property-mappers.json 1(+1 -0)
testsuite/integration-deprecated/src/test/resources/broker-test/test-broker-realm-with-saml.json 3(+0 -3)
themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappers-add.html 6(+3 -3)
themes/src/main/resources/theme/base/admin/resources/partials/client-scope-protocol-mapper-detail.html 6(+3 -3)
themes/src/main/resources/theme/base/admin/resources/partials/client-scope-scope-mappings.html 21(+5 -16)
themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html 239(+239 -0)
themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-realm-default.html 91(+91 -0)
themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html 15(+0 -15)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts 94(+47 -47)
themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js 11(+10 -1)
themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties 12(+6 -6)
themes/src/main/resources-community/theme/base/admin/messages/admin-messages_lt.properties 12(+6 -6)
themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties 12(+6 -6)
themes/src/main/resources-community/theme/base/admin/messages/admin-messages_pt_BR.properties 14(+7 -7)
Details
diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js
index fcd6b3c..0edde89 100755
--- a/adapters/oidc/js/src/main/resources/keycloak.js
+++ b/adapters/oidc/js/src/main/resources/keycloak.js
@@ -250,7 +250,16 @@
baseUrl = kc.endpoints.authorize();
}
- var scope = (options && options.scope) ? "openid " + options.scope : "openid";
+ var scope;
+ if (options && options.scope) {
+ if (options.scope.indexOf("openid") != -1) {
+ scope = options.scope;
+ } else {
+ scope = "openid " + options.scope;
+ }
+ } else {
+ scope = "openid";
+ }
var url = baseUrl
+ '?client_id=' + encodeURIComponent(kc.clientId)
diff --git a/core/src/main/java/org/keycloak/representations/AccessToken.java b/core/src/main/java/org/keycloak/representations/AccessToken.java
index 02ff2e0..880b949 100755
--- a/core/src/main/java/org/keycloak/representations/AccessToken.java
+++ b/core/src/main/java/org/keycloak/representations/AccessToken.java
@@ -97,6 +97,21 @@ public class AccessToken extends IDToken {
}
}
+ // KEYCLOAK-6771 Certificate Bound Token
+ // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3.1
+ public static class CertConf {
+ @JsonProperty("x5t#S256")
+ protected String certThumbprint;
+
+ public String getCertThumbprint() {
+ return certThumbprint;
+ }
+
+ public void setCertThumbprint(String certThumbprint) {
+ this.certThumbprint = certThumbprint;
+ }
+ }
+
@JsonProperty("trusted-certs")
protected Set<String> trustedCertificates;
@@ -112,6 +127,12 @@ public class AccessToken extends IDToken {
@JsonProperty("authorization")
protected Authorization authorization;
+ @JsonProperty("cnf")
+ protected CertConf certConf;
+
+ @JsonProperty("scope")
+ protected String scope;
+
public Map<String, Access> getResourceAccess() {
return resourceAccess;
}
@@ -233,24 +254,6 @@ public class AccessToken extends IDToken {
public void setAuthorization(Authorization authorization) {
this.authorization = authorization;
}
-
- // KEYCLOAK-6771 Certificate Bound Token
- // https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3.1
- public static class CertConf {
- @JsonProperty("x5t#S256")
- protected String certThumbprint;
-
- public String getCertThumbprint() {
- return certThumbprint;
- }
-
- public void setCertThumbprint(String certThumbprint) {
- this.certThumbprint = certThumbprint;
- }
- }
-
- @JsonProperty("cnf")
- protected CertConf certConf;
public CertConf getCertConf() {
return certConf;
@@ -259,4 +262,12 @@ public class AccessToken extends IDToken {
public void setCertConf(CertConf certConf) {
this.certConf = certConf;
}
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
index 83c4f8a..6258c88 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientRepresentation.java
@@ -61,10 +61,19 @@ public class ClientRepresentation {
protected Integer nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
protected List<ProtocolMapperRepresentation> protocolMappers;
+
+ @Deprecated
protected String clientTemplate;
+ @Deprecated
private Boolean useTemplateConfig;
+ @Deprecated
private Boolean useTemplateScope;
+ @Deprecated
private Boolean useTemplateMappers;
+
+ protected List<String> defaultClientScopes;
+ protected List<String> optionalClientScopes;
+
private ResourceServerRepresentation authorizationSettings;
private Map<String, Boolean> access;
protected String origin;
@@ -338,36 +347,40 @@ public class ClientRepresentation {
this.protocolMappers = protocolMappers;
}
+ @Deprecated
public String getClientTemplate() {
return clientTemplate;
}
- public void setClientTemplate(String clientTemplate) {
- this.clientTemplate = clientTemplate;
- }
-
+ @Deprecated
public Boolean isUseTemplateConfig() {
return useTemplateConfig;
}
- public void setUseTemplateConfig(Boolean useTemplateConfig) {
- this.useTemplateConfig = useTemplateConfig;
- }
-
+ @Deprecated
public Boolean isUseTemplateScope() {
return useTemplateScope;
}
- public void setUseTemplateScope(Boolean useTemplateScope) {
- this.useTemplateScope = useTemplateScope;
- }
-
+ @Deprecated
public Boolean isUseTemplateMappers() {
return useTemplateMappers;
}
- public void setUseTemplateMappers(Boolean useTemplateMappers) {
- this.useTemplateMappers = useTemplateMappers;
+ public List<String> getDefaultClientScopes() {
+ return defaultClientScopes;
+ }
+
+ public void setDefaultClientScopes(List<String> defaultClientScopes) {
+ this.defaultClientScopes = defaultClientScopes;
+ }
+
+ public List<String> getOptionalClientScopes() {
+ return optionalClientScopes;
+ }
+
+ public void setOptionalClientScopes(List<String> optionalClientScopes) {
+ this.optionalClientScopes = optionalClientScopes;
}
public ResourceServerRepresentation getAuthorizationSettings() {
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientScopeRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientScopeRepresentation.java
new file mode 100755
index 0000000..c1fbfad
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientScopeRepresentation.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.representations.idm;
+
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@JsonIgnoreProperties(ignoreUnknown=true) // Backwards compatibility of admin REST endpoints (ClientTemplateRepresentation was more rich)
+public class ClientScopeRepresentation {
+
+ protected String id;
+ protected String name;
+ protected String description;
+ protected String protocol;
+ protected Map<String, String> attributes;
+
+ protected List<ProtocolMapperRepresentation> protocolMappers;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+
+ public List<ProtocolMapperRepresentation> getProtocolMappers() {
+ return protocolMappers;
+ }
+
+ public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
+ this.protocolMappers = protocolMappers;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(Map<String, String> attributes) {
+ this.attributes = attributes;
+ }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
old mode 100755
new mode 100644
index 934bb90..3ad7cc4
--- a/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java
@@ -24,6 +24,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
+@Deprecated // Use ClientScopeRepresentation instead
public class ClientTemplateRepresentation {
/**
* Use this value in ClientRepresentation.setClientTemplate when you want to clear this value
@@ -167,3 +168,4 @@ public class ClientTemplateRepresentation {
this.attributes = attributes;
}
}
+
diff --git a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java
index 5c64361..b07a8d1 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ProtocolMapperRepresentation.java
@@ -29,7 +29,11 @@ public class ProtocolMapperRepresentation {
protected String name;
protected String protocol;
protected String protocolMapper;
+
+ @Deprecated // backwards compatibility only
protected boolean consentRequired;
+
+ @Deprecated // backwards compatibility only
protected String consentText;
protected Map<String, String> config = new HashMap<String, String>();
@@ -74,19 +78,14 @@ public class ProtocolMapperRepresentation {
this.config = config;
}
+ @Deprecated
public boolean isConsentRequired() {
return consentRequired;
}
- public void setConsentRequired(boolean consentRequired) {
- this.consentRequired = consentRequired;
- }
-
+ @Deprecated
public String getConsentText() {
return consentText;
}
- public void setConsentText(String consentText) {
- this.consentText = consentText;
- }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index 1c039d3..200e4e1 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -106,7 +106,9 @@ public class RealmRepresentation {
protected List<ScopeMappingRepresentation> scopeMappings;
protected Map<String, List<ScopeMappingRepresentation>> clientScopeMappings;
protected List<ClientRepresentation> clients;
- protected List<ClientTemplateRepresentation> clientTemplates;
+ protected List<ClientScopeRepresentation> clientScopes;
+ protected List<String> defaultDefaultClientScopes;
+ protected List<String> defaultOptionalClientScopes;
protected Map<String, String> browserSecurityHeaders;
protected Map<String, String> smtpServer;
protected List<UserFederationProviderRepresentation> userFederationProviders;
@@ -159,6 +161,8 @@ public class RealmRepresentation {
protected List<ApplicationRepresentation> applications;
@Deprecated
protected List<OAuthClientRepresentation> oauthClients;
+ @Deprecated
+ protected List<ClientTemplateRepresentation> clientTemplates;
public String getId() {
return id;
@@ -304,9 +308,9 @@ public class RealmRepresentation {
return mapping;
}
- public ScopeMappingRepresentation clientTemplateScopeMapping(String clientTemplateName) {
+ public ScopeMappingRepresentation clientScopeScopeMapping(String clientScopeName) {
ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
- mapping.setClientTemplate(clientTemplateName);
+ mapping.setClientScope(clientScopeName);
if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
scopeMappings.add(mapping);
return mapping;
@@ -930,12 +934,33 @@ public class RealmRepresentation {
this.groups = groups;
}
+ @Deprecated // use getClientScopes() instead
public List<ClientTemplateRepresentation> getClientTemplates() {
return clientTemplates;
}
- public void setClientTemplates(List<ClientTemplateRepresentation> clientTemplates) {
- this.clientTemplates = clientTemplates;
+ public List<ClientScopeRepresentation> getClientScopes() {
+ return clientScopes;
+ }
+
+ public void setClientScopes(List<ClientScopeRepresentation> clientScopes) {
+ this.clientScopes = clientScopes;
+ }
+
+ public List<String> getDefaultDefaultClientScopes() {
+ return defaultDefaultClientScopes;
+ }
+
+ public void setDefaultDefaultClientScopes(List<String> defaultDefaultClientScopes) {
+ this.defaultDefaultClientScopes = defaultDefaultClientScopes;
+ }
+
+ public List<String> getDefaultOptionalClientScopes() {
+ return defaultOptionalClientScopes;
+ }
+
+ public void setDefaultOptionalClientScopes(List<String> defaultOptionalClientScopes) {
+ this.defaultOptionalClientScopes = defaultOptionalClientScopes;
}
public MultivaluedHashMap<String, ComponentExportRepresentation> getComponents() {
diff --git a/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java
index 9614ca3..8f361b3 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java
@@ -29,6 +29,7 @@ public class RoleRepresentation {
protected String id;
protected String name;
protected String description;
+ @Deprecated
protected Boolean scopeParamRequired;
protected boolean composite;
protected Composites composites;
@@ -96,14 +97,11 @@ public class RoleRepresentation {
this.description = description;
}
+ @Deprecated
public Boolean isScopeParamRequired() {
return scopeParamRequired;
}
- public void setScopeParamRequired(Boolean scopeParamRequired) {
- this.scopeParamRequired = scopeParamRequired;
- }
-
public Composites getComposites() {
return composites;
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/ScopeMappingRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ScopeMappingRepresentation.java
index f8e2a7b..488f250 100755
--- a/core/src/main/java/org/keycloak/representations/idm/ScopeMappingRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/ScopeMappingRepresentation.java
@@ -27,7 +27,10 @@ import java.util.Set;
public class ScopeMappingRepresentation {
protected String self; // link
protected String client;
+
+ @Deprecated // Replaced by clientScope
protected String clientTemplate;
+ protected String clientScope;
protected Set<String> roles;
public String getSelf() {
@@ -46,12 +49,17 @@ public class ScopeMappingRepresentation {
this.client = client;
}
+ @Deprecated
public String getClientTemplate() {
return clientTemplate;
}
- public void setClientTemplate(String clientTemplate) {
- this.clientTemplate = clientTemplate;
+ public String getClientScope() {
+ return clientScope;
+ }
+
+ public void setClientScope(String clientScope) {
+ this.clientScope = clientScope;
}
public Set<String> getRoles() {
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
index 681a03b..82915ba 100644
--- a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
@@ -20,25 +20,25 @@ package org.keycloak.representations.idm;
import java.util.List;
import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
+@JsonIgnoreProperties(ignoreUnknown=true)
public class UserConsentRepresentation {
protected String clientId;
- // Key is protocol, Value is list of granted consents for this protocol
- protected Map<String, List<String>> grantedProtocolMappers;
-
- protected List<String> grantedRealmRoles;
-
- // Key is clientId, Value is list of granted roles of this client
- protected Map<String, List<String>> grantedClientRoles;
+ protected List<String> grantedClientScopes;
private Long createdDate;
private Long lastUpdatedDate;
+ @Deprecated
+ protected List<String> grantedRealmRoles;
+
public String getClientId() {
return clientId;
}
@@ -47,28 +47,12 @@ public class UserConsentRepresentation {
this.clientId = clientId;
}
- public Map<String, List<String>> getGrantedProtocolMappers() {
- return grantedProtocolMappers;
- }
-
- public void setGrantedProtocolMappers(Map<String, List<String>> grantedProtocolMappers) {
- this.grantedProtocolMappers = grantedProtocolMappers;
- }
-
- public List<String> getGrantedRealmRoles() {
- return grantedRealmRoles;
- }
-
- public void setGrantedRealmRoles(List<String> grantedRealmRoles) {
- this.grantedRealmRoles = grantedRealmRoles;
- }
-
- public Map<String, List<String>> getGrantedClientRoles() {
- return grantedClientRoles;
+ public List<String> getGrantedClientScopes() {
+ return grantedClientScopes;
}
- public void setGrantedClientRoles(Map<String, List<String>> grantedClientRoles) {
- this.grantedClientRoles = grantedClientRoles;
+ public void setGrantedClientScopes(List<String> grantedClientScopes) {
+ this.grantedClientScopes = grantedClientScopes;
}
public void setCreatedDate(Long createdDate) {
@@ -86,4 +70,9 @@ public class UserConsentRepresentation {
public Long getLastUpdatedDate() {
return lastUpdatedDate;
}
+
+ @Deprecated
+ public List<String> getGrantedRealmRoles() {
+ return grantedRealmRoles;
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/RefreshToken.java b/core/src/main/java/org/keycloak/representations/RefreshToken.java
index 3a7a951..24ed5d1 100755
--- a/core/src/main/java/org/keycloak/representations/RefreshToken.java
+++ b/core/src/main/java/org/keycloak/representations/RefreshToken.java
@@ -46,6 +46,7 @@ public class RefreshToken extends AccessToken {
this.sessionState = token.sessionState;
this.nonce = token.nonce;
this.audience = token.audience;
+ this.scope = token.scope;
if (token.realmAccess != null) {
realmAccess = token.realmAccess.clone();
}
@@ -56,4 +57,5 @@ public class RefreshToken extends AccessToken {
}
}
}
+
}
diff --git a/core/src/main/java/org/keycloak/util/TokenUtil.java b/core/src/main/java/org/keycloak/util/TokenUtil.java
index 742983a..dd76ca1 100644
--- a/core/src/main/java/org/keycloak/util/TokenUtil.java
+++ b/core/src/main/java/org/keycloak/util/TokenUtil.java
@@ -48,6 +48,8 @@ public class TokenUtil {
public static String attachOIDCScope(String scopeParam) {
if (scopeParam == null || scopeParam.isEmpty()) {
return OAuth2Constants.SCOPE_OPENID;
+ } else if (hasScope(scopeParam, OAuth2Constants.SCOPE_OPENID)) {
+ return scopeParam;
} else {
return OAuth2Constants.SCOPE_OPENID + " " + scopeParam;
}
examples/kerberos/kerberosrealm.json 4(+0 -4)
diff --git a/examples/kerberos/kerberosrealm.json b/examples/kerberos/kerberosrealm.json
index 36a3113..4c7e55d 100644
--- a/examples/kerberos/kerberosrealm.json
+++ b/examples/kerberos/kerberosrealm.json
@@ -28,8 +28,6 @@
"protocolMapper" : "oidc-usermodel-property-mapper",
"protocol" : "openid-connect",
"name" : "username",
- "consentText" : "username",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "username",
@@ -42,8 +40,6 @@
"protocolMapper" : "oidc-usersessionmodel-note-mapper",
"protocol" : "openid-connect",
"name" : "gss delegation credential",
- "consentText" : "gss delegation credential",
- "consentRequired" : true,
"config" : {
"user.session.note" : "gss_delegation_credential",
"claim.name" : "gss_delegation_credential",
examples/ldap/ldaprealm.json 16(+0 -16)
diff --git a/examples/ldap/ldaprealm.json b/examples/ldap/ldaprealm.json
index 1630924..600d087 100644
--- a/examples/ldap/ldaprealm.json
+++ b/examples/ldap/ldaprealm.json
@@ -27,8 +27,6 @@
"protocolMapper" : "oidc-usermodel-property-mapper",
"protocol" : "openid-connect",
"name" : "username",
- "consentText" : "${username}",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "username",
@@ -41,8 +39,6 @@
"protocolMapper" : "oidc-full-name-mapper",
"protocol" : "openid-connect",
"name" : "full name",
- "consentText" : "${fullName}",
- "consentRequired" : true,
"config" : {
"id.token.claim" : "true",
"access.token.claim" : "true"
@@ -52,8 +48,6 @@
"protocolMapper" : "oidc-usermodel-property-mapper",
"protocol" : "openid-connect",
"name" : "given name",
- "consentText" : "${givenName}",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "firstName",
@@ -66,8 +60,6 @@
"protocolMapper" : "oidc-usermodel-property-mapper",
"protocol" : "openid-connect",
"name" : "family name",
- "consentText" : "${familyName}",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "lastName",
@@ -80,8 +72,6 @@
"protocolMapper" : "oidc-usermodel-property-mapper",
"protocol" : "openid-connect",
"name" : "email",
- "consentText" : "${email}",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "email",
@@ -94,8 +84,6 @@
"protocolMapper" : "oidc-usermodel-attribute-mapper",
"protocol" : "openid-connect",
"name" : "postal code",
- "consentText" : "${postal_code}",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "postal_code",
@@ -109,8 +97,6 @@
"protocolMapper" : "oidc-usermodel-attribute-mapper",
"protocol" : "openid-connect",
"name" : "street",
- "consentText" : "${street}",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "street",
@@ -124,8 +110,6 @@
"protocolMapper" : "oidc-usermodel-attribute-mapper",
"protocol" : "openid-connect",
"name" : "picture",
- "consentText" : "Picture",
- "consentRequired" : true,
"config" : {
"Claim JSON Type" : "String",
"user.attribute" : "picture",
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientResource.java
index fb9640b..7d79549 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/ClientResource.java
@@ -20,6 +20,7 @@ package org.keycloak.admin.client.resource;
import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
@@ -122,6 +123,42 @@ public interface ClientResource {
@Path("/roles")
public RolesResource roles();
+ /**
+ * Get default client scopes. Only name and ids are returned.
+ *
+ * @return default client scopes
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("default-client-scopes")
+ List<ClientScopeRepresentation> getDefaultClientScopes();
+
+ @PUT
+ @Path("default-client-scopes/{clientScopeId}")
+ void addDefaultClientScope(@PathParam("clientScopeId") String clientScopeId);
+
+ @DELETE
+ @Path("default-client-scopes/{clientScopeId}")
+ void removeDefaultClientScope(@PathParam("clientScopeId") String clientScopeId);
+
+ /**
+ * Get optional client scopes. Only name and ids are returned.
+ *
+ * @return optional client scopes
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("optional-client-scopes")
+ List<ClientScopeRepresentation> getOptionalClientScopes();
+
+ @PUT
+ @Path("optional-client-scopes/{clientScopeId}")
+ void addOptionalClientScope(@PathParam("clientScopeId") String clientScopeId);
+
+ @DELETE
+ @Path("optional-client-scopes/{clientScopeId}")
+ void removeOptionalClientScope(@PathParam("clientScopeId") String clientScopeId);
+
@Path("/service-account-user")
@GET
@NoCache
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
index ffcd178..0833011 100644
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RealmResource.java
@@ -21,6 +21,7 @@ import org.jboss.resteasy.annotations.cache.NoCache;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.PartialImportRepresentation;
@@ -39,7 +40,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.io.IOException;
+
import java.util.List;
import java.util.Map;
@@ -59,8 +60,34 @@ public interface RealmResource {
@Path("clients")
ClientsResource clients();
- @Path("client-templates")
- ClientTemplatesResource clientTemplates();
+ @Path("client-scopes")
+ ClientScopesResource clientScopes();
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("default-default-client-scopes")
+ List<ClientScopeRepresentation> getDefaultDefaultClientScopes();
+
+ @PUT
+ @Path("default-default-client-scopes/{clientScopeId}")
+ void addDefaultDefaultClientScope(@PathParam("clientScopeId") String clientScopeId);
+
+ @DELETE
+ @Path("default-default-client-scopes/{clientScopeId}")
+ void removeDefaultDefaultClientScope(@PathParam("clientScopeId") String clientScopeId);
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("default-optional-client-scopes")
+ List<ClientScopeRepresentation> getDefaultOptionalClientScopes();
+
+ @PUT
+ @Path("default-optional-client-scopes/{clientScopeId}")
+ void addDefaultOptionalClientScope(@PathParam("clientScopeId") String clientScopeId);
+
+ @DELETE
+ @Path("default-optional-client-scopes/{clientScopeId}")
+ void removeDefaultOptionalClientScope(@PathParam("clientScopeId") String clientScopeId);
@Path("client-description-converter")
@POST
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
index 4c28ad6..a406f23 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientAdapter.java
@@ -18,7 +18,7 @@
package org.keycloak.models.cache.infinispan;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
@@ -97,60 +97,37 @@ public class ClientAdapter implements ClientModel, CachedObject {
}
@Override
- public ClientTemplateModel getClientTemplate() {
- if (isUpdated()) return updated.getClientTemplate();
- if (cached.getClientTemplate() == null) return null;
- return cacheSession.getClientTemplateById(cached.getClientTemplate(), cachedRealm);
- }
-
- @Override
- public void setClientTemplate(ClientTemplateModel template) {
- getDelegateForUpdate();
- updated.setClientTemplate(template);
-
- }
-
- @Override
- public boolean useTemplateScope() {
- if (isUpdated()) return updated.useTemplateScope();
- return cached.isUseTemplateScope();
- }
-
- @Override
- public void setUseTemplateScope(boolean value) {
+ public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
getDelegateForUpdate();
- updated.setUseTemplateScope(value);
-
+ updated.addClientScope(clientScope, defaultScope);
}
@Override
- public boolean useTemplateConfig() {
- if (isUpdated()) return updated.useTemplateConfig();
- return cached.isUseTemplateConfig();
- }
-
- @Override
- public void setUseTemplateConfig(boolean value) {
+ public void removeClientScope(ClientScopeModel clientScope) {
getDelegateForUpdate();
- updated.setUseTemplateConfig(value);
-
+ updated.removeClientScope(clientScope);
}
@Override
- public boolean useTemplateMappers() {
- if (isUpdated()) return updated.useTemplateMappers();
- return cached.isUseTemplateMappers();
- }
+ public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
+ if (isUpdated()) return updated.getClientScopes(defaultScope, filterByProtocol);
+ List<String> clientScopeIds = defaultScope ? cached.getDefaultClientScopesIds() : cached.getOptionalClientScopesIds();
- @Override
- public void setUseTemplateMappers(boolean value) {
- getDelegateForUpdate();
- updated.setUseTemplateMappers(value);
+ // Defaults to openid-connect
+ String clientProtocol = getProtocol() == null ? "openid-connect" : getProtocol();
+ Map<String, ClientScopeModel> clientScopes = new HashMap<>();
+ for (String scopeId : clientScopeIds) {
+ ClientScopeModel clientScope = cacheSession.getClientScopeById(scopeId, cachedRealm);
+ if (clientScope != null) {
+ if (!filterByProtocol || clientScope.getProtocol().equals(clientProtocol)) {
+ clientScopes.put(clientScope.getName(), clientScope);
+ }
+ }
+ }
+ return clientScopes;
}
-
-
public void addWebOrigin(String webOrigin) {
getDelegateForUpdate();
updated.addWebOrigin(webOrigin);
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
index 787dc45..f6f288b 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedClient.java
@@ -18,6 +18,7 @@
package org.keycloak.models.cache.infinispan.entities;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@@ -67,10 +68,8 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
protected boolean serviceAccountsEnabled;
protected int nodeReRegistrationTimeout;
protected Map<String, Integer> registeredNodes;
- protected String clientTemplate;
- protected boolean useTemplateScope;
- protected boolean useTemplateConfig;
- protected boolean useTemplateMappers;
+ protected List<String> defaultClientScopesIds;
+ protected List<String> optionalClientScopesIds;
public CachedClient(Long revision, RealmModel realm, ClientModel model) {
super(revision, model.getId());
@@ -111,12 +110,15 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
nodeReRegistrationTimeout = model.getNodeReRegistrationTimeout();
registeredNodes = new TreeMap<>(model.getRegisteredNodes());
- if (model.getClientTemplate() != null) {
- clientTemplate = model.getClientTemplate().getId();
+
+ defaultClientScopesIds = new LinkedList<>();
+ for (ClientScopeModel clientScope : model.getClientScopes(true, false).values()) {
+ defaultClientScopesIds.add(clientScope.getId());
+ }
+ optionalClientScopesIds = new LinkedList<>();
+ for (ClientScopeModel clientScope : model.getClientScopes(false, false).values()) {
+ optionalClientScopesIds.add(clientScope.getId());
}
- useTemplateConfig = model.useTemplateConfig();
- useTemplateMappers = model.useTemplateMappers();
- useTemplateScope = model.useTemplateScope();
}
public String getClientId() {
@@ -243,20 +245,12 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
return registeredNodes;
}
- public String getClientTemplate() {
- return clientTemplate;
- }
-
- public boolean isUseTemplateScope() {
- return useTemplateScope;
- }
-
- public boolean isUseTemplateConfig() {
- return useTemplateConfig;
+ public List<String> getDefaultClientScopesIds() {
+ return defaultClientScopesIds;
}
- public boolean isUseTemplateMappers() {
- return useTemplateMappers;
+ public List<String> getOptionalClientScopesIds() {
+ return optionalClientScopesIds;
}
public Map<String, String> getAuthFlowBindings() {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
index f187e9c..7f9cbed 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRealm.java
@@ -24,7 +24,7 @@ import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
@@ -135,7 +135,9 @@ public class CachedRealm extends AbstractExtendableRevisioned {
}
protected List<String> defaultGroups = new LinkedList<String>();
- protected List<String> clientTemplates= new LinkedList<>();
+ protected List<String> clientScopes = new LinkedList<>();
+ protected List<String> defaultDefaultClientScopes = new LinkedList<>();
+ protected List<String> optionalDefaultClientScopes = new LinkedList<>();
protected boolean internationalizationEnabled;
protected Set<String> supportedLocales;
protected String defaultLocale;
@@ -227,7 +229,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
ClientModel masterAdminClient = model.getMasterAdminClient();
this.masterAdminClient = (masterAdminClient != null) ? masterAdminClient.getId() : null;
- cacheClientTemplates(model);
+ cacheClientScopes(model);
internationalizationEnabled = model.isInternationalizationEnabled();
supportedLocales = model.getSupportedLocales();
@@ -279,9 +281,15 @@ public class CachedRealm extends AbstractExtendableRevisioned {
}
- protected void cacheClientTemplates(RealmModel model) {
- for (ClientTemplateModel template : model.getClientTemplates()) {
- clientTemplates.add(template.getId());
+ protected void cacheClientScopes(RealmModel model) {
+ for (ClientScopeModel clientScope : model.getClientScopes()) {
+ clientScopes.add(clientScope.getId());
+ }
+ for (ClientScopeModel clientScope : model.getDefaultClientScopes(true)) {
+ defaultDefaultClientScopes.add(clientScope.getId());
+ }
+ for (ClientScopeModel clientScope : model.getDefaultClientScopes(false)) {
+ optionalDefaultClientScopes.add(clientScope.getId());
}
}
@@ -585,8 +593,16 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return defaultGroups;
}
- public List<String> getClientTemplates() {
- return clientTemplates;
+ public List<String> getClientScopes() {
+ return clientScopes;
+ }
+
+ public List<String> getDefaultDefaultClientScopes() {
+ return defaultDefaultClientScopes;
+ }
+
+ public List<String> getOptionalDefaultClientScopes() {
+ return optionalDefaultClientScopes;
}
public List<AuthenticationFlowModel> getAuthenticationFlowList() {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRole.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRole.java
index 0dff46b..5ee29bc 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRole.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedRole.java
@@ -32,7 +32,6 @@ public class CachedRole extends AbstractRevisioned implements InRealm {
final protected String name;
final protected String realm;
final protected String description;
- final protected Boolean scopeParamRequired;
final protected boolean composite;
final protected Set<String> composites = new HashSet<String>();
@@ -41,7 +40,6 @@ public class CachedRole extends AbstractRevisioned implements InRealm {
composite = model.isComposite();
description = model.getDescription();
name = model.getName();
- scopeParamRequired = model.isScopeParamRequired();
this.realm = realm.getId();
if (composite) {
for (RoleModel child : model.getComposites()) {
@@ -63,10 +61,6 @@ public class CachedRole extends AbstractRevisioned implements InRealm {
return description;
}
- public Boolean isScopeParamRequired() {
- return scopeParamRequired;
- }
-
public boolean isComposite() {
return composite;
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java
index 4b24bd1..7be27e7 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java
@@ -17,6 +17,7 @@
package org.keycloak.models.cache.infinispan.entities;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
@@ -30,16 +31,14 @@ import java.util.Set;
public class CachedUserConsent {
private final String clientDbId;
- private final Set<ProtocolMapperModel> protocolMappers = new HashSet<>();
- private final Set<String> roleIds = new HashSet<>();
+ private final Set<String> clientScopeIds = new HashSet<>();
private final Long createdDate;
private final Long lastUpdatedDate;
public CachedUserConsent(UserConsentModel consentModel) {
this.clientDbId = consentModel.getClient().getId();
- this.protocolMappers.addAll(consentModel.getGrantedProtocolMappers());
- for (RoleModel role : consentModel.getGrantedRoles()) {
- this.roleIds.add(role.getId());
+ for (ClientScopeModel clientScope : consentModel.getGrantedClientScopes()) {
+ this.clientScopeIds.add(clientScope.getId());
}
this.createdDate = consentModel.getCreatedDate();
this.lastUpdatedDate = consentModel.getLastUpdatedDate();
@@ -49,12 +48,8 @@ public class CachedUserConsent {
return clientDbId;
}
- public Set<ProtocolMapperModel> getProtocolMappers() {
- return protocolMappers;
- }
-
- public Set<String> getRoleIds() {
- return roleIds;
+ public Set<String> getClientScopeIds() {
+ return clientScopeIds;
}
public Long getCreatedDate() {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientTemplateEvent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientTemplateEvent.java
index 4a49ee6..7ec3a03 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientTemplateEvent.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/events/ClientTemplateEvent.java
@@ -28,6 +28,9 @@ import org.infinispan.commons.marshall.MarshallUtil;
import org.infinispan.commons.marshall.SerializeWith;
/**
+ * TODO Leave the name ClientTemplateEvent just due the backwards compatibility of infinispan migration. See if can be renamed based on
+ * rolling upgrades plan...
+ *
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@SerializeWith(ClientTemplateEvent.ExternalizerImpl.class)
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
index 7e6c3c7..9a058b5 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmAdapter.java
@@ -1287,15 +1287,15 @@ public class RealmAdapter implements CachedRealmModel {
}
@Override
- public List<ClientTemplateModel> getClientTemplates() {
- if (isUpdated()) return updated.getClientTemplates();
- List<String> clientTemplates = cached.getClientTemplates();
- if (clientTemplates.isEmpty()) return Collections.EMPTY_LIST;
- List<ClientTemplateModel> apps = new LinkedList<ClientTemplateModel>();
- for (String id : clientTemplates) {
- ClientTemplateModel model = cacheSession.getClientTemplateById(id, this);
+ public List<ClientScopeModel> getClientScopes() {
+ if (isUpdated()) return updated.getClientScopes();
+ List<String> clientScopes = cached.getClientScopes();
+ if (clientScopes.isEmpty()) return Collections.EMPTY_LIST;
+ List<ClientScopeModel> apps = new LinkedList<ClientScopeModel>();
+ for (String id : clientScopes) {
+ ClientScopeModel model = cacheSession.getClientScopeById(id, this);
if (model == null) {
- throw new IllegalStateException("Cached clientemplate not found: " + id);
+ throw new IllegalStateException("Cached clientScope not found: " + id);
}
apps.add(model);
}
@@ -1304,32 +1304,60 @@ public class RealmAdapter implements CachedRealmModel {
}
@Override
- public ClientTemplateModel addClientTemplate(String name) {
+ public ClientScopeModel addClientScope(String name) {
getDelegateForUpdate();
- ClientTemplateModel app = updated.addClientTemplate(name);
- cacheSession.registerClientTemplateInvalidation(app.getId());
+ ClientScopeModel app = updated.addClientScope(name);
+ cacheSession.registerClientScopeInvalidation(app.getId());
return app;
}
@Override
- public ClientTemplateModel addClientTemplate(String id, String name) {
+ public ClientScopeModel addClientScope(String id, String name) {
getDelegateForUpdate();
- ClientTemplateModel app = updated.addClientTemplate(id, name);
- cacheSession.registerClientTemplateInvalidation(app.getId());
+ ClientScopeModel app = updated.addClientScope(id, name);
+ cacheSession.registerClientScopeInvalidation(app.getId());
return app;
}
@Override
- public boolean removeClientTemplate(String id) {
- cacheSession.registerClientTemplateInvalidation(id);
+ public boolean removeClientScope(String id) {
+ cacheSession.registerClientScopeInvalidation(id);
getDelegateForUpdate();
- return updated.removeClientTemplate(id);
+ return updated.removeClientScope(id);
}
@Override
- public ClientTemplateModel getClientTemplateById(String id) {
- if (isUpdated()) return updated.getClientTemplateById(id);
- return cacheSession.getClientTemplateById(id, this);
+ public ClientScopeModel getClientScopeById(String id) {
+ if (isUpdated()) return updated.getClientScopeById(id);
+ return cacheSession.getClientScopeById(id, this);
+ }
+
+ @Override
+ public void addDefaultClientScope(ClientScopeModel clientScope, boolean defaultScope) {
+ getDelegateForUpdate();
+ updated.addDefaultClientScope(clientScope, defaultScope);
+ }
+
+ @Override
+ public void removeDefaultClientScope(ClientScopeModel clientScope) {
+ getDelegateForUpdate();
+ updated.removeDefaultClientScope(clientScope);
+ }
+
+ @Override
+ public List<ClientScopeModel> getDefaultClientScopes(boolean defaultScope) {
+ if (isUpdated()) return updated.getDefaultClientScopes(defaultScope);
+
+ List<String> clientScopeIds = defaultScope ? cached.getDefaultDefaultClientScopes() : cached.getOptionalDefaultClientScopes();
+
+ List<ClientScopeModel> clientScopes = new LinkedList<>();
+ for (String scopeId : clientScopeIds) {
+ ClientScopeModel clientScope = cacheSession.getClientScopeById(scopeId, this);
+ if (clientScope != null) {
+ clientScopes.add(clientScope);
+ }
+ }
+ return clientScopes;
}
@Override
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
index 018213f..ccb4ad7 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
@@ -27,7 +27,6 @@ import org.keycloak.models.cache.CachedRealmModel;
import org.keycloak.models.cache.infinispan.entities.*;
import org.keycloak.models.cache.infinispan.events.*;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.storage.CacheableStorageProviderModel;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.client.ClientStorageProviderModel;
@@ -105,7 +104,7 @@ public class RealmCacheSession implements CacheRealmProvider {
protected Map<String, RealmAdapter> managedRealms = new HashMap<>();
protected Map<String, ClientModel> managedApplications = new HashMap<>();
- protected Map<String, ClientTemplateAdapter> managedClientTemplates = new HashMap<>();
+ protected Map<String, ClientScopeAdapter> managedClientScopes = new HashMap<>();
protected Map<String, RoleAdapter> managedRoles = new HashMap<>();
protected Map<String, GroupAdapter> managedGroups = new HashMap<>();
protected Set<String> listInvalidations = new HashSet<>();
@@ -182,16 +181,16 @@ public class RealmCacheSession implements CacheRealmProvider {
}
@Override
- public void registerClientTemplateInvalidation(String id) {
- invalidateClientTemplate(id);
- // Note: Adding/Removing client template is supposed to invalidate CachedRealm as well, so the list of clientTemplates is invalidated.
+ public void registerClientScopeInvalidation(String id) {
+ invalidateClientScope(id);
+ // Note: Adding/Removing client template is supposed to invalidate CachedRealm as well, so the list of clientScopes is invalidated.
// But separate RealmUpdatedEvent will be sent for it. So ClientTemplateEvent don't need to take care of it.
invalidationEvents.add(ClientTemplateEvent.create(id));
}
- private void invalidateClientTemplate(String id) {
+ private void invalidateClientScope(String id) {
invalidations.add(id);
- ClientTemplateAdapter adapter = managedClientTemplates.get(id);
+ ClientScopeAdapter adapter = managedClientScopes.get(id);
if (adapter != null) adapter.invalidate();
}
@@ -218,9 +217,9 @@ public class RealmCacheSession implements CacheRealmProvider {
group.invalidate();
continue;
}
- ClientTemplateAdapter clientTemplate = managedClientTemplates.get(id);
- if (clientTemplate != null) {
- clientTemplate.invalidate();
+ ClientScopeAdapter clientScope = managedClientScopes.get(id);
+ if (clientScope != null) {
+ clientScope.invalidate();
continue;
}
RoleAdapter role = managedRoles.get(id);
@@ -1134,26 +1133,26 @@ public class RealmCacheSession implements CacheRealmProvider {
}
@Override
- public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
- CachedClientTemplate cached = cache.get(id, CachedClientTemplate.class);
+ public ClientScopeModel getClientScopeById(String id, RealmModel realm) {
+ CachedClientScope cached = cache.get(id, CachedClientScope.class);
if (cached != null && !cached.getRealm().equals(realm.getId())) {
cached = null;
}
if (cached == null) {
Long loaded = cache.getCurrentRevision(id);
- ClientTemplateModel model = getRealmDelegate().getClientTemplateById(id, realm);
+ ClientScopeModel model = getRealmDelegate().getClientScopeById(id, realm);
if (model == null) return null;
if (invalidations.contains(id)) return model;
- cached = new CachedClientTemplate(loaded, realm, model);
+ cached = new CachedClientScope(loaded, realm, model);
cache.addRevisioned(cached, startupRevision);
} else if (invalidations.contains(id)) {
- return getRealmDelegate().getClientTemplateById(id, realm);
- } else if (managedClientTemplates.containsKey(id)) {
- return managedClientTemplates.get(id);
+ return getRealmDelegate().getClientScopeById(id, realm);
+ } else if (managedClientScopes.containsKey(id)) {
+ return managedClientScopes.get(id);
}
- ClientTemplateAdapter adapter = new ClientTemplateAdapter(realm, cached, this);
- managedClientTemplates.put(id, adapter);
+ ClientScopeAdapter adapter = new ClientScopeAdapter(realm, cached, this);
+ managedClientScopes.put(id, adapter);
return adapter;
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
index 24ed6d9..6a6ac86 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
@@ -89,18 +89,6 @@ public class RoleAdapter implements RoleModel {
}
@Override
- public boolean isScopeParamRequired() {
- if (isUpdated()) return updated.isScopeParamRequired();
- return cached.isScopeParamRequired();
- }
-
- @Override
- public void setScopeParamRequired(boolean scopeParamRequired) {
- getDelegateForUpdate();
- updated.setScopeParamRequired(scopeParamRequired);
- }
-
- @Override
public String getId() {
if (isUpdated()) return updated.getId();
return cached.getId();
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
index ddb3a64..3b548b9 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
@@ -1,7 +1,7 @@
package org.keycloak.models.cache.infinispan.stream;
import org.keycloak.models.cache.infinispan.entities.CachedClient;
-import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate;
+import org.keycloak.models.cache.infinispan.entities.CachedClientScope;
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
import org.keycloak.models.cache.infinispan.entities.CachedRole;
import org.keycloak.models.cache.infinispan.entities.Revisioned;
@@ -55,9 +55,9 @@ public class HasRolePredicate implements Predicate<Map.Entry<String, Revisioned>
if (cachedClient.getScope().contains(role)) return true;
}
- if (value instanceof CachedClientTemplate) {
- CachedClientTemplate cachedClientTemplate = (CachedClientTemplate)value;
- if (cachedClientTemplate.getScope().contains(role)) return true;
+ if (value instanceof CachedClientScope) {
+ CachedClientScope cachedClientScope = (CachedClientScope)value;
+ if (cachedClientScope.getScope().contains(role)) return true;
}
return false;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
index be3d0ad..c0bdb5e 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
@@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
import org.jboss.logging.Logger;
import org.keycloak.cluster.ClusterProvider;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.cache.CachedObject;
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
import org.keycloak.common.constants.ServiceAccountConstants;
@@ -49,6 +50,7 @@ import org.keycloak.models.cache.infinispan.events.UserFederationLinkRemovedEven
import org.keycloak.models.cache.infinispan.events.UserFederationLinkUpdatedEvent;
import org.keycloak.models.cache.infinispan.events.UserFullInvalidationEvent;
import org.keycloak.models.cache.infinispan.events.UserUpdatedEvent;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
import org.keycloak.storage.CacheableStorageProviderModel;
import org.keycloak.storage.StorageId;
@@ -715,15 +717,13 @@ public class UserCacheSession implements UserCache {
consentModel.setCreatedDate(cachedConsent.getCreatedDate());
consentModel.setLastUpdatedDate(cachedConsent.getLastUpdatedDate());
- for (String roleId : cachedConsent.getRoleIds()) {
- RoleModel role = session.realms().getRoleById(roleId, realm);
- if (role != null) {
- consentModel.addGrantedRole(role);
+ for (String clientScopeId : cachedConsent.getClientScopeIds()) {
+ ClientScopeModel clientScope = KeycloakModelUtils.findClientScopeById(realm, clientScopeId);
+ if (clientScope != null) {
+ consentModel.addGrantedClientScope(clientScope);
}
}
- for (ProtocolMapperModel protocolMapper : cachedConsent.getProtocolMappers()) {
- consentModel.addGrantedProtocolMapper(protocolMapper);
- }
+
return consentModel;
}
@@ -853,6 +853,12 @@ public class UserCacheSession implements UserCache {
}
@Override
+ public void preRemove(ClientScopeModel clientScope) {
+ // Not needed to invalidate realm probably. Just consents are affected ATM and they are checked if they exists
+ getDelegate().preRemove(clientScope);
+ }
+
+ @Override
public void preRemove(RealmModel realm, ComponentModel component) {
if (!component.getProviderType().equals(UserStorageProvider.class.getName()) && !component.getProviderType().equals(ClientStorageProvider.class.getName())) return;
addRealmInvalidation(realm.getId()); // easier to just invalidate whole realm
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticatedClientSessionAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticatedClientSessionAdapter.java
index cf8f577..6551bf8 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticatedClientSessionAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticatedClientSessionAdapter.java
@@ -239,44 +239,6 @@ public class AuthenticatedClientSessionAdapter implements AuthenticatedClientSes
}
@Override
- public Set<String> getRoles() {
- return entity.getRoles();
- }
-
- @Override
- public void setRoles(Set<String> roles) {
- ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
-
- @Override
- public void runUpdate(AuthenticatedClientSessionEntity entity) {
- entity.setRoles(roles); // TODO not thread-safe. But we will remove setRoles anyway...?
- }
-
- };
-
- update(task);
- }
-
- @Override
- public Set<String> getProtocolMappers() {
- return entity.getProtocolMappers();
- }
-
- @Override
- public void setProtocolMappers(Set<String> protocolMappers) {
- ClientSessionUpdateTask task = new ClientSessionUpdateTask() {
-
- @Override
- public void runUpdate(AuthenticatedClientSessionEntity entity) {
- entity.setProtocolMappers(protocolMappers); // TODO not thread-safe. But we will remove setProtocolMappers anyway...?
- }
-
- };
-
- update(task);
- }
-
- @Override
public String getNote(String name) {
return entity.getNotes().get(name);
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticationSessionAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticationSessionAdapter.java
index 3092503..831bd32 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticationSessionAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/AuthenticationSessionAdapter.java
@@ -99,26 +99,14 @@ public class AuthenticationSessionAdapter implements AuthenticationSessionModel
}
@Override
- public Set<String> getRoles() {
- if (entity.getRoles() == null || entity.getRoles().isEmpty()) return Collections.emptySet();
- return new HashSet<>(entity.getRoles());
+ public Set<String> getClientScopes() {
+ if (entity.getClientScopes() == null || entity.getClientScopes().isEmpty()) return Collections.emptySet();
+ return new HashSet<>(entity.getClientScopes());
}
@Override
- public void setRoles(Set<String> roles) {
- entity.setRoles(roles);
- update();
- }
-
- @Override
- public Set<String> getProtocolMappers() {
- if (entity.getProtocolMappers() == null || entity.getProtocolMappers().isEmpty()) return Collections.emptySet();
- return new HashSet<>(entity.getProtocolMappers());
- }
-
- @Override
- public void setProtocolMappers(Set<String> protocolMappers) {
- entity.setProtocolMappers(protocolMappers);
+ public void setClientScopes(Set<String> clientScopes) {
+ entity.setClientScopes(clientScopes);
update();
}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
index bc7d1e8..635f0e6 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticatedClientSessionEntity.java
@@ -21,7 +21,6 @@ import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.infinispan.commons.marshall.Externalizer;
@@ -49,8 +48,6 @@ public class AuthenticatedClientSessionEntity extends SessionEntity {
private volatile int timestamp;
private String action;
- private Set<String> roles;
- private Set<String> protocolMappers;
private Map<String, String> notes = new ConcurrentHashMap<>();
private String currentRefreshToken;
@@ -94,22 +91,6 @@ public class AuthenticatedClientSessionEntity extends SessionEntity {
this.action = action;
}
- public Set<String> getRoles() {
- return roles;
- }
-
- public void setRoles(Set<String> roles) {
- this.roles = roles;
- }
-
- public Set<String> getProtocolMappers() {
- return protocolMappers;
- }
-
- public void setProtocolMappers(Set<String> protocolMappers) {
- this.protocolMappers = protocolMappers;
- }
-
public Map<String, String> getNotes() {
return notes;
}
@@ -199,9 +180,6 @@ public class AuthenticatedClientSessionEntity extends SessionEntity {
Map<String, String> notes = session.getNotes();
KeycloakMarshallUtil.writeMap(notes, KeycloakMarshallUtil.STRING_EXT, KeycloakMarshallUtil.STRING_EXT, output);
- KeycloakMarshallUtil.writeCollection(session.getProtocolMappers(), KeycloakMarshallUtil.STRING_EXT, output);
- KeycloakMarshallUtil.writeCollection(session.getRoles(), KeycloakMarshallUtil.STRING_EXT, output);
-
MarshallUtil.marshallString(session.getCurrentRefreshToken(), output);
MarshallUtil.marshallInt(output, session.getCurrentRefreshTokenUseCount());
}
@@ -222,12 +200,6 @@ public class AuthenticatedClientSessionEntity extends SessionEntity {
new KeycloakMarshallUtil.ConcurrentHashMapBuilder<>());
sessionEntity.setNotes(notes);
- Set<String> protocolMappers = KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, new KeycloakMarshallUtil.HashSetBuilder<>());
- sessionEntity.setProtocolMappers(protocolMappers);
-
- Set<String> roles = KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, new KeycloakMarshallUtil.HashSetBuilder<>());
- sessionEntity.setRoles(roles);
-
sessionEntity.setCurrentRefreshToken(MarshallUtil.unmarshallString(input));
sessionEntity.setCurrentRefreshTokenUseCount(MarshallUtil.unmarshallInt(input));
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java
index be21ef6..cf51796 100644
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/AuthenticationSessionEntity.java
@@ -45,8 +45,7 @@ public class AuthenticationSessionEntity implements Serializable {
private String redirectUri;
private String action;
- private Set<String> roles;
- private Set<String> protocolMappers;
+ private Set<String> clientScopes;
private Map<String, AuthenticationSessionModel.ExecutionStatus> executionStatus = new ConcurrentHashMap<>();
private String protocol;
@@ -62,7 +61,7 @@ public class AuthenticationSessionEntity implements Serializable {
public AuthenticationSessionEntity(
String clientUUID,
String authUserId,
- String redirectUri, String action, Set<String> roles, Set<String> protocolMappers,
+ String redirectUri, String action, Set<String> clientScopes,
Map<String, AuthenticationSessionModel.ExecutionStatus> executionStatus, String protocol,
Map<String, String> clientNotes, Map<String, String> authNotes, Set<String> requiredActions, Map<String, String> userSessionNotes) {
this.clientUUID = clientUUID;
@@ -71,8 +70,7 @@ public class AuthenticationSessionEntity implements Serializable {
this.redirectUri = redirectUri;
this.action = action;
- this.roles = roles;
- this.protocolMappers = protocolMappers;
+ this.clientScopes = clientScopes;
this.executionStatus = executionStatus;
this.protocol = protocol;
@@ -115,20 +113,12 @@ public class AuthenticationSessionEntity implements Serializable {
this.action = action;
}
- public Set<String> getRoles() {
- return roles;
+ public Set<String> getClientScopes() {
+ return clientScopes;
}
- public void setRoles(Set<String> roles) {
- this.roles = roles;
- }
-
- public Set<String> getProtocolMappers() {
- return protocolMappers;
- }
-
- public void setProtocolMappers(Set<String> protocolMappers) {
- this.protocolMappers = protocolMappers;
+ public void setClientScopes(Set<String> clientScopes) {
+ this.clientScopes = clientScopes;
}
public Map<String, AuthenticationSessionModel.ExecutionStatus> getExecutionStatus() {
@@ -215,8 +205,7 @@ public class AuthenticationSessionEntity implements Serializable {
MarshallUtil.marshallString(value.redirectUri, output);
MarshallUtil.marshallString(value.action, output);
- KeycloakMarshallUtil.writeCollection(value.roles, KeycloakMarshallUtil.STRING_EXT, output);
- KeycloakMarshallUtil.writeCollection(value.protocolMappers, KeycloakMarshallUtil.STRING_EXT, output);
+ KeycloakMarshallUtil.writeCollection(value.clientScopes, KeycloakMarshallUtil.STRING_EXT, output);
KeycloakMarshallUtil.writeMap(value.executionStatus, KeycloakMarshallUtil.STRING_EXT, EXECUTION_STATUS_EXT, output);
MarshallUtil.marshallString(value.protocol, output);
@@ -245,8 +234,7 @@ public class AuthenticationSessionEntity implements Serializable {
MarshallUtil.unmarshallString(input), // redirectUri
MarshallUtil.unmarshallString(input), // action
- KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashSet<>()), // roles
- KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashSet<>()), // protocolMappers
+ KeycloakMarshallUtil.readCollection(input, KeycloakMarshallUtil.STRING_EXT, size -> new ConcurrentHashSet<>()), // clientScopes
KeycloakMarshallUtil.readMap(input, KeycloakMarshallUtil.STRING_EXT, EXECUTION_STATUS_EXT, size -> new ConcurrentHashMap<>(size)), // executionStatus
MarshallUtil.unmarshallString(input), // protocol
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 709c417..22ef5b7 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -925,9 +925,7 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
entity.setAuthMethod(clientSession.getProtocol());
entity.setNotes(clientSession.getNotes() == null ? new ConcurrentHashMap<>() : clientSession.getNotes());
- entity.setProtocolMappers(clientSession.getProtocolMappers());
entity.setRedirectUri(clientSession.getRedirectUri());
- entity.setRoles(clientSession.getRoles());
entity.setTimestamp(clientSession.getTimestamp());
SessionUpdateTask<AuthenticatedClientSessionEntity> createClientSessionTask = Tasks.addIfAbsentSync();
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
index 7bd7ec4..a7c6c7f 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
@@ -41,7 +41,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
index 7bd0854..6af8726 100644
--- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGRemoveSessionTest.java
@@ -178,8 +178,6 @@ public class ConcurrencyJDGRemoveSessionTest {
clientSession.setAuthMethod("saml");
clientSession.setAction("something");
clientSession.setTimestamp(1234);
- clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
- clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
SessionEntityWrapper<UserSessionEntity> wrappedSession = new SessionEntityWrapper<>(session);
diff --git a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
index 2a26a3c..7cf38ad 100644
--- a/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/cluster/infinispan/ConcurrencyJDGSessionsCacheTest.java
@@ -18,8 +18,6 @@
package org.keycloak.cluster.infinispan;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -97,8 +95,6 @@ public class ConcurrencyJDGSessionsCacheTest {
clientSession.setAuthMethod("saml");
clientSession.setAction("something");
clientSession.setTimestamp(1234);
- clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
- clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
SessionEntityWrapper<UserSessionEntity> wrappedSession = new SessionEntityWrapper<>(session);
diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
index af9139f..fff93e3 100644
--- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheConcurrentWritesTest.java
@@ -75,8 +75,6 @@ public class DistributedCacheConcurrentWritesTest {
clientSession.setAuthMethod("saml");
clientSession.setAction("something");
clientSession.setTimestamp(1234);
- clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
- clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
cache1.put("123", session);
diff --git a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
index 23f25b7..b578ab3 100644
--- a/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
+++ b/model/infinispan/src/test/java/org/keycloak/models/sessions/infinispan/initializer/DistributedCacheWriteSkewTest.java
@@ -17,8 +17,6 @@
package org.keycloak.models.sessions.infinispan.initializer;
-import java.util.Arrays;
-import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -75,8 +73,6 @@ public class DistributedCacheWriteSkewTest {
clientSession.setAuthMethod("saml");
clientSession.setAction("something");
clientSession.setTimestamp(1234);
- clientSession.setProtocolMappers(new HashSet<>(Arrays.asList("mapper1", "mapper2")));
- clientSession.setRoles(new HashSet<>(Arrays.asList("role1", "role2")));
session.getAuthenticatedClientSessions().put(CLIENT_1_UUID.toString(), clientSession.getId());
cache1.put("123", session);
diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java
index 4400671..d1ff82a 100755
--- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java
+++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/JpaUpdate1_2_0_Beta1.java
@@ -327,8 +327,7 @@ public class JpaUpdate1_2_0_Beta1 extends CustomKeycloakTask {
.addColumnValue("ID", mapperId)
.addColumnValue("PROTOCOL", protocolMapper.getProtocol())
.addColumnValue("NAME", protocolMapper.getName())
- .addColumnValue("CONSENT_REQUIRED", protocolMapper.isConsentRequired())
- .addColumnValue("CONSENT_TEXT", protocolMapper.getConsentText())
+ .addColumnValue("CONSENT_REQUIRED", false)
.addColumnValue("PROTOCOL_MAPPER_NAME", protocolMapper.getProtocolMapper())
.addColumnValue("CLIENT_ID", resultSet.getString("ID"));
statements.add(insert);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
index 3a7eabb..cab098e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientAdapter.java
@@ -18,20 +18,27 @@
package org.keycloak.models.jpa;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.jpa.entities.ClientEntity;
-import org.keycloak.models.jpa.entities.ClientTemplateEntity;
+import org.keycloak.models.jpa.entities.ClientScopeClientMappingEntity;
+import org.keycloak.models.jpa.entities.ClientScopeEntity;
+import org.keycloak.models.jpa.entities.ClientScopeRoleMappingEntity;
import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collection;
@@ -317,56 +324,48 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
}
@Override
- public ClientTemplateModel getClientTemplate() {
- ClientTemplateEntity templateEntity = entity.getClientTemplate();
- if (templateEntity == null) return null;
- return session.realms().getClientTemplateById(templateEntity.getId(), realm);
- }
-
- @Override
- public void setClientTemplate(ClientTemplateModel template) {
- if (template == null) {
- entity.setClientTemplate(null);
-
- } else {
- ClientTemplateEntity templateEntity = em.getReference(ClientTemplateEntity.class, template.getId());
- entity.setClientTemplate(templateEntity);
- }
-
- }
-
- @Override
- public boolean useTemplateScope() {
- return entity.isUseTemplateScope();
- }
-
- @Override
- public void setUseTemplateScope(boolean flag) {
- entity.setUseTemplateScope(flag);
+ public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
+ if (getClientScopes(defaultScope, false).containsKey(clientScope.getName())) return;
+ ClientScopeClientMappingEntity entity = new ClientScopeClientMappingEntity();
+ entity.setClientScope(ClientScopeAdapter.toClientScopeEntity(clientScope, em));
+ entity.setClient(getEntity());
+ entity.setDefaultScope(defaultScope);
+ em.persist(entity);
+ em.flush();
+ em.detach(entity);
}
@Override
- public boolean useTemplateMappers() {
- return entity.isUseTemplateMappers();
+ public void removeClientScope(ClientScopeModel clientScope) {
+ int numRemoved = em.createNamedQuery("deleteClientScopeClientMapping")
+ .setParameter("clientScope", ClientScopeAdapter.toClientScopeEntity(clientScope, em))
+ .setParameter("client", getEntity())
+ .executeUpdate();
+ em.flush();
}
@Override
- public void setUseTemplateMappers(boolean flag) {
- entity.setUseTemplateMappers(flag);
+ public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
+ TypedQuery<String> query = em.createNamedQuery("clientScopeClientMappingIdsByClient", String.class);
+ query.setParameter("client", getEntity());
+ query.setParameter("defaultScope", defaultScope);
+ List<String> ids = query.getResultList();
- }
+ // Defaults to openid-connect
+ String clientProtocol = getProtocol() == null ? OIDCLoginProtocol.LOGIN_PROTOCOL : getProtocol();
- @Override
- public boolean useTemplateConfig() {
- return entity.isUseTemplateConfig();
+ Map<String, ClientScopeModel> clientScopes = new HashMap<>();
+ for (String clientScopeId : ids) {
+ ClientScopeModel clientScope = realm.getClientScopeById(clientScopeId);
+ if (clientScope == null) continue;
+ if (!filterByProtocol || clientScope.getProtocol().equals(clientProtocol)) {
+ clientScopes.put(clientScope.getName(), clientScope);
+ }
+ }
+ return clientScopes;
}
- @Override
- public void setUseTemplateConfig(boolean flag) {
- entity.setUseTemplateConfig(flag);
-
- }
public static boolean contains(String str, String[] array) {
for (String s : array) {
@@ -384,8 +383,6 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol());
mapping.setProtocolMapper(entity.getProtocolMapper());
- mapping.setConsentRequired(entity.isConsentRequired());
- mapping.setConsentText(entity.getConsentText());
Map<String, String> config = new HashMap<String, String>();
if (entity.getConfig() != null) {
config.putAll(entity.getConfig());
@@ -409,8 +406,6 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
entity.setProtocolMapper(model.getProtocolMapper());
entity.setClient(this.entity);
entity.setConfig(model.getConfig());
- entity.setConsentRequired(model.isConsentRequired());
- entity.setConsentText(model.getConsentText());
em.persist(entity);
this.entity.getProtocolMappers().add(entity);
@@ -453,8 +448,6 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
public void updateProtocolMapper(ProtocolMapperModel mapping) {
ProtocolMapperEntity entity = getProtocolMapperEntity(mapping.getId());
entity.setProtocolMapper(mapping.getProtocolMapper());
- entity.setConsentRequired(mapping.isConsentRequired());
- entity.setConsentText(mapping.getConsentText());
if (entity.getConfig() == null) {
entity.setConfig(mapping.getConfig());
} else {
@@ -485,8 +478,6 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
mapping.setName(entity.getName());
mapping.setProtocol(entity.getProtocol());
mapping.setProtocolMapper(entity.getProtocolMapper());
- mapping.setConsentRequired(entity.isConsentRequired());
- mapping.setConsentText(entity.getConsentText());
Map<String, String> config = new HashMap<String, String>();
if (entity.getConfig() != null) config.putAll(entity.getConfig());
mapping.setConfig(config);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
index ea8a580..cc0672f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientEntity.java
@@ -91,19 +91,6 @@ public class ClientEntity {
private boolean fullScopeAllowed;
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "CLIENT_TEMPLATE_ID")
- protected ClientTemplateEntity clientTemplate;
-
- @Column(name="USE_TEMPLATE_CONFIG")
- private boolean useTemplateConfig;
-
- @Column(name="USE_TEMPLATE_SCOPE")
- private boolean useTemplateScope;
-
- @Column(name="USE_TEMPLATE_MAPPERS")
- private boolean useTemplateMappers;
-
- @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REALM_ID")
protected RealmEntity realm;
@@ -435,38 +422,6 @@ public class ClientEntity {
this.registeredNodes = registeredNodes;
}
- public ClientTemplateEntity getClientTemplate() {
- return clientTemplate;
- }
-
- public void setClientTemplate(ClientTemplateEntity clientTemplate) {
- this.clientTemplate = clientTemplate;
- }
-
- public boolean isUseTemplateConfig() {
- return useTemplateConfig;
- }
-
- public void setUseTemplateConfig(boolean useTemplateConfig) {
- this.useTemplateConfig = useTemplateConfig;
- }
-
- public boolean isUseTemplateScope() {
- return useTemplateScope;
- }
-
- public void setUseTemplateScope(boolean useTemplateScope) {
- this.useTemplateScope = useTemplateScope;
- }
-
- public boolean isUseTemplateMappers() {
- return useTemplateMappers;
- }
-
- public void setUseTemplateMappers(boolean useTemplateMappers) {
- this.useTemplateMappers = useTemplateMappers;
- }
-
public Set<RoleEntity> getScopeMapping() {
return scopeMapping;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeClientMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeClientMappingEntity.java
new file mode 100644
index 0000000..c75b319
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientScopeClientMappingEntity.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.jpa.entities;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * Binding between client and clientScope
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NamedQueries({
+ @NamedQuery(name="clientScopeClientMappingIdsByClient", query="select m.clientScope.id from ClientScopeClientMappingEntity m where m.client = :client and m.defaultScope = :defaultScope"),
+ @NamedQuery(name="deleteClientScopeClientMapping", query="delete from ClientScopeClientMappingEntity where client = :client and clientScope = :clientScope"),
+ @NamedQuery(name="deleteClientScopeClientMappingByClient", query="delete from ClientScopeClientMappingEntity where client = :client")
+})
+@Entity
+@Table(name="CLIENT_SCOPE_CLIENT")
+@IdClass(ClientScopeClientMappingEntity.Key.class)
+public class ClientScopeClientMappingEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "SCOPE_ID")
+ protected ClientScopeEntity clientScope;
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name="CLIENT_ID")
+ protected ClientEntity client;
+
+ @Column(name="DEFAULT_SCOPE")
+ protected boolean defaultScope;
+
+ public ClientScopeEntity getClientScope() {
+ return clientScope;
+ }
+
+ public void setClientScope(ClientScopeEntity clientScope) {
+ this.clientScope = clientScope;
+ }
+
+ public ClientEntity getClient() {
+ return client;
+ }
+
+ public void setClient(ClientEntity client) {
+ this.client = client;
+ }
+
+ public boolean isDefaultScope() {
+ return defaultScope;
+ }
+
+ public void setDefaultScope(boolean defaultScope) {
+ this.defaultScope = defaultScope;
+ }
+
+ public static class Key implements Serializable {
+
+ protected ClientScopeEntity clientScope;
+
+ protected ClientEntity client;
+
+ public Key() {
+ }
+
+ public Key(ClientScopeEntity clientScope, ClientEntity client) {
+ this.clientScope = clientScope;
+ this.client = client;
+ }
+
+ public ClientScopeEntity getClientScope() {
+ return clientScope;
+ }
+
+ public ClientEntity getClient() {
+ return client;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ClientScopeClientMappingEntity.Key key = (ClientScopeClientMappingEntity.Key) o;
+
+ if (clientScope != null ? !clientScope.getId().equals(key.clientScope != null ? key.clientScope.getId() : null) : key.clientScope != null) return false;
+ if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientScope != null ? clientScope.getId().hashCode() : 0;
+ result = 31 * result + (client != null ? client.getId().hashCode() : 0);
+ return result;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof ClientScopeClientMappingEntity)) return false;
+
+ ClientScopeClientMappingEntity key = (ClientScopeClientMappingEntity) o;
+
+ if (clientScope != null ? !clientScope.getId().equals(key.clientScope != null ? key.clientScope.getId() : null) : key.clientScope != null) return false;
+ if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientScope != null ? clientScope.getId().hashCode() : 0;
+ result = 31 * result + (client != null ? client.getId().hashCode() : 0);
+ return result;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/DefaultClientScopeRealmMappingEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/DefaultClientScopeRealmMappingEntity.java
new file mode 100644
index 0000000..334724a
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/DefaultClientScopeRealmMappingEntity.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.jpa.entities;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * Binding between realm and default clientScope
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NamedQueries({
+ @NamedQuery(name="defaultClientScopeRealmMappingIdsByRealm", query="select m.clientScope.id from DefaultClientScopeRealmMappingEntity m where m.realm = :realm and m.defaultScope = :defaultScope"),
+ @NamedQuery(name="deleteDefaultClientScopeRealmMapping", query="delete from DefaultClientScopeRealmMappingEntity where realm = :realm and clientScope = :clientScope"),
+ @NamedQuery(name="deleteDefaultClientScopeRealmMappingByRealm", query="delete from DefaultClientScopeRealmMappingEntity where realm = :realm")
+})
+@Entity
+@Table(name="DEFAULT_CLIENT_SCOPE")
+@IdClass(DefaultClientScopeRealmMappingEntity.Key.class)
+public class DefaultClientScopeRealmMappingEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "SCOPE_ID")
+ protected ClientScopeEntity clientScope;
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name="REALM_ID")
+ protected RealmEntity realm;
+
+ @Column(name="DEFAULT_SCOPE")
+ protected boolean defaultScope;
+
+ public ClientScopeEntity getClientScope() {
+ return clientScope;
+ }
+
+ public void setClientScope(ClientScopeEntity clientScope) {
+ this.clientScope = clientScope;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+
+ public boolean isDefaultScope() {
+ return defaultScope;
+ }
+
+ public void setDefaultScope(boolean defaultScope) {
+ this.defaultScope = defaultScope;
+ }
+
+ public static class Key implements Serializable {
+
+ protected ClientScopeEntity clientScope;
+
+ protected RealmEntity realm;
+
+ public Key() {
+ }
+
+ public Key(ClientScopeEntity clientScope, RealmEntity realm) {
+ this.clientScope = clientScope;
+ this.realm = realm;
+ }
+
+ public ClientScopeEntity getClientScope() {
+ return clientScope;
+ }
+
+ public RealmEntity getRealm() {
+ return realm;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ DefaultClientScopeRealmMappingEntity.Key key = (DefaultClientScopeRealmMappingEntity.Key) o;
+
+ if (clientScope != null ? !clientScope.getId().equals(key.clientScope != null ? key.clientScope.getId() : null) : key.clientScope != null) return false;
+ if (realm != null ? !realm.getId().equals(key.realm != null ? key.realm.getId() : null) : key.realm != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientScope != null ? clientScope.getId().hashCode() : 0;
+ result = 31 * result + (realm != null ? realm.getId().hashCode() : 0);
+ return result;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!(o instanceof DefaultClientScopeRealmMappingEntity)) return false;
+
+ DefaultClientScopeRealmMappingEntity key = (DefaultClientScopeRealmMappingEntity) o;
+
+ if (clientScope != null ? !clientScope.getId().equals(key.clientScope != null ? key.clientScope.getId() : null) : key.clientScope != null) return false;
+ if (realm != null ? !realm.getId().equals(key.realm != null ? key.realm.getId() : null) : key.realm != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientScope != null ? clientScope.getId().hashCode() : 0;
+ result = 31 * result + (realm != null ? realm.getId().hashCode() : 0);
+ return result;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java
index b6e3071..050f114 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ProtocolMapperEntity.java
@@ -51,10 +51,6 @@ public class ProtocolMapperEntity {
protected String protocol;
@Column(name = "PROTOCOL_MAPPER_NAME")
protected String protocolMapper;
- @Column(name="CONSENT_REQUIRED")
- protected boolean consentRequired;
- @Column(name="CONSENT_TEXT")
- protected String consentText;
@ElementCollection
@MapKeyColumn(name="NAME")
@@ -67,8 +63,8 @@ public class ProtocolMapperEntity {
private ClientEntity client;
@ManyToOne(fetch = FetchType.LAZY)
- @JoinColumn(name = "CLIENT_TEMPLATE_ID")
- private ClientTemplateEntity clientTemplate;
+ @JoinColumn(name = "CLIENT_SCOPE_ID")
+ private ClientScopeEntity clientScope;
public String getId() {
return id;
@@ -118,29 +114,14 @@ public class ProtocolMapperEntity {
this.client = client;
}
- public ClientTemplateEntity getClientTemplate() {
- return clientTemplate;
+ public ClientScopeEntity getClientScope() {
+ return clientScope;
}
- public void setClientTemplate(ClientTemplateEntity clientTemplate) {
- this.clientTemplate = clientTemplate;
+ public void setClientScope(ClientScopeEntity clientScope) {
+ this.clientScope = clientScope;
}
- public boolean isConsentRequired() {
- return consentRequired;
- }
-
- public void setConsentRequired(boolean consentRequired) {
- this.consentRequired = consentRequired;
- }
-
- public String getConsentText() {
- return consentText;
- }
-
- public void setConsentText(String consentText) {
- this.consentText = consentText;
- }
@Override
public boolean equals(Object o) {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index 4f6244f..7fa765d 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -145,7 +145,7 @@ public class RealmEntity {
Collection<UserFederationMapperEntity> userFederationMappers = new ArrayList<UserFederationMapperEntity>();
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
- Collection<ClientTemplateEntity> clientTemplates = new ArrayList<>();
+ Collection<ClientScopeEntity> clientScopes = new ArrayList<>();
@ElementCollection
@MapKeyColumn(name="NAME")
@@ -757,12 +757,12 @@ public class RealmEntity {
return this;
}
- public Collection<ClientTemplateEntity> getClientTemplates() {
- return clientTemplates;
+ public Collection<ClientScopeEntity> getClientScopes() {
+ return clientScopes;
}
- public void setClientTemplates(Collection<ClientTemplateEntity> clientTemplates) {
- this.clientTemplates = clientTemplates;
+ public void setClientScopes(Collection<ClientScopeEntity> clientScopes) {
+ this.clientScopes = clientScopes;
}
public void setAllowUserManagedAccess(boolean allowUserManagedAccess) {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
index a8d30aa..526d559 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
@@ -69,8 +69,6 @@ public class RoleEntity {
@Nationalized
@Column(name = "DESCRIPTION")
private String description;
- @Column(name = "SCOPE_PARAM_REQUIRED")
- private boolean scopeParamRequired;
// hax! couldn't get constraint to work properly
@Column(name = "REALM_ID")
@@ -129,14 +127,6 @@ public class RoleEntity {
this.description = description;
}
- public boolean isScopeParamRequired() {
- return scopeParamRequired;
- }
-
- public void setScopeParamRequired(boolean scopeParamRequired) {
- this.scopeParamRequired = scopeParamRequired;
- }
-
public Set<RoleEntity> getCompositeRoles() {
return compositeRoles;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
index 9772810..88a57b2 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
@@ -73,10 +73,7 @@ public class UserConsentEntity {
protected String externalClientId;
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
- Collection<UserConsentRoleEntity> grantedRoles = new ArrayList<UserConsentRoleEntity>();
-
- @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
- Collection<UserConsentProtocolMapperEntity> grantedProtocolMappers = new ArrayList<UserConsentProtocolMapperEntity>();
+ Collection<UserConsentClientScopeEntity> grantedClientScopes = new ArrayList<>();
@Column(name = "CREATED_DATE")
private Long createdDate;
@@ -100,20 +97,12 @@ public class UserConsentEntity {
this.user = user;
}
- public Collection<UserConsentRoleEntity> getGrantedRoles() {
- return grantedRoles;
- }
-
- public void setGrantedRoles(Collection<UserConsentRoleEntity> grantedRoles) {
- this.grantedRoles = grantedRoles;
- }
-
- public Collection<UserConsentProtocolMapperEntity> getGrantedProtocolMappers() {
- return grantedProtocolMappers;
+ public Collection<UserConsentClientScopeEntity> getGrantedClientScopes() {
+ return grantedClientScopes;
}
- public void setGrantedProtocolMappers(Collection<UserConsentProtocolMapperEntity> grantedProtocolMappers) {
- this.grantedProtocolMappers = grantedProtocolMappers;
+ public void setGrantedClientScopes(Collection<UserConsentClientScopeEntity> grantedClientScopes) {
+ this.grantedClientScopes = grantedClientScopes;
}
public Long getCreatedDate() {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 6f23dc5..da2df69 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -23,7 +23,7 @@ import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.migration.MigrationModel;
import org.keycloak.models.ClientInitialAccessModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
@@ -33,7 +33,7 @@ import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.ClientInitialAccessEntity;
-import org.keycloak.models.jpa.entities.ClientTemplateEntity;
+import org.keycloak.models.jpa.entities.ClientScopeEntity;
import org.keycloak.models.jpa.entities.GroupEntity;
import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
@@ -142,8 +142,11 @@ public class JpaRealmProvider implements RealmProvider {
removeClient(client, adapter);
}
- for (ClientTemplateEntity a : new LinkedList<>(realm.getClientTemplates())) {
- adapter.removeClientTemplate(a.getId());
+ num = em.createNamedQuery("deleteDefaultClientScopeRealmMappingByRealm")
+ .setParameter("realm", realm).executeUpdate();
+
+ for (ClientScopeEntity a : new LinkedList<>(realm.getClientScopes())) {
+ adapter.removeClientScope(a.getId());
}
for (RoleModel role : adapter.getRoles()) {
@@ -285,7 +288,7 @@ public class JpaRealmProvider implements RealmProvider {
String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate();
realm.getClients().forEach(c -> c.deleteScopeMapping(role));
- em.createNamedQuery("deleteTemplateScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
+ em.createNamedQuery("deleteClientScopeRoleMappingByRole").setParameter("role", roleEntity).executeUpdate();
int val = em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate();
em.flush();
@@ -567,6 +570,9 @@ public class JpaRealmProvider implements RealmProvider {
}
});
+ int countRemoved = em.createNamedQuery("deleteClientScopeClientMappingByClient")
+ .setParameter("client", clientEntity)
+ .executeUpdate();
em.remove(clientEntity); // i have no idea why, but this needs to come before deleteScopeMapping
try {
@@ -580,12 +586,12 @@ public class JpaRealmProvider implements RealmProvider {
}
@Override
- public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
- ClientTemplateEntity app = em.find(ClientTemplateEntity.class, id);
+ public ClientScopeModel getClientScopeById(String id, RealmModel realm) {
+ ClientScopeEntity app = em.find(ClientScopeEntity.class, id);
// Check if application belongs to this realm
if (app == null || !realm.getId().equals(app.getRealm().getId())) return null;
- ClientTemplateAdapter adapter = new ClientTemplateAdapter(realm, em, session, app);
+ ClientScopeAdapter adapter = new ClientScopeAdapter(realm, em, session, app);
return adapter;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index f20e87a..2ee9ea5 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -23,7 +23,7 @@ import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@@ -39,9 +39,8 @@ import org.keycloak.models.UserProvider;
import org.keycloak.models.jpa.entities.CredentialAttributeEntity;
import org.keycloak.models.jpa.entities.CredentialEntity;
import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
+import org.keycloak.models.jpa.entities.UserConsentClientScopeEntity;
import org.keycloak.models.jpa.entities.UserConsentEntity;
-import org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity;
-import org.keycloak.models.jpa.entities.UserConsentRoleEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import org.keycloak.models.utils.DefaultRoles;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -132,8 +131,7 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
em.createNamedQuery("deleteUserRoleMappingsByUser").setParameter("user", user).executeUpdate();
em.createNamedQuery("deleteUserGroupMembershipsByUser").setParameter("user", user).executeUpdate();
em.createNamedQuery("deleteFederatedIdentityByUser").setParameter("user", user).executeUpdate();
- em.createNamedQuery("deleteUserConsentRolesByUser").setParameter("user", user).executeUpdate();
- em.createNamedQuery("deleteUserConsentProtMappersByUser").setParameter("user", user).executeUpdate();
+ em.createNamedQuery("deleteUserConsentClientScopesByUser").setParameter("user", user).executeUpdate();
em.createNamedQuery("deleteUserConsentsByUser").setParameter("user", user).executeUpdate();
em.flush();
// not sure why i have to do a clear() here. I was getting some messed up errors that Hibernate couldn't
@@ -297,36 +295,12 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
model.setCreatedDate(entity.getCreatedDate());
model.setLastUpdatedDate(entity.getLastUpdatedDate());
- Collection<UserConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles();
- if (grantedRoleEntities != null) {
- for (UserConsentRoleEntity grantedRole : grantedRoleEntities) {
- RoleModel grantedRoleModel = realm.getRoleById(grantedRole.getRoleId());
- if (grantedRoleModel != null) {
- model.addGrantedRole(grantedRoleModel);
- }
- }
- }
-
- Collection<UserConsentProtocolMapperEntity> grantedProtocolMapperEntities = entity.getGrantedProtocolMappers();
- if (grantedProtocolMapperEntities != null) {
-
- ClientTemplateModel clientTemplate = null;
- if (client.useTemplateMappers()) {
- clientTemplate = client.getClientTemplate();
- }
-
- for (UserConsentProtocolMapperEntity grantedProtMapper : grantedProtocolMapperEntities) {
- ProtocolMapperModel protocolMapper = client.getProtocolMapperById(grantedProtMapper.getProtocolMapperId());
-
- // Fallback to client template
- if (protocolMapper == null) {
- if (clientTemplate != null) {
- protocolMapper = clientTemplate.getProtocolMapperById(grantedProtMapper.getProtocolMapperId());
- }
- }
-
- if (protocolMapper != null) {
- model.addGrantedProtocolMapper(protocolMapper);
+ Collection<UserConsentClientScopeEntity> grantedClientScopeEntities = entity.getGrantedClientScopes();
+ if (grantedClientScopeEntities != null) {
+ for (UserConsentClientScopeEntity grantedClientScope : grantedClientScopeEntities) {
+ ClientScopeModel grantedClientScopeModel = KeycloakModelUtils.findClientScopeById(realm, grantedClientScope.getScopeId());
+ if (grantedClientScopeModel != null) {
+ model.addGrantedClientScope(grantedClientScopeModel);
}
}
}
@@ -336,48 +310,26 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
// Update roles and protocolMappers to given consentEntity from the consentModel
private void updateGrantedConsentEntity(UserConsentEntity consentEntity, UserConsentModel consentModel) {
- Collection<UserConsentProtocolMapperEntity> grantedProtocolMapperEntities = consentEntity.getGrantedProtocolMappers();
- Collection<UserConsentProtocolMapperEntity> mappersToRemove = new HashSet<UserConsentProtocolMapperEntity>(grantedProtocolMapperEntities);
+ Collection<UserConsentClientScopeEntity> grantedClientScopeEntities = consentEntity.getGrantedClientScopes();
+ Collection<UserConsentClientScopeEntity> scopesToRemove = new HashSet<>(grantedClientScopeEntities);
- for (ProtocolMapperModel protocolMapper : consentModel.getGrantedProtocolMappers()) {
- UserConsentProtocolMapperEntity grantedProtocolMapperEntity = new UserConsentProtocolMapperEntity();
- grantedProtocolMapperEntity.setUserConsent(consentEntity);
- grantedProtocolMapperEntity.setProtocolMapperId(protocolMapper.getId());
+ for (ClientScopeModel clientScope : consentModel.getGrantedClientScopes()) {
+ UserConsentClientScopeEntity grantedClientScopeEntity = new UserConsentClientScopeEntity();
+ grantedClientScopeEntity.setUserConsent(consentEntity);
+ grantedClientScopeEntity.setScopeId(clientScope.getId());
// Check if it's already there
- if (!grantedProtocolMapperEntities.contains(grantedProtocolMapperEntity)) {
- em.persist(grantedProtocolMapperEntity);
+ if (!grantedClientScopeEntities.contains(grantedClientScopeEntity)) {
+ em.persist(grantedClientScopeEntity);
em.flush();
- grantedProtocolMapperEntities.add(grantedProtocolMapperEntity);
+ grantedClientScopeEntities.add(grantedClientScopeEntity);
} else {
- mappersToRemove.remove(grantedProtocolMapperEntity);
+ scopesToRemove.remove(grantedClientScopeEntity);
}
}
- // Those mappers were no longer on consentModel and will be removed
- for (UserConsentProtocolMapperEntity toRemove : mappersToRemove) {
- grantedProtocolMapperEntities.remove(toRemove);
- em.remove(toRemove);
- }
-
- Collection<UserConsentRoleEntity> grantedRoleEntities = consentEntity.getGrantedRoles();
- Set<UserConsentRoleEntity> rolesToRemove = new HashSet<UserConsentRoleEntity>(grantedRoleEntities);
- for (RoleModel role : consentModel.getGrantedRoles()) {
- UserConsentRoleEntity consentRoleEntity = new UserConsentRoleEntity();
- consentRoleEntity.setUserConsent(consentEntity);
- consentRoleEntity.setRoleId(role.getId());
-
- // Check if it's already there
- if (!grantedRoleEntities.contains(consentRoleEntity)) {
- em.persist(consentRoleEntity);
- em.flush();
- grantedRoleEntities.add(consentRoleEntity);
- } else {
- rolesToRemove.remove(consentRoleEntity);
- }
- }
- // Those roles were no longer on consentModel and will be removed
- for (UserConsentRoleEntity toRemove : rolesToRemove) {
- grantedRoleEntities.remove(toRemove);
+ // Those client scopes were no longer on consentModel and will be removed
+ for (UserConsentClientScopeEntity toRemove : scopesToRemove) {
+ grantedClientScopeEntities.remove(toRemove);
em.remove(toRemove);
}
@@ -409,9 +361,7 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
@Override
public void preRemove(RealmModel realm) {
- int num = em.createNamedQuery("deleteUserConsentRolesByRealm")
- .setParameter("realmId", realm.getId()).executeUpdate();
- num = em.createNamedQuery("deleteUserConsentProtMappersByRealm")
+ int num = em.createNamedQuery("deleteUserConsentClientScopesByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserConsentsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
@@ -463,11 +413,7 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
.setParameter("realmId", realm.getId())
.setParameter("link", storageProviderId)
.executeUpdate();
- num = em.createNamedQuery("deleteUserConsentProtMappersByRealmAndLink")
- .setParameter("realmId", realm.getId())
- .setParameter("link", storageProviderId)
- .executeUpdate();
- num = em.createNamedQuery("deleteUserConsentRolesByRealmAndLink")
+ num = em.createNamedQuery("deleteUserConsentClientScopesByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", storageProviderId)
.executeUpdate();
@@ -491,7 +437,6 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
@Override
public void preRemove(RealmModel realm, RoleModel role) {
- em.createNamedQuery("deleteUserConsentRolesByRole").setParameter("roleId", role.getId()).executeUpdate();
em.createNamedQuery("deleteUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate();
}
@@ -499,21 +444,14 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
public void preRemove(RealmModel realm, ClientModel client) {
StorageId clientStorageId = new StorageId(client.getId());
if (clientStorageId.isLocal()) {
- em.createNamedQuery("deleteUserConsentProtMappersByClient")
- .setParameter("clientId", client.getId())
- .executeUpdate();
- em.createNamedQuery("deleteUserConsentRolesByClient")
+ int num = em.createNamedQuery("deleteUserConsentClientScopesByClient")
.setParameter("clientId", client.getId())
.executeUpdate();
- em.createNamedQuery("deleteUserConsentsByClient")
+ num = em.createNamedQuery("deleteUserConsentsByClient")
.setParameter("clientId", client.getId())
.executeUpdate();
} else {
- em.createNamedQuery("deleteUserConsentProtMappersByExternalClient")
- .setParameter("clientStorageProvider", clientStorageId.getProviderId())
- .setParameter("externalClientId",clientStorageId.getExternalId())
- .executeUpdate();
- em.createNamedQuery("deleteUserConsentRolesByExternalClient")
+ em.createNamedQuery("deleteUserConsentClientScopesByExternalClient")
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
.setParameter("externalClientId", clientStorageId.getExternalId())
.executeUpdate();
@@ -527,8 +465,13 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
@Override
public void preRemove(ProtocolMapperModel protocolMapper) {
- em.createNamedQuery("deleteUserConsentProtMappersByProtocolMapper")
- .setParameter("protocolMapperId", protocolMapper.getId())
+ // No-op
+ }
+
+ @Override
+ public void preRemove(ClientScopeModel clientScope) {
+ em.createNamedQuery("deleteUserConsentClientScopesByClientScope")
+ .setParameter("scopeId", clientScope.getId())
.executeUpdate();
}
@@ -863,10 +806,7 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
}
protected void removeConsentByClientStorageProvider(RealmModel realm, String providerId) {
- em.createNamedQuery("deleteUserConsentProtMappersByClientStorageProvider")
- .setParameter("clientStorageProvider", providerId)
- .executeUpdate();
- em.createNamedQuery("deleteUserConsentRolesByClientStorageProvider")
+ em.createNamedQuery("deleteUserConsentClientScopesByClientStorageProvider")
.setParameter("clientStorageProvider", providerId)
.executeUpdate();
em.createNamedQuery("deleteUserConsentsByClientStorageProvider")
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index e188139..2795c39 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -28,6 +28,8 @@ import org.keycloak.models.utils.ComponentUtil;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -1769,60 +1771,64 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
}
@Override
- public List<ClientTemplateModel> getClientTemplates() {
- Collection<ClientTemplateEntity> entities = realm.getClientTemplates();
+ public List<ClientScopeModel> getClientScopes() {
+ Collection<ClientScopeEntity> entities = realm.getClientScopes();
if (entities == null || entities.isEmpty()) return Collections.EMPTY_LIST;
- List<ClientTemplateModel> list = new LinkedList<>();
- for (ClientTemplateEntity entity : entities) {
- list.add(session.realms().getClientTemplateById(entity.getId(), this));
+ List<ClientScopeModel> list = new LinkedList<>();
+ for (ClientScopeEntity entity : entities) {
+ list.add(session.realms().getClientScopeById(entity.getId(), this));
}
return Collections.unmodifiableList(list);
}
@Override
- public ClientTemplateModel addClientTemplate(String name) {
- return this.addClientTemplate(KeycloakModelUtils.generateId(), name);
+ public ClientScopeModel addClientScope(String name) {
+ return this.addClientScope(KeycloakModelUtils.generateId(), name);
}
@Override
- public ClientTemplateModel addClientTemplate(String id, String name) {
- ClientTemplateEntity entity = new ClientTemplateEntity();
+ public ClientScopeModel addClientScope(String id, String name) {
+ ClientScopeEntity entity = new ClientScopeEntity();
entity.setId(id);
+ name = KeycloakModelUtils.convertClientScopeName(name);
entity.setName(name);
entity.setRealm(realm);
- realm.getClientTemplates().add(entity);
+ realm.getClientScopes().add(entity);
em.persist(entity);
em.flush();
- final ClientTemplateModel resource = new ClientTemplateAdapter(this, em, session, entity);
+ final ClientScopeModel resource = new ClientScopeAdapter(this, em, session, entity);
em.flush();
return resource;
}
@Override
- public boolean removeClientTemplate(String id) {
+ public boolean removeClientScope(String id) {
if (id == null) return false;
- ClientTemplateModel client = getClientTemplateById(id);
- if (client == null) return false;
- if (KeycloakModelUtils.isClientTemplateUsed(this, client)) {
- throw new ModelException("Cannot remove client template, it is currently in use");
+ ClientScopeModel clientScope = getClientScopeById(id);
+ if (clientScope == null) return false;
+ if (KeycloakModelUtils.isClientScopeUsed(this, clientScope)) {
+ throw new ModelException("Cannot remove client scope, it is currently in use");
}
- ClientTemplateEntity clientEntity = null;
- Iterator<ClientTemplateEntity> it = realm.getClientTemplates().iterator();
+ ClientScopeEntity clientScopeEntity = null;
+ Iterator<ClientScopeEntity> it = realm.getClientScopes().iterator();
while (it.hasNext()) {
- ClientTemplateEntity ae = it.next();
+ ClientScopeEntity ae = it.next();
if (ae.getId().equals(id)) {
- clientEntity = ae;
+ clientScopeEntity = ae;
it.remove();
break;
}
}
- if (client == null) {
+ if (clientScope == null) {
return false;
}
- em.createNamedQuery("deleteTemplateScopeMappingByClient").setParameter("template", clientEntity).executeUpdate();
+
+ session.users().preRemove(clientScope);
+
+ em.createNamedQuery("deleteClientScopeRoleMappingByClientScope").setParameter("clientScope", clientScopeEntity).executeUpdate();
em.flush();
- em.remove(clientEntity);
+ em.remove(clientScopeEntity);
em.flush();
@@ -1830,8 +1836,44 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
}
@Override
- public ClientTemplateModel getClientTemplateById(String id) {
- return session.realms().getClientTemplateById(id, this);
+ public ClientScopeModel getClientScopeById(String id) {
+ return session.realms().getClientScopeById(id, this);
+ }
+
+ @Override
+ public void addDefaultClientScope(ClientScopeModel clientScope, boolean defaultScope) {
+ DefaultClientScopeRealmMappingEntity entity = new DefaultClientScopeRealmMappingEntity();
+ entity.setClientScope(ClientScopeAdapter.toClientScopeEntity(clientScope, em));
+ entity.setRealm(getEntity());
+ entity.setDefaultScope(defaultScope);
+ em.persist(entity);
+ em.flush();
+ em.detach(entity);
+ }
+
+ @Override
+ public void removeDefaultClientScope(ClientScopeModel clientScope) {
+ int numRemoved = em.createNamedQuery("deleteDefaultClientScopeRealmMapping")
+ .setParameter("clientScope", ClientScopeAdapter.toClientScopeEntity(clientScope, em))
+ .setParameter("realm", getEntity())
+ .executeUpdate();
+ em.flush();
+ }
+
+ @Override
+ public List<ClientScopeModel> getDefaultClientScopes(boolean defaultScope) {
+ TypedQuery<String> query = em.createNamedQuery("defaultClientScopeRealmMappingIdsByRealm", String.class);
+ query.setParameter("realm", getEntity());
+ query.setParameter("defaultScope", defaultScope);
+ List<String> ids = query.getResultList();
+
+ List<ClientScopeModel> clientScopes = new LinkedList<>();
+ for (String clientScopeId : ids) {
+ ClientScopeModel clientScope = getClientScopeById(clientScopeId);
+ if (clientScope == null) continue;
+ clientScopes.add(clientScope);
+ }
+ return clientScopes;
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
index cab7f2f..27b81ba 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -69,16 +69,6 @@ public class RoleAdapter implements RoleModel, JpaModel<RoleEntity> {
}
@Override
- public boolean isScopeParamRequired() {
- return role.isScopeParamRequired();
- }
-
- @Override
- public void setScopeParamRequired(boolean scopeParamRequired) {
- role.setScopeParamRequired(scopeParamRequired);
- }
-
- @Override
public String getId() {
return role.getId();
}
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserConsentEntity.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserConsentEntity.java
index c2eac0b..87d427d 100755
--- a/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserConsentEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/entity/FederatedUserConsentEntity.java
@@ -83,10 +83,7 @@ public class FederatedUserConsentEntity {
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
- Collection<FederatedUserConsentRoleEntity> grantedRoles = new ArrayList<FederatedUserConsentRoleEntity>();
-
- @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
- Collection<FederatedUserConsentProtocolMapperEntity> grantedProtocolMappers = new ArrayList<FederatedUserConsentProtocolMapperEntity>();
+ Collection<FederatedUserConsentClientScopeEntity> grantedClientScopes = new ArrayList<>();
public String getId() {
return id;
@@ -144,20 +141,12 @@ public class FederatedUserConsentEntity {
this.externalClientId = externalClientId;
}
- public Collection<FederatedUserConsentRoleEntity> getGrantedRoles() {
- return grantedRoles;
- }
-
- public void setGrantedRoles(Collection<FederatedUserConsentRoleEntity> grantedRoles) {
- this.grantedRoles = grantedRoles;
- }
-
- public Collection<FederatedUserConsentProtocolMapperEntity> getGrantedProtocolMappers() {
- return grantedProtocolMappers;
+ public Collection<FederatedUserConsentClientScopeEntity> getGrantedClientScopes() {
+ return grantedClientScopes;
}
- public void setGrantedProtocolMappers(Collection<FederatedUserConsentProtocolMapperEntity> grantedProtocolMappers) {
- this.grantedProtocolMappers = grantedProtocolMappers;
+ public void setGrantedClientScopes(Collection<FederatedUserConsentClientScopeEntity> grantedClientScopes) {
+ this.grantedClientScopes = grantedClientScopes;
}
public Long getCreatedDate() {
diff --git a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
index 7474155..f5727d2 100644
--- a/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/storage/jpa/JpaUserFederatedStorageProvider.java
@@ -22,6 +22,7 @@ import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@@ -32,7 +33,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
-import org.keycloak.models.jpa.entities.UserConsentEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
@@ -41,9 +41,8 @@ import org.keycloak.storage.federated.UserFederatedStorageProvider;
import org.keycloak.storage.jpa.entity.BrokerLinkEntity;
import org.keycloak.storage.jpa.entity.FederatedUser;
import org.keycloak.storage.jpa.entity.FederatedUserAttributeEntity;
+import org.keycloak.storage.jpa.entity.FederatedUserConsentClientScopeEntity;
import org.keycloak.storage.jpa.entity.FederatedUserConsentEntity;
-import org.keycloak.storage.jpa.entity.FederatedUserConsentProtocolMapperEntity;
-import org.keycloak.storage.jpa.entity.FederatedUserConsentRoleEntity;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialAttributeEntity;
import org.keycloak.storage.jpa.entity.FederatedUserCredentialEntity;
import org.keycloak.storage.jpa.entity.FederatedUserGroupMembershipEntity;
@@ -361,71 +360,41 @@ public class JpaUserFederatedStorageProvider implements
model.setCreatedDate(entity.getCreatedDate());
model.setLastUpdatedDate(entity.getLastUpdatedDate());
- Collection<FederatedUserConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles();
- if (grantedRoleEntities != null) {
- for (FederatedUserConsentRoleEntity grantedRole : grantedRoleEntities) {
- RoleModel grantedRoleModel = realm.getRoleById(grantedRole.getRoleId());
- if (grantedRoleModel != null) {
- model.addGrantedRole(grantedRoleModel);
+ Collection<FederatedUserConsentClientScopeEntity> grantedClientScopeEntities = entity.getGrantedClientScopes();
+ if (grantedClientScopeEntities != null) {
+ for (FederatedUserConsentClientScopeEntity grantedClientScope : grantedClientScopeEntities) {
+ ClientScopeModel grantedClientScopeModel = realm.getClientScopeById(grantedClientScope.getScopeId());
+ if (grantedClientScopeModel != null) {
+ model.addGrantedClientScope(grantedClientScopeModel);
}
}
}
- Collection<FederatedUserConsentProtocolMapperEntity> grantedProtocolMapperEntities = entity.getGrantedProtocolMappers();
- if (grantedProtocolMapperEntities != null) {
- for (FederatedUserConsentProtocolMapperEntity grantedProtMapper : grantedProtocolMapperEntities) {
- ProtocolMapperModel protocolMapper = client.getProtocolMapperById(grantedProtMapper.getProtocolMapperId());
- model.addGrantedProtocolMapper(protocolMapper);
- }
- }
-
return model;
}
// Update roles and protocolMappers to given consentEntity from the consentModel
private void updateGrantedConsentEntity(FederatedUserConsentEntity consentEntity, UserConsentModel consentModel) {
- Collection<FederatedUserConsentProtocolMapperEntity> grantedProtocolMapperEntities = consentEntity.getGrantedProtocolMappers();
- Collection<FederatedUserConsentProtocolMapperEntity> mappersToRemove = new HashSet<>(grantedProtocolMapperEntities);
+ Collection<FederatedUserConsentClientScopeEntity> grantedClientScopeEntities = consentEntity.getGrantedClientScopes();
+ Collection<FederatedUserConsentClientScopeEntity> scopesToRemove = new HashSet<>(grantedClientScopeEntities);
- for (ProtocolMapperModel protocolMapper : consentModel.getGrantedProtocolMappers()) {
- FederatedUserConsentProtocolMapperEntity grantedProtocolMapperEntity = new FederatedUserConsentProtocolMapperEntity();
- grantedProtocolMapperEntity.setUserConsent(consentEntity);
- grantedProtocolMapperEntity.setProtocolMapperId(protocolMapper.getId());
+ for (ClientScopeModel clientScope : consentModel.getGrantedClientScopes()) {
+ FederatedUserConsentClientScopeEntity grantedClientScopeEntity = new FederatedUserConsentClientScopeEntity();
+ grantedClientScopeEntity.setUserConsent(consentEntity);
+ grantedClientScopeEntity.setScopeId(clientScope.getId());
// Check if it's already there
- if (!grantedProtocolMapperEntities.contains(grantedProtocolMapperEntity)) {
- em.persist(grantedProtocolMapperEntity);
+ if (!grantedClientScopeEntities.contains(grantedClientScopeEntity)) {
+ em.persist(grantedClientScopeEntity);
em.flush();
- grantedProtocolMapperEntities.add(grantedProtocolMapperEntity);
+ grantedClientScopeEntities.add(grantedClientScopeEntity);
} else {
- mappersToRemove.remove(grantedProtocolMapperEntity);
+ scopesToRemove.remove(grantedClientScopeEntity);
}
}
// Those mappers were no longer on consentModel and will be removed
- for (FederatedUserConsentProtocolMapperEntity toRemove : mappersToRemove) {
- grantedProtocolMapperEntities.remove(toRemove);
- em.remove(toRemove);
- }
-
- Collection<FederatedUserConsentRoleEntity> grantedRoleEntities = consentEntity.getGrantedRoles();
- Set<FederatedUserConsentRoleEntity> rolesToRemove = new HashSet<>(grantedRoleEntities);
- for (RoleModel role : consentModel.getGrantedRoles()) {
- FederatedUserConsentRoleEntity consentRoleEntity = new FederatedUserConsentRoleEntity();
- consentRoleEntity.setUserConsent(consentEntity);
- consentRoleEntity.setRoleId(role.getId());
-
- // Check if it's already there
- if (!grantedRoleEntities.contains(consentRoleEntity)) {
- em.persist(consentRoleEntity);
- em.flush();
- grantedRoleEntities.add(consentRoleEntity);
- } else {
- rolesToRemove.remove(consentRoleEntity);
- }
- }
- // Those roles were no longer on consentModel and will be removed
- for (FederatedUserConsentRoleEntity toRemove : rolesToRemove) {
- grantedRoleEntities.remove(toRemove);
+ for (FederatedUserConsentClientScopeEntity toRemove : scopesToRemove) {
+ grantedClientScopeEntities.remove(toRemove);
em.remove(toRemove);
}
@@ -804,9 +773,7 @@ public class JpaUserFederatedStorageProvider implements
@Override
public void preRemove(RealmModel realm) {
- int num = em.createNamedQuery("deleteFederatedUserConsentRolesByRealm")
- .setParameter("realmId", realm.getId()).executeUpdate();
- num = em.createNamedQuery("deleteFederatedUserConsentProtMappersByRealm")
+ int num = em.createNamedQuery("deleteFederatedUserConsentClientScopesByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteFederatedUserConsentsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
@@ -831,7 +798,6 @@ public class JpaUserFederatedStorageProvider implements
@Override
public void preRemove(RealmModel realm, RoleModel role) {
em.createNamedQuery("deleteFederatedUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate();
- em.createNamedQuery("deleteFederatedUserRoleMappingsByRole").setParameter("roleId", role.getId()).executeUpdate();
}
@Override
@@ -843,15 +809,10 @@ public class JpaUserFederatedStorageProvider implements
public void preRemove(RealmModel realm, ClientModel client) {
StorageId clientStorageId = new StorageId(client.getId());
if (clientStorageId.isLocal()) {
- em.createNamedQuery("deleteFederatedUserConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate();
- em.createNamedQuery("deleteFederatedUserConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate();
+ em.createNamedQuery("deleteFederatedUserConsentClientScopesByClient").setParameter("clientId", client.getId()).executeUpdate();
em.createNamedQuery("deleteFederatedUserConsentsByClient").setParameter("clientId", client.getId()).executeUpdate();
} else {
- em.createNamedQuery("deleteFederatedUserConsentProtMappersByExternalClient")
- .setParameter("clientStorageProvider", clientStorageId.getProviderId())
- .setParameter("externalClientId",clientStorageId.getExternalId())
- .executeUpdate();
- em.createNamedQuery("deleteFederatedUserConsentRolesByExternalClient")
+ em.createNamedQuery("deleteFederatedUserConsentClientScopesByExternalClient")
.setParameter("clientStorageProvider", clientStorageId.getProviderId())
.setParameter("externalClientId",clientStorageId.getExternalId())
.executeUpdate();
@@ -865,8 +826,13 @@ public class JpaUserFederatedStorageProvider implements
@Override
public void preRemove(ProtocolMapperModel protocolMapper) {
- em.createNamedQuery("deleteFederatedUserConsentProtMappersByProtocolMapper")
- .setParameter("protocolMapperId", protocolMapper.getId())
+ // No op
+ }
+
+ @Override
+ public void preRemove(ClientScopeModel clientScope) {
+ em.createNamedQuery("deleteFederatedUserConsentClientScopesByClientScope")
+ .setParameter("scopeId", clientScope.getId())
.executeUpdate();
}
@@ -880,11 +846,7 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("userId", user.getId())
.setParameter("realmId", realm.getId())
.executeUpdate();
- em.createNamedQuery("deleteFederatedUserConsentProtMappersByUser")
- .setParameter("userId", user.getId())
- .setParameter("realmId", realm.getId())
- .executeUpdate();
- em.createNamedQuery("deleteFederatedUserConsentRolesByUser")
+ em.createNamedQuery("deleteFederatedUserConsentClientScopesByUser")
.setParameter("userId", user.getId())
.setParameter("realmId", realm.getId())
.executeUpdate();
@@ -929,10 +891,7 @@ public class JpaUserFederatedStorageProvider implements
em.createNamedQuery("deleteFederatedAttributesByStorageProvider")
.setParameter("storageProviderId", model.getId())
.executeUpdate();
- em.createNamedQuery("deleteFederatedUserConsentProtMappersByStorageProvider")
- .setParameter("storageProviderId", model.getId())
- .executeUpdate();
- em.createNamedQuery("deleteFederatedUserRoleMappingsByStorageProvider")
+ em.createNamedQuery("deleteFederatedUserConsentClientScopesByStorageProvider")
.setParameter("storageProviderId", model.getId())
.executeUpdate();
em.createNamedQuery("deleteFederatedUserConsentsByStorageProvider")
@@ -957,10 +916,7 @@ public class JpaUserFederatedStorageProvider implements
.setParameter("storageProviderId", model.getId())
.executeUpdate();
} else if (model.getProviderType().equals(ClientStorageProvider.class.getName())) {
- em.createNamedQuery("deleteFederatedUserConsentProtMappersByClientStorageProvider")
- .setParameter("clientStorageProvider", model.getId())
- .executeUpdate();
- em.createNamedQuery("deleteFederatedUserConsentRolesByClientStorageProvider")
+ em.createNamedQuery("deleteFederatedUserConsentClientScopesByClientStorageProvider")
.setParameter("clientStorageProvider", model.getId())
.executeUpdate();
em.createNamedQuery("deleteFederatedUserConsentsByClientStorageProvider")
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
index 085844d..e34f2d3 100644
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-4.0.0.xml
@@ -84,4 +84,184 @@
<addPrimaryKey columnNames="USER_SESSION_ID,CLIENT_ID, CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_CL_SES_PK3" tableName="OFFLINE_CLIENT_SESSION"/>
</changeSet>
+
+ <changeSet author="mposolda@redhat.com" id="4.0.0-KEYCLOAK-5579">
+
+ <!-- 1 - Rename clientTemplate to clientScope and drop some unused things from clientTemplate -->
+ <dropForeignKeyConstraint baseTableName="CLIENT_TEMPLATE_ATTRIBUTES" constraintName="FK_CL_TEMPL_ATTR_TEMPL" />
+ <renameTable oldTableName="CLIENT_TEMPLATE_ATTRIBUTES" newTableName="CLIENT_SCOPE_ATTRIBUTES" />
+ <renameColumn tableName="CLIENT_SCOPE_ATTRIBUTES" newColumnName="SCOPE_ID" oldColumnName="TEMPLATE_ID" columnDataType="VARCHAR(36)" />
+
+ <dropForeignKeyConstraint baseTableName="TEMPLATE_SCOPE_MAPPING" constraintName="FK_TEMPL_SCOPE_TEMPL" />
+ <dropForeignKeyConstraint baseTableName="TEMPLATE_SCOPE_MAPPING" constraintName="FK_TEMPL_SCOPE_ROLE" />
+ <renameTable oldTableName="TEMPLATE_SCOPE_MAPPING" newTableName="CLIENT_SCOPE_ROLE_MAPPING" />
+ <renameColumn tableName="CLIENT_SCOPE_ROLE_MAPPING" newColumnName="SCOPE_ID" oldColumnName="TEMPLATE_ID" columnDataType="VARCHAR(36)" />
+
+ <dropForeignKeyConstraint baseTableName="CLIENT" constraintName="FK_CLI_TMPLT_CLIENT" />
+
+ <dropForeignKeyConstraint baseTableName="PROTOCOL_MAPPER" constraintName="FK_CLI_TMPLT_MAPPER" />
+ <renameColumn tableName="PROTOCOL_MAPPER" newColumnName="CLIENT_SCOPE_ID" oldColumnName="CLIENT_TEMPLATE_ID" columnDataType="VARCHAR(36)" />
+
+ <dropForeignKeyConstraint baseTableName="CLIENT_TEMPLATE" constraintName="FK_REALM_CLI_TMPLT" />
+ <dropUniqueConstraint constraintName="UK_CLI_TEMPLATE" tableName="CLIENT_TEMPLATE"/>
+
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="FULL_SCOPE_ALLOWED" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="CONSENT_REQUIRED" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="STANDARD_FLOW_ENABLED" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="IMPLICIT_FLOW_ENABLED" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="DIRECT_ACCESS_GRANTS_ENABLED" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="SERVICE_ACCOUNTS_ENABLED" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="FRONTCHANNEL_LOGOUT" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="BEARER_ONLY" />
+ <dropDefaultValue tableName="CLIENT_TEMPLATE" columnName="PUBLIC_CLIENT" />
+
+ <dropIndex tableName="CLIENT_SCOPE_ROLE_MAPPING" indexName="IDX_TEMPL_SCOPE_MAPP_ROLE" />
+ <dropIndex tableName="PROTOCOL_MAPPER" indexName="IDX_PROTO_MAPP_CLIENT_TEMPL" />
+ <dropIndex tableName="CLIENT" indexName="IDX_CLIENT_CLIENT_TEMPL_ID" />
+
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="FULL_SCOPE_ALLOWED" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="CONSENT_REQUIRED" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="STANDARD_FLOW_ENABLED" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="IMPLICIT_FLOW_ENABLED" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="DIRECT_ACCESS_GRANTS_ENABLED" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="SERVICE_ACCOUNTS_ENABLED" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="FRONTCHANNEL_LOGOUT" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="BEARER_ONLY" />
+ <dropColumn tableName="CLIENT_TEMPLATE" columnName="PUBLIC_CLIENT" />
+
+ <renameTable oldTableName="CLIENT_TEMPLATE" newTableName="CLIENT_SCOPE" />
+
+ <addUniqueConstraint columnNames="REALM_ID,NAME" constraintName="UK_CLI_SCOPE" tableName="CLIENT_SCOPE"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="CLIENT_SCOPE"
+ constraintName="FK_REALM_CLI_SCOPE" referencedColumnNames="ID" referencedTableName="REALM"/>
+
+ <addForeignKeyConstraint baseColumnNames="CLIENT_SCOPE_ID" baseTableName="PROTOCOL_MAPPER"
+ constraintName="FK_CLI_SCOPE_MAPPER" referencedColumnNames="ID" referencedTableName="CLIENT_SCOPE"/>
+
+ <addForeignKeyConstraint baseColumnNames="SCOPE_ID" baseTableName="CLIENT_SCOPE_ROLE_MAPPING"
+ constraintName="FK_CL_SCOPE_RM_SCOPE" referencedColumnNames="ID" referencedTableName="CLIENT_SCOPE"/>
+ <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="CLIENT_SCOPE_ROLE_MAPPING"
+ constraintName="FK_CL_SCOPE_RM_ROLE" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+
+ <addForeignKeyConstraint baseTableName="CLIENT_SCOPE_ATTRIBUTES" baseColumnNames="SCOPE_ID"
+ constraintName="FK_CL_SCOPE_ATTR_SCOPE" referencedTableName="CLIENT_SCOPE" referencedColumnNames="ID" />
+
+ <!-- 2 - Client binding to more clientScopes -->
+ <!-- Foreign key dropped above TODO:mposolda migration existing clientTemplate to default clientScope -->
+ <dropColumn tableName="CLIENT" columnName="CLIENT_TEMPLATE_ID" />
+ <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_CONFIG"/>
+ <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_SCOPE" />
+ <dropDefaultValue tableName="CLIENT" columnName="USE_TEMPLATE_MAPPERS" />
+ <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_CONFIG" />
+ <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_SCOPE" />
+ <dropColumn tableName="CLIENT" columnName="USE_TEMPLATE_MAPPERS" />
+
+ <createTable tableName="CLIENT_SCOPE_CLIENT">
+ <column name="CLIENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="SCOPE_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="DEFAULT_SCOPE" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <addPrimaryKey columnNames="CLIENT_ID, SCOPE_ID" constraintName="C_CLI_SCOPE_BIND" tableName="CLIENT_SCOPE_CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_SCOPE_CLIENT" constraintName="FK_C_CLI_SCOPE_CLIENT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+ <addForeignKeyConstraint baseColumnNames="SCOPE_ID" baseTableName="CLIENT_SCOPE_CLIENT" constraintName="FK_C_CLI_SCOPE_SCOPE" referencedColumnNames="ID" referencedTableName="CLIENT_SCOPE"/>
+
+ <!-- Default client scopes (global scopes configured at realm level) -->
+ <createTable tableName="DEFAULT_CLIENT_SCOPE">
+ <column name="REALM_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="SCOPE_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="DEFAULT_SCOPE" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <addPrimaryKey columnNames="REALM_ID, SCOPE_ID" constraintName="R_DEF_CLI_SCOPE_BIND" tableName="DEFAULT_CLIENT_SCOPE"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="DEFAULT_CLIENT_SCOPE" constraintName="FK_R_DEF_CLI_SCOPE_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="SCOPE_ID" baseTableName="DEFAULT_CLIENT_SCOPE" constraintName="FK_R_DEF_CLI_SCOPE_SCOPE" referencedColumnNames="ID" referencedTableName="CLIENT_SCOPE"/>
+
+ <!-- Remove scopeParamRequired -->
+ <dropDefaultValue tableName="KEYCLOAK_ROLE" columnName="SCOPE_PARAM_REQUIRED" />
+ <dropColumn tableName="KEYCLOAK_ROLE" columnName="SCOPE_PARAM_REQUIRED" />
+
+ <!-- Drop consent stuff from protocolMappers table -->
+ <dropDefaultValue tableName="PROTOCOL_MAPPER" columnName="CONSENT_REQUIRED" />
+ <dropColumn tableName="PROTOCOL_MAPPER" columnName="CONSENT_REQUIRED" />
+ <dropColumn tableName="PROTOCOL_MAPPER" columnName="CONSENT_TEXT" />
+
+ <!-- Consents related changes -->
+ <dropForeignKeyConstraint baseTableName="USER_CONSENT_ROLE" constraintName="FK_GRNTCSNT_ROLE_GR" />
+ <dropTable tableName="USER_CONSENT_ROLE" />
+ <dropForeignKeyConstraint baseTableName="USER_CONSENT_PROT_MAPPER" constraintName="FK_GRNTCSNT_PRM_GR" />
+ <dropTable tableName="USER_CONSENT_PROT_MAPPER" />
+
+ <createTable tableName="USER_CONSENT_CLIENT_SCOPE">
+ <column name="USER_CONSENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="SCOPE_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <addPrimaryKey columnNames="USER_CONSENT_ID, SCOPE_ID" constraintName="CONSTRAINT_GRNTCSNT_CLSC_PM" tableName="USER_CONSENT_CLIENT_SCOPE"/>
+ <addForeignKeyConstraint baseColumnNames="USER_CONSENT_ID" baseTableName="USER_CONSENT_CLIENT_SCOPE" constraintName="FK_GRNTCSNT_CLSC_USC" referencedColumnNames="ID" referencedTableName="USER_CONSENT"/>
+
+ <!-- Federated consents related changes -->
+ <dropTable tableName="FED_USER_CONSENT_ROLE" />
+ <dropTable tableName="FED_USER_CONSENT_PROT_MAPPER" />
+
+ <createTable tableName="FED_USER_CONSENT_CL_SCOPE">
+ <column name="USER_CONSENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="SCOPE_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <addPrimaryKey columnNames="USER_CONSENT_ID, SCOPE_ID" constraintName="CONSTRAINT_FGRNTCSNT_CLSC_PM" tableName="FED_USER_CONSENT_CL_SCOPE"/>
+
+ <!-- Indexes for foreign keys -->
+ <createIndex indexName="IDX_REALM_CLSCOPE" tableName="CLIENT_SCOPE">
+ <column name="REALM_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_CLSCOPE_PROTMAP" tableName="PROTOCOL_MAPPER">
+ <column name="CLIENT_SCOPE_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_CLSCOPE_ROLE" tableName="CLIENT_SCOPE_ROLE_MAPPING">
+ <column name="SCOPE_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_ROLE_CLSCOPE" tableName="CLIENT_SCOPE_ROLE_MAPPING">
+ <column name="ROLE_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_CLSCOPE_ATTRS" tableName="CLIENT_SCOPE_ATTRIBUTES">
+ <column name="SCOPE_ID" type="VARCHAR(36)"/>
+ </createIndex>
+
+ <createIndex indexName="IDX_CLSCOPE_CL" tableName="CLIENT_SCOPE_CLIENT">
+ <column name="CLIENT_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_CL_CLSCOPE" tableName="CLIENT_SCOPE_CLIENT">
+ <column name="SCOPE_ID" type="VARCHAR(36)"/>
+ </createIndex>
+
+ <createIndex indexName="IDX_DEFCLS_REALM" tableName="DEFAULT_CLIENT_SCOPE">
+ <column name="REALM_ID" type="VARCHAR(36)"/>
+ </createIndex>
+ <createIndex indexName="IDX_DEFCLS_SCOPE" tableName="DEFAULT_CLIENT_SCOPE">
+ <column name="SCOPE_ID" type="VARCHAR(36)"/>
+ </createIndex>
+
+ <createIndex indexName="IDX_USCONSENT_CLSCOPE" tableName="USER_CONSENT_CLIENT_SCOPE">
+ <column name="USER_CONSENT_ID" type="VARCHAR(36)"/>
+ </createIndex>
+
+ </changeSet>
+
</databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/persistence.xml b/model/jpa/src/main/resources/META-INF/persistence.xml
index 805b7c8..76aae9d 100755
--- a/model/jpa/src/main/resources/META-INF/persistence.xml
+++ b/model/jpa/src/main/resources/META-INF/persistence.xml
@@ -41,8 +41,7 @@
<class>org.keycloak.models.jpa.entities.IdentityProviderMapperEntity</class>
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>
<class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
- <class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
- <class>org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity</class>
+ <class>org.keycloak.models.jpa.entities.UserConsentClientScopeEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationFlowEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticatorConfigEntity</class>
@@ -53,8 +52,10 @@
<class>org.keycloak.models.jpa.entities.GroupAttributeEntity</class>
<class>org.keycloak.models.jpa.entities.GroupRoleMappingEntity</class>
<class>org.keycloak.models.jpa.entities.UserGroupMembershipEntity</class>
- <class>org.keycloak.models.jpa.entities.ClientTemplateEntity</class>
- <class>org.keycloak.models.jpa.entities.TemplateScopeMappingEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientScopeEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientScopeRoleMappingEntity</class>
+ <class>org.keycloak.models.jpa.entities.ClientScopeClientMappingEntity</class>
+ <class>org.keycloak.models.jpa.entities.DefaultClientScopeRealmMappingEntity</class>
<class>org.keycloak.models.jpa.entities.ClientInitialAccessEntity</class>
<!-- JpaAuditProviders -->
@@ -74,8 +75,7 @@
<class>org.keycloak.storage.jpa.entity.FederatedUser</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserAttributeEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserConsentEntity</class>
- <class>org.keycloak.storage.jpa.entity.FederatedUserConsentRoleEntity</class>
- <class>org.keycloak.storage.jpa.entity.FederatedUserConsentProtocolMapperEntity</class>
+ <class>org.keycloak.storage.jpa.entity.FederatedUserConsentClientScopeEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserCredentialEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserCredentialAttributeEntity</class>
<class>org.keycloak.storage.jpa.entity.FederatedUserGroupMembershipEntity</class>
diff --git a/server-spi/src/main/java/org/keycloak/models/ClientModel.java b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
index aa406cf..ae6453a 100755
--- a/server-spi/src/main/java/org/keycloak/models/ClientModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/ClientModel.java
@@ -24,7 +24,7 @@ import java.util.Set;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
-public interface ClientModel extends RoleContainerModel, ProtocolMapperContainerModel, ScopeContainerModel {
+public interface ClientModel extends ClientScopeModel, RoleContainerModel, ProtocolMapperContainerModel, ScopeContainerModel {
// COMMON ATTRIBUTES
@@ -133,6 +133,9 @@ public interface ClientModel extends RoleContainerModel, ProtocolMapperContaine
boolean isFrontchannelLogout();
void setFrontchannelLogout(boolean flag);
+ boolean isFullScopeAllowed();
+ void setFullScopeAllowed(boolean value);
+
boolean isPublicClient();
void setPublicClient(boolean flag);
@@ -154,14 +157,24 @@ public interface ClientModel extends RoleContainerModel, ProtocolMapperContaine
RealmModel getRealm();
- ClientTemplateModel getClientTemplate();
- void setClientTemplate(ClientTemplateModel template);
- boolean useTemplateScope();
- void setUseTemplateScope(boolean flag);
- boolean useTemplateMappers();
- void setUseTemplateMappers(boolean flag);
- boolean useTemplateConfig();
- void setUseTemplateConfig(boolean flag);
+ /**
+ * Add clientScope with this client. Add it as default scope (if parameter 'defaultScope' is true) or optional scope (if parameter 'defaultScope' is false)
+ * @param clientScope
+ * @param defaultScope
+ */
+ void addClientScope(ClientScopeModel clientScope, boolean defaultScope);
+
+ void removeClientScope(ClientScopeModel clientScope);
+
+ /**
+ * Return all default scopes (if 'defaultScope' is true) or all optional scopes (if 'defaultScope' is false) linked with this client
+ *
+ * @param defaultScope
+ * @param filterByProtocol if true, then just client scopes of same protocol like current client will be returned
+ * @return map where key is the name of the clientScope, value is particular clientScope. Returns empty map if no scopes linked (never returns null).
+ */
+ Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol);
+
/**
* Time in seconds since epoc
@@ -183,4 +196,22 @@ public interface ClientModel extends RoleContainerModel, ProtocolMapperContaine
void registerNode(String nodeHost, int registrationTime);
void unregisterNode(String nodeHost);
+
+
+ // Clients are not displayed on consent screen by default
+ @Override
+ default boolean isDisplayOnConsentScreen() {
+ String displayVal = getAttribute(DISPLAY_ON_CONSENT_SCREEN);
+ return displayVal==null ? false : Boolean.parseBoolean(displayVal);
+ }
+
+ // Fallback to name or clientId if consentScreenText attribute is null
+ @Override
+ default String getConsentScreenText() {
+ String consentScreenText = ClientScopeModel.super.getConsentScreenText();
+ if (consentScreenText == null) {
+ consentScreenText = getClientId();
+ }
+ return consentScreenText;
+ }
}
diff --git a/server-spi/src/main/java/org/keycloak/models/ClientSessionContext.java b/server-spi/src/main/java/org/keycloak/models/ClientSessionContext.java
new file mode 100644
index 0000000..3edfeb6
--- /dev/null
+++ b/server-spi/src/main/java/org/keycloak/models/ClientSessionContext.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models;
+
+import java.util.Set;
+
+/**
+ * Request-scoped context object
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface ClientSessionContext {
+
+ AuthenticatedClientSessionModel getClientSession();
+
+ Set<String> getClientScopeIds();
+
+ Set<ClientScopeModel> getClientScopes();
+
+ Set<RoleModel> getRoles();
+
+ Set<ProtocolMapperModel> getProtocolMappers();
+
+ String getScopeString();
+}
diff --git a/server-spi/src/main/java/org/keycloak/models/ProtocolMapperModel.java b/server-spi/src/main/java/org/keycloak/models/ProtocolMapperModel.java
index 0233c82..017454c 100755
--- a/server-spi/src/main/java/org/keycloak/models/ProtocolMapperModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/ProtocolMapperModel.java
@@ -69,22 +69,6 @@ public class ProtocolMapperModel implements Serializable {
this.protocolMapper = protocolMapper;
}
- public boolean isConsentRequired() {
- return consentRequired;
- }
-
- public void setConsentRequired(boolean consentRequired) {
- this.consentRequired = consentRequired;
- }
-
- public String getConsentText() {
- return consentText;
- }
-
- public void setConsentText(String consentText) {
- this.consentText = consentText;
- }
-
public Map<String, String> getConfig() {
return config;
}
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmModel.java b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
index c316c47..581b59b 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmModel.java
@@ -437,14 +437,18 @@ public interface RealmModel extends RoleContainerModel {
boolean removeGroup(GroupModel group);
void moveGroup(GroupModel group, GroupModel toParent);
- List<ClientTemplateModel> getClientTemplates();
+ List<ClientScopeModel> getClientScopes();
- ClientTemplateModel addClientTemplate(String name);
+ ClientScopeModel addClientScope(String name);
- ClientTemplateModel addClientTemplate(String id, String name);
+ ClientScopeModel addClientScope(String id, String name);
- boolean removeClientTemplate(String id);
+ boolean removeClientScope(String id);
- ClientTemplateModel getClientTemplateById(String id);
+ ClientScopeModel getClientScopeById(String id);
+
+ void addDefaultClientScope(ClientScopeModel clientScope, boolean defaultScope);
+ void removeDefaultClientScope(ClientScopeModel clientScope);
+ List<ClientScopeModel> getDefaultClientScopes(boolean defaultScope);
}
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
index 6fed88a..5542953 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
@@ -19,7 +19,6 @@ package org.keycloak.models;
import org.keycloak.migration.MigrationModel;
import org.keycloak.provider.Provider;
-import org.keycloak.storage.client.ClientLookupProvider;
import java.util.List;
import java.util.Set;
@@ -72,7 +71,7 @@ public interface RealmProvider extends Provider, ClientProvider {
RoleModel getRoleById(String id, RealmModel realm);
- ClientTemplateModel getClientTemplateById(String id, RealmModel realm);
+ ClientScopeModel getClientScopeById(String id, RealmModel realm);
GroupModel getGroupById(String id, RealmModel realm);
diff --git a/server-spi/src/main/java/org/keycloak/models/RoleModel.java b/server-spi/src/main/java/org/keycloak/models/RoleModel.java
index ac88058..119539c 100755
--- a/server-spi/src/main/java/org/keycloak/models/RoleModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/RoleModel.java
@@ -34,10 +34,6 @@ public interface RoleModel {
void setName(String name);
- boolean isScopeParamRequired();
-
- void setScopeParamRequired(boolean scopeParamRequired);
-
boolean isComposite();
void addCompositeRole(RoleModel role);
diff --git a/server-spi/src/main/java/org/keycloak/models/ScopeContainerModel.java b/server-spi/src/main/java/org/keycloak/models/ScopeContainerModel.java
index 1c01e21..201c15f 100755
--- a/server-spi/src/main/java/org/keycloak/models/ScopeContainerModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/ScopeContainerModel.java
@@ -24,9 +24,6 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public interface ScopeContainerModel {
- boolean isFullScopeAllowed();
-
- void setFullScopeAllowed(boolean value);
Set<RoleModel> getScopeMappings();
diff --git a/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java b/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java
index 847696d..e2a55f7 100644
--- a/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java
@@ -26,8 +26,7 @@ import java.util.Set;
public class UserConsentModel {
private final ClientModel client;
- private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
- private Set<RoleModel> roles = new HashSet<RoleModel>();
+ private Set<ClientScopeModel> clientScopes = new HashSet<>();
private Long createdDate;
private Long lastUpdatedDate;
@@ -39,32 +38,18 @@ public class UserConsentModel {
return client;
}
- public void addGrantedRole(RoleModel role) {
- roles.add(role);
+ public void addGrantedClientScope(ClientScopeModel clientScope) {
+ clientScopes.add(clientScope);
}
- public Set<RoleModel> getGrantedRoles() {
- return roles;
+ public Set<ClientScopeModel> getGrantedClientScopes() {
+ return clientScopes;
}
- public boolean isRoleGranted(RoleModel role) {
- for (RoleModel currentRole : roles) {
- if (currentRole.getId().equals(role.getId())) return true;
- }
- return false;
- }
-
- public void addGrantedProtocolMapper(ProtocolMapperModel protocolMapper) {
- protocolMappers.add(protocolMapper);
- }
-
- public Set<ProtocolMapperModel> getGrantedProtocolMappers() {
- return protocolMappers;
- }
-
- public boolean isProtocolMapperGranted(ProtocolMapperModel protocolMapper) {
- for (ProtocolMapperModel currentProtMapper : protocolMappers) {
- if (currentProtMapper.getId().equals(protocolMapper.getId())) return true;
+ public boolean isClientScopeGranted(ClientScopeModel clientScope) {
+ // TODO: May need to be changed with adding support for client scopes inheritance
+ for (ClientScopeModel apprClientScope : clientScopes) {
+ if (apprClientScope.getId().equals(clientScope.getId())) return true;
}
return false;
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserProvider.java b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
index c337f3a..d73103e 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
@@ -92,6 +92,7 @@ public interface UserProvider extends Provider,
void preRemove(RealmModel realm, ClientModel client);
void preRemove(ProtocolMapperModel protocolMapper);
+ void preRemove(ClientScopeModel clientScope);
void close();
diff --git a/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionModel.java b/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionModel.java
index 1dcbee7..7d10fe0 100644
--- a/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionModel.java
+++ b/server-spi/src/main/java/org/keycloak/sessions/AuthenticationSessionModel.java
@@ -129,4 +129,14 @@ public interface AuthenticationSessionModel extends CommonClientSessionModel {
*/
void clearClientNotes();
+ /**
+ * Get client scope IDs
+ */
+ Set<String> getClientScopes();
+
+ /**
+ * Set client scope IDs
+ */
+ void setClientScopes(Set<String> clientScopes);
+
}
diff --git a/server-spi/src/main/java/org/keycloak/sessions/CommonClientSessionModel.java b/server-spi/src/main/java/org/keycloak/sessions/CommonClientSessionModel.java
index b59ffa1..0e5573b 100644
--- a/server-spi/src/main/java/org/keycloak/sessions/CommonClientSessionModel.java
+++ b/server-spi/src/main/java/org/keycloak/sessions/CommonClientSessionModel.java
@@ -17,9 +17,6 @@
package org.keycloak.sessions;
-import java.util.Map;
-import java.util.Set;
-
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
@@ -42,14 +39,6 @@ public interface CommonClientSessionModel {
public String getProtocol();
public void setProtocol(String method);
- // TODO: Not needed here...?
- public Set<String> getRoles();
- public void setRoles(Set<String> roles);
-
- // TODO: Not needed here...?
- public Set<String> getProtocolMappers();
- public void setProtocolMappers(Set<String> protocolMappers);
-
public static enum Action {
OAUTH_GRANT,
AUTHENTICATE,
diff --git a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
index da42c4c..82931c6 100755
--- a/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
+++ b/server-spi/src/main/java/org/keycloak/storage/federated/UserFederatedStorageProvider.java
@@ -19,6 +19,7 @@ package org.keycloak.storage.federated;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -55,6 +56,8 @@ public interface UserFederatedStorageProvider extends Provider,
void preRemove(ProtocolMapperModel protocolMapper);
+ void preRemove(ClientScopeModel clientScope);
+
void preRemove(RealmModel realm, UserModel user);
void preRemove(RealmModel realm, ComponentModel model);
diff --git a/server-spi-private/src/main/java/org/keycloak/events/admin/ResourceType.java b/server-spi-private/src/main/java/org/keycloak/events/admin/ResourceType.java
index 128b344..2270784 100644
--- a/server-spi-private/src/main/java/org/keycloak/events/admin/ResourceType.java
+++ b/server-spi-private/src/main/java/org/keycloak/events/admin/ResourceType.java
@@ -141,7 +141,7 @@ public enum ResourceType {
/**
*
*/
- , CLIENT_TEMPLATE
+ , CLIENT_SCOPE
/**
*
diff --git a/server-spi-private/src/main/java/org/keycloak/events/Details.java b/server-spi-private/src/main/java/org/keycloak/events/Details.java
index bfe9073..81c8291 100755
--- a/server-spi-private/src/main/java/org/keycloak/events/Details.java
+++ b/server-spi-private/src/main/java/org/keycloak/events/Details.java
@@ -47,6 +47,7 @@ public interface Details {
String REASON = "reason";
String REVOKED_CLIENT = "revoked_client";
String AUDIENCE = "audience";
+ String SCOPE = "scope";
String REQUESTED_ISSUER = "requested_issuer";
String REQUESTED_SUBJECT = "requested_subject";
String CLIENT_SESSION_STATE = "client_session_state";
diff --git a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
index 31f430d..eb8013e 100755
--- a/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java
@@ -17,6 +17,7 @@
package org.keycloak.forms.login;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
@@ -84,8 +85,7 @@ public interface LoginFormsProvider extends Provider {
LoginFormsProvider setClientSessionCode(String accessCode);
- LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappers);
- LoginFormsProvider setAccessRequest(String message);
+ LoginFormsProvider setAccessRequest(List<ClientScopeModel> clientScopesRequested);
/**
* Set one global error message.
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java
index dc13fa6..e68f694 100755
--- a/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java
@@ -38,6 +38,7 @@ import org.keycloak.migration.migrators.MigrateTo3_2_0;
import org.keycloak.migration.migrators.MigrateTo3_4_0;
import org.keycloak.migration.migrators.MigrateTo3_4_1;
import org.keycloak.migration.migrators.MigrateTo3_4_2;
+import org.keycloak.migration.migrators.MigrateTo4_0_0;
import org.keycloak.migration.migrators.Migration;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -70,7 +71,8 @@ public class MigrationModelManager {
new MigrateTo3_2_0(),
new MigrateTo3_4_0(),
new MigrateTo3_4_1(),
- new MigrateTo3_4_2()
+ new MigrateTo3_4_2(),
+ new MigrateTo4_0_0()
};
public static void migrate(KeycloakSession session) {
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/MigrationProvider.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationProvider.java
index f21934e..0fcbd63 100755
--- a/server-spi-private/src/main/java/org/keycloak/migration/MigrationProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/MigrationProvider.java
@@ -23,6 +23,7 @@ import org.keycloak.provider.Provider;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import java.util.List;
+import java.util.Map;
/**
* Various common utils needed for migration from older version to newer
@@ -37,7 +38,7 @@ public interface MigrationProvider extends Provider {
*/
List<ProtocolMapperRepresentation> getMappersForClaimMask(Long claimMask);
- List<ProtocolMapperModel> getBuiltinMappers(String protocol);
+ Map<String, ProtocolMapperModel> getBuiltinMappers(String protocol);
void setupAdminCli(RealmModel realm);
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java
index 1dffc27..db5c42b 100755
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java
@@ -53,7 +53,6 @@ public class MigrateTo1_2_0 implements Migration {
if (roleModel != null) continue;
roleModel = client.addRole(role);
roleModel.setDescription("${role_" + role.toLowerCase().replaceAll("_", "-") + "}");
- roleModel.setScopeParamRequired(false);
}
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java
index d044d92..d66bfc1 100644
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java
@@ -45,13 +45,7 @@ public class MigrateTo1_6_0 implements Migration {
public void migrate(KeycloakSession session) {
MigrationProvider provider = session.getProvider(MigrationProvider.class);
- List<ProtocolMapperModel> builtinMappers = provider.getBuiltinMappers("openid-connect");
- ProtocolMapperModel localeMapper = null;
- for (ProtocolMapperModel m : builtinMappers) {
- if (m.getName().equals("locale")) {
- localeMapper = m;
- }
- }
+ ProtocolMapperModel localeMapper = provider.getBuiltinMappers("openid-connect").get("locale");
if (localeMapper == null) {
throw new RuntimeException("Can't find default locale mapper");
@@ -66,13 +60,8 @@ public class MigrateTo1_6_0 implements Migration {
@Override
public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
MigrationProvider provider = session.getProvider(MigrationProvider.class);
- List<ProtocolMapperModel> builtinMappers = provider.getBuiltinMappers("openid-connect");
- ProtocolMapperModel localeMapper = null;
- for (ProtocolMapperModel m : builtinMappers) {
- if (m.getName().equals("locale")) {
- localeMapper = m;
- }
- }
+ ProtocolMapperModel localeMapper = provider.getBuiltinMappers("openid-connect").get("locale");
+
if (localeMapper == null) {
throw new RuntimeException("Can't find default locale mapper");
}
@@ -85,16 +74,7 @@ public class MigrateTo1_6_0 implements Migration {
realm.setOfflineSessionIdleTimeout(Constants.DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT);
if (realm.getRole(Constants.OFFLINE_ACCESS_ROLE) == null) {
- for (RoleModel realmRole : realm.getRoles()) {
- realmRole.setScopeParamRequired(false);
- }
- for (ClientModel client : realm.getClients()) {
- for (RoleModel clientRole : client.getRoles()) {
- clientRole.setScopeParamRequired(false);
- }
- }
-
- KeycloakModelUtils.setupOfflineTokens(realm);
+ KeycloakModelUtils.setupOfflineRole(realm);
RoleModel role = realm.getRole(Constants.OFFLINE_ACCESS_ROLE);
// Bulk grant of offline_access role to all users
@@ -110,7 +90,6 @@ public class MigrateTo1_6_0 implements Migration {
if (client.getRole(AdminRoles.CREATE_CLIENT) == null) {
RoleModel role = client.addRole(AdminRoles.CREATE_CLIENT);
role.setDescription("${role_" + AdminRoles.CREATE_CLIENT + "}");
- role.setScopeParamRequired(false);
client.getRealm().getRole(AdminRoles.ADMIN).addCompositeRole(role);
}
@@ -120,8 +99,6 @@ public class MigrateTo1_6_0 implements Migration {
if (client.getRole(AdminRoles.CREATE_CLIENT) == null) {
RoleModel role = client.addRole(AdminRoles.CREATE_CLIENT);
role.setDescription("${role_" + AdminRoles.CREATE_CLIENT + "}");
- role.setScopeParamRequired(false);
-
client.getRole(AdminRoles.REALM_ADMIN).addCompositeRole(role);
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java
index 8568776..af9fe9e 100644
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java
@@ -20,7 +20,7 @@ package org.keycloak.migration.migrators;
import org.keycloak.migration.ModelVersion;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -44,8 +44,8 @@ public class MigrateTo2_3_0 implements Migration {
MigrationUtils.updateProtocolMappers(client);
}
- for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) {
- MigrationUtils.updateProtocolMappers(clientTemplate);
+ for (ClientScopeModel clientScope : realm.getClientScopes()) {
+ MigrationUtils.updateProtocolMappers(clientScope);
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java
index 95623c0..b28264f 100644
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_5_0.java
@@ -19,8 +19,6 @@ package org.keycloak.migration.migrators;
import org.keycloak.migration.ModelVersion;
-import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultKeyProviders;
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java
new file mode 100644
index 0000000..659ca62
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo4_0_0.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.migration.migrators;
+
+import java.util.List;
+
+import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.component.ComponentModel;
+import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.DefaultClientScopes;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.RealmRepresentation;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MigrateTo4_0_0 implements Migration {
+
+ public static final ModelVersion VERSION = new ModelVersion("4.0.0");
+
+ private static final Logger LOG = Logger.getLogger(MigrateTo4_0_0.class);
+
+ @Override
+ public ModelVersion getVersion() {
+ return VERSION;
+ }
+
+ @Override
+ public void migrate(KeycloakSession session) {
+ session.realms().getRealms().stream().forEach(
+ r -> {
+ migrateRealm(session, r, false);
+ }
+ );
+ }
+
+ @Override
+ public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
+ migrateRealm(session, realm, true);
+ }
+
+
+ protected void migrateRealm(KeycloakSession session, RealmModel realm, boolean json) {
+ // Upgrade names of clientScopes to not contain space
+ for (ClientScopeModel clientScope : realm.getClientScopes()) {
+ if (clientScope.getName().contains(" ")) {
+ LOG.debugf("Replacing spaces with underscores in the name of client scope '%s' of realm '%s'", clientScope.getName(), realm.getName());
+ String replacedName = clientScope.getName().replaceAll(" ", "_");
+ clientScope.setName(replacedName);
+ }
+ }
+
+ if (!json) {
+ // Add default client scopes. But don't add them to existing clients. For JSON, they were already added
+ LOG.debugf("Adding defaultClientScopes for realm '%s'", realm.getName());
+ DefaultClientScopes.createDefaultClientScopes(session, realm, false);
+ }
+
+ // Upgrade configuration of "allowed-client-templates" client registration policy
+ for (ComponentModel component : realm.getComponents(realm.getId(), "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy")) {
+ if ("allowed-client-templates".equals(component.getProviderId())) {
+ List<String> configVal = component.getConfig().remove("allowed-client-templates");
+ if (configVal != null) {
+ component.getConfig().put("allowed-client-scopes", configVal);
+ }
+ component.put("allow-default-scopes", true);
+
+ realm.updateComponent(component);
+ }
+ }
+
+
+ // If client has scope for offline_access role (either directly or through fullScopeAllowed), then add offline_access client
+ // scope as optional scope to the client. If it's indirectly (no fullScopeAllowed), then remove role from the scoped roles
+ RoleModel offlineAccessRole = realm.getRole(OAuth2Constants.OFFLINE_ACCESS);
+ ClientScopeModel offlineAccessScope = null;
+ if (offlineAccessRole == null) {
+ LOG.infof("Role 'offline_access' not available in realm '%s'. Skip migration of offline_access client scope.", realm.getName());
+ } else {
+ offlineAccessScope = KeycloakModelUtils.getClientScopeByName(realm, OAuth2Constants.OFFLINE_ACCESS);
+ if (offlineAccessScope == null) {
+ LOG.infof("Client scope 'offline_access' not available in realm '%s'. Skip migration of offline_access client scope.", realm.getName());
+ } else {
+ for (ClientModel client : realm.getClients()) {
+ if ("openid-connect".equals(client.getProtocol())
+ && !client.isBearerOnly()
+ && client.hasScope(offlineAccessRole)
+ && !client.getClientScopes(false, true).containsKey(OAuth2Constants.OFFLINE_ACCESS)) {
+ LOG.debugf("Adding client scope 'offline_access' as optional scope to client '%s' in realm '%s'.", client.getClientId(), realm.getName());
+ client.addClientScope(offlineAccessScope, false);
+
+ if (!client.isFullScopeAllowed()) {
+ LOG.debugf("Removing role scope mapping for role 'offline_access' from client '%s' in realm '%s'.", client.getClientId(), realm.getName());
+ client.deleteScopeMapping(offlineAccessRole);
+ }
+ }
+ }
+ }
+ }
+
+
+ // Clients with consentRequired, which don't have any client scopes will be added itself to require consent, so that consent screen is shown when users authenticate
+ for (ClientModel client : realm.getClients()) {
+ if (client.isConsentRequired() && client.getClientScopes(true, true).isEmpty()) {
+ LOG.debugf("Adding client '%s' of realm '%s' to display itself on consent screen", client.getClientId(), realm.getName());
+ client.setDisplayOnConsentScreen(true);
+ String consentText = client.getName()==null ? client.getClientId() : client.getName();
+ client.setConsentScreenText(consentText);
+ }
+ }
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
index 7eb997a..fd167d3 100644
--- a/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
+++ b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java
@@ -17,18 +17,27 @@
package org.keycloak.migration.migrators;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
+
import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.OAuthErrorException;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperContainerModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -40,7 +49,6 @@ public class MigrationUtils {
if (client != null && client.getRole(roleName) == null) {
RoleModel role = client.addRole(roleName);
role.setDescription("${role_" + roleName + "}");
- role.setScopeParamRequired(false);
client.getRealm().getRole(AdminRoles.ADMIN).addCompositeRole(role);
}
@@ -50,7 +58,6 @@ public class MigrationUtils {
if (client != null && client.getRole(roleName) == null) {
RoleModel role = client.addRole(roleName);
role.setDescription("${role_" + roleName + "}");
- role.setScopeParamRequired(false);
client.getRole(AdminRoles.REALM_ADMIN).addCompositeRole(role);
}
@@ -79,4 +86,27 @@ public class MigrationUtils {
}
}
+
+ // Called when offline token older than 4.0 (Offline token without clientScopeIds) is called
+ public static void migrateOldOfflineToken(KeycloakSession session, RealmModel realm, ClientModel client, UserModel user) throws OAuthErrorException {
+ ClientScopeModel offlineScope = KeycloakModelUtils.getClientScopeByName(realm, OAuth2Constants.OFFLINE_ACCESS);
+ if (offlineScope == null) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Offline Access scope not found");
+ }
+
+ if (client.isConsentRequired()) {
+ // Automatically add consents for client and for offline_access. We know that both were defacto approved by user already and offlineSession is still valid
+ UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId());
+ if (consent != null) {
+ if (client.isDisplayOnConsentScreen()) {
+ consent.addGrantedClientScope(client);
+ }
+ if (offlineScope.isDisplayOnConsentScreen()) {
+ consent.addGrantedClientScope(offlineScope);
+ }
+ session.users().updateConsent(realm, user.getId(), consent);
+ }
+ }
+ }
+
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java b/server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java
index ce71dee..41ee75e 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java
@@ -30,7 +30,7 @@ public interface CacheRealmProvider extends RealmProvider {
void registerRealmInvalidation(String id, String name);
void registerClientInvalidation(String id, String clientId, String realmId);
- void registerClientTemplateInvalidation(String id);
+ void registerClientScopeInvalidation(String id);
void registerRoleInvalidation(String id, String roleName, String roleContainerId);
diff --git a/server-spi-private/src/main/java/org/keycloak/models/ClientConfigResolver.java b/server-spi-private/src/main/java/org/keycloak/models/ClientConfigResolver.java
index 5e7347e..ca4cb26 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/ClientConfigResolver.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/ClientConfigResolver.java
@@ -18,55 +18,35 @@
package org.keycloak.models;
/**
+ * TODO: remove this class entirely?
+ *
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class ClientConfigResolver {
protected ClientModel client;
- protected ClientTemplateModel clientTemplate;
public ClientConfigResolver(ClientModel client) {
this.client = client;
- this.clientTemplate = client.getClientTemplate();
}
public String resolveAttribute(String name) {
- if (clientTemplate != null && client.useTemplateConfig()) {
- return clientTemplate.getAttribute(name);
- } else {
- return client.getAttribute(name);
- }
+ return client.getAttribute(name);
}
public boolean isFrontchannelLogout() {
- if (clientTemplate != null && client.useTemplateConfig()) {
- return clientTemplate.isFrontchannelLogout();
- }
-
return client.isFrontchannelLogout();
}
boolean isConsentRequired() {
- if (clientTemplate != null && client.useTemplateConfig()) {
- return clientTemplate.isConsentRequired();
- }
-
return client.isConsentRequired();
}
boolean isStandardFlowEnabled() {
- if (clientTemplate != null && client.useTemplateConfig()) {
- return clientTemplate.isStandardFlowEnabled();
- }
-
return client.isStandardFlowEnabled();
}
boolean isServiceAccountsEnabled() {
- if (clientTemplate != null && client.useTemplateConfig()) {
- return clientTemplate.isServiceAccountsEnabled();
- }
-
return client.isServiceAccountsEnabled();
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/Constants.java b/server-spi-private/src/main/java/org/keycloak/models/Constants.java
index ae2f4c7..8a682dc 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/Constants.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/Constants.java
@@ -69,4 +69,6 @@ public interface Constants {
String GENERATE = "GENERATE";
int DEFAULT_MAX_RESULTS = 100;
+
+ String OFFLINE_ACCESS_SCOPE_CONSENT_TEXT = "${offlineAccessScopeConsentText}";
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/ImpersonationConstants.java b/server-spi-private/src/main/java/org/keycloak/models/ImpersonationConstants.java
index 05f6b45..be338ce 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/ImpersonationConstants.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/ImpersonationConstants.java
@@ -43,7 +43,6 @@ public class ImpersonationConstants {
if (realmAdminApp.getRole(IMPERSONATION_ROLE) != null) return;
RoleModel impersonationRole = realmAdminApp.addRole(IMPERSONATION_ROLE);
impersonationRole.setDescription("${role_" + IMPERSONATION_ROLE + "}");
- impersonationRole.setScopeParamRequired(false);
adminRole.addCompositeRole(impersonationRole);
}
@@ -54,7 +53,6 @@ public class ImpersonationConstants {
if (realmAdminApp.getRole(IMPERSONATION_ROLE) != null) return;
RoleModel impersonationRole = realmAdminApp.addRole(IMPERSONATION_ROLE);
impersonationRole.setDescription("${role_" + IMPERSONATION_ROLE + "}");
- impersonationRole.setScopeParamRequired(false);
RoleModel adminRole = realmAdminApp.getRole(AdminRoles.REALM_ADMIN);
adminRole.addCompositeRole(impersonationRole);
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java
index f38b31c..2bb13b7 100644
--- a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentAuthenticatedClientSessionAdapter.java
@@ -48,9 +48,7 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
data.setAction(clientSession.getAction());
data.setAuthMethod(clientSession.getProtocol());
data.setNotes(clientSession.getNotes());
- data.setProtocolMappers(clientSession.getProtocolMappers());
data.setRedirectUri(clientSession.getRedirectUri());
- data.setRoles(clientSession.getRoles());
model = new PersistentClientSessionModel();
model.setClientId(clientSession.getClient().getId());
@@ -175,26 +173,6 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
}
@Override
- public Set<String> getRoles() {
- return getData().getRoles();
- }
-
- @Override
- public void setRoles(Set<String> roles) {
- getData().setRoles(roles);
- }
-
- @Override
- public Set<String> getProtocolMappers() {
- return getData().getProtocolMappers();
- }
-
- @Override
- public void setProtocolMappers(Set<String> protocolMappers) {
- getData().setProtocolMappers(protocolMappers);
- }
-
- @Override
public String getProtocol() {
return getData().getAuthMethod();
}
@@ -256,12 +234,6 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
@JsonProperty("redirectUri")
private String redirectUri;
- @JsonProperty("protocolMappers")
- private Set<String> protocolMappers;
-
- @JsonProperty("roles")
- private Set<String> roles;
-
@JsonProperty("notes")
private Map<String, String> notes;
@@ -275,6 +247,10 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
private Map<String, Object> executionStatus;
@JsonProperty("requiredActions")
private Set<String> requiredActions;
+ @JsonProperty("protocolMappers")
+ private Set<String> protocolMappers;
+ @JsonProperty("roles")
+ private Set<String> roles;
public String getAuthMethod() {
@@ -293,22 +269,6 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
this.redirectUri = redirectUri;
}
- public Set<String> getProtocolMappers() {
- return protocolMappers;
- }
-
- public void setProtocolMappers(Set<String> protocolMappers) {
- this.protocolMappers = protocolMappers;
- }
-
- public Set<String> getRoles() {
- return roles;
- }
-
- public void setRoles(Set<String> roles) {
- this.roles = roles;
- }
-
public Map<String, String> getNotes() {
return notes;
}
@@ -348,5 +308,21 @@ public class PersistentAuthenticatedClientSessionAdapter implements Authenticate
public void setRequiredActions(Set<String> requiredActions) {
this.requiredActions = requiredActions;
}
+
+ public Set<String> getProtocolMappers() {
+ return protocolMappers;
+ }
+
+ public void setProtocolMappers(Set<String> protocolMappers) {
+ this.protocolMappers = protocolMappers;
+ }
+
+ public Set<String> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Set<String> roles) {
+ this.roles = roles;
+ }
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java
index e650fee..61d5818 100644
--- a/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java
@@ -40,7 +40,6 @@ public class PersistentUserSessionAdapter implements OfflineUserSessionModel {
private final PersistentUserSessionModel model;
private UserModel user;
private String userId;
- private String username;
private final RealmModel realm;
private KeycloakSession session;
private final Map<String, AuthenticatedClientSessionModel> authenticatedClientSessions;
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultClientScopes.java b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultClientScopes.java
new file mode 100644
index 0000000..3ef8bf0
--- /dev/null
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultClientScopes.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.models.utils;
+
+import java.util.List;
+
+import org.keycloak.OAuth2Constants;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocolFactory;
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class DefaultClientScopes {
+
+ /**
+ *
+ * @param session
+ * @param realm
+ * @param addScopesToExistingClients true when creating new realm. False when migrating from previous version
+ */
+ public static void createDefaultClientScopes(KeycloakSession session, RealmModel realm, boolean addScopesToExistingClients) {
+ List<ProviderFactory> loginProtocolFactories = session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class);
+ for (ProviderFactory factory : loginProtocolFactories) {
+ LoginProtocolFactory lpf = (LoginProtocolFactory) factory;
+ lpf.createDefaultClientScopes(realm, addScopesToExistingClients);
+ }
+ }
+
+
+ // Asumption is that newRealm and offlineRole are not null AND offline_access clientScope doesn't yet exists in the realm. Caller of this method is supposed to ensure that.
+ public static void createOfflineAccessClientScope(RealmModel newRealm, RoleModel offlineRole) {
+ ClientScopeModel offlineAccessScope = newRealm.addClientScope(OAuth2Constants.OFFLINE_ACCESS);
+ offlineAccessScope.setDescription("OpenID Connect built-in scope: offline_access");
+ offlineAccessScope.setDisplayOnConsentScreen(true);
+ offlineAccessScope.setConsentScreenText(Constants.OFFLINE_ACCESS_SCOPE_CONSENT_TEXT);
+ offlineAccessScope.setProtocol("openid-connect");
+ offlineAccessScope.addScopeMapping(offlineRole);
+
+ // Optional scope. Needs to be requested by scope parameter
+ newRealm.addDefaultClientScope(offlineAccessScope, false);
+ }
+}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index dc69fc8..96d0cb3 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -26,7 +26,7 @@ import org.keycloak.component.ComponentModel;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
import org.keycloak.models.IdentityProviderModel;
@@ -303,13 +303,16 @@ public final class KeycloakModelUtils {
return str==null ? null : str.toLowerCase();
}
- public static void setupOfflineTokens(RealmModel realm) {
- if (realm.getRole(Constants.OFFLINE_ACCESS_ROLE) == null) {
- RoleModel role = realm.addRole(Constants.OFFLINE_ACCESS_ROLE);
- role.setDescription("${role_offline-access}");
- role.setScopeParamRequired(true);
+ public static RoleModel setupOfflineRole(RealmModel realm) {
+ RoleModel offlineRole = realm.getRole(Constants.OFFLINE_ACCESS_ROLE);
+
+ if (offlineRole == null) {
+ offlineRole = realm.addRole(Constants.OFFLINE_ACCESS_ROLE);
+ offlineRole.setDescription("${role_offline-access}");
realm.addDefaultRole(Constants.OFFLINE_ACCESS_ROLE);
}
+
+ return offlineRole;
}
@@ -500,29 +503,54 @@ public final class KeycloakModelUtils {
}
- public static boolean isClientTemplateUsed(RealmModel realm, ClientTemplateModel template) {
+ public static boolean isClientScopeUsed(RealmModel realm, ClientScopeModel clientScope) {
for (ClientModel client : realm.getClients()) {
- if (client.getClientTemplate() != null && client.getClientTemplate().getId().equals(template.getId())) return true;
+ if ((client.getClientScopes(true, false).containsKey(clientScope.getName())) ||
+ (client.getClientScopes(false, false).containsKey(clientScope.getName()))) {
+ return true;
+ }
}
return false;
}
- public static ClientTemplateModel getClientTemplateByName(RealmModel realm, String templateName) {
- for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) {
- if (templateName.equals(clientTemplate.getName())) {
- return clientTemplate;
+ public static ClientScopeModel getClientScopeByName(RealmModel realm, String clientScopeName) {
+ for (ClientScopeModel clientScope : realm.getClientScopes()) {
+ if (clientScopeName.equals(clientScope.getName())) {
+ return clientScope;
}
}
return null;
}
+ /**
+ * Lookup clientScope OR client by id. Method is useful if you know just ID, but you don't know
+ * if underlying model is clientScope or client
+ */
+ public static ClientScopeModel findClientScopeById(RealmModel realm, String clientScopeId) {
+ ClientScopeModel clientScope = realm.getClientScopeById(clientScopeId);
+
+ if (clientScope != null) {
+ return clientScope;
+ } else {
+ return realm.getClientById(clientScopeId);
+ }
+ }
+
+ /** Replace spaces in the name with underscore, so that scope name can be used as value of scope parameter **/
+ public static String convertClientScopeName(String previousName) {
+ if (previousName.contains(" ")) {
+ return previousName.replaceAll(" ", "_");
+ } else {
+ return previousName;
+ }
+ }
+
public static void setupAuthorizationServices(RealmModel realm) {
for (String roleName : Constants.AUTHZ_DEFAULT_AUTHORIZATION_ROLES) {
if (realm.getRole(roleName) == null) {
RoleModel role = realm.addRole(roleName);
role.setDescription("${role_" + roleName + "}");
- role.setScopeParamRequired(false);
realm.addDefaultRole(roleName);
}
}
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 9bd2f8e..3fb7574 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -211,7 +211,6 @@ public class ModelToRepresentation {
rep.setId(role.getId());
rep.setName(role.getName());
rep.setDescription(role.getDescription());
- rep.setScopeParamRequired(role.isScopeParamRequired());
rep.setComposite(role.isComposite());
rep.setClientRole(role.isClientRole());
rep.setContainerId(role.getContainerId());
@@ -468,20 +467,21 @@ public class ModelToRepresentation {
return rep;
}
- public static ClientTemplateRepresentation toRepresentation(ClientTemplateModel clientModel) {
- ClientTemplateRepresentation rep = new ClientTemplateRepresentation();
- rep.setId(clientModel.getId());
- rep.setName(clientModel.getName());
- rep.setDescription(clientModel.getDescription());
- rep.setProtocol(clientModel.getProtocol());
- if (!clientModel.getProtocolMappers().isEmpty()) {
+ public static ClientScopeRepresentation toRepresentation(ClientScopeModel clientScopeModel) {
+ ClientScopeRepresentation rep = new ClientScopeRepresentation();
+ rep.setId(clientScopeModel.getId());
+ rep.setName(clientScopeModel.getName());
+ rep.setDescription(clientScopeModel.getDescription());
+ rep.setProtocol(clientScopeModel.getProtocol());
+ if (!clientScopeModel.getProtocolMappers().isEmpty()) {
List<ProtocolMapperRepresentation> mappings = new LinkedList<>();
- for (ProtocolMapperModel model : clientModel.getProtocolMappers()) {
+ for (ProtocolMapperModel model : clientScopeModel.getProtocolMappers()) {
mappings.add(toRepresentation(model));
}
rep.setProtocolMappers(mappings);
}
- rep.setFullScopeAllowed(clientModel.isFullScopeAllowed());
+
+ rep.setAttributes(new HashMap<>(clientScopeModel.getAttributes()));
return rep;
}
@@ -515,7 +515,9 @@ public class ModelToRepresentation {
rep.setNotBefore(clientModel.getNotBefore());
rep.setNodeReRegistrationTimeout(clientModel.getNodeReRegistrationTimeout());
rep.setClientAuthenticatorType(clientModel.getClientAuthenticatorType());
- if (clientModel.getClientTemplate() != null) rep.setClientTemplate(clientModel.getClientTemplate().getName());
+
+ rep.setDefaultClientScopes(new LinkedList<>(clientModel.getClientScopes(true, false).keySet()));
+ rep.setOptionalClientScopes(new LinkedList<>(clientModel.getClientScopes(false, false).keySet()));
Set<String> redirectUris = clientModel.getRedirectUris();
if (redirectUris != null) {
@@ -542,9 +544,6 @@ public class ModelToRepresentation {
}
rep.setProtocolMappers(mappings);
}
- rep.setUseTemplateMappers(clientModel.useTemplateMappers());
- rep.setUseTemplateConfig(clientModel.useTemplateConfig());
- rep.setUseTemplateScope(clientModel.useTemplateScope());
return rep;
}
@@ -596,8 +595,6 @@ public class ModelToRepresentation {
rep.setConfig(config);
rep.setName(model.getName());
rep.setProtocolMapper(model.getProtocolMapper());
- rep.setConsentText(model.getConsentText());
- rep.setConsentRequired(model.isConsentRequired());
return rep;
}
@@ -616,33 +613,14 @@ public class ModelToRepresentation {
public static UserConsentRepresentation toRepresentation(UserConsentModel model) {
String clientId = model.getClient().getClientId();
- Map<String, List<String>> grantedProtocolMappers = new HashMap<String, List<String>>();
- for (ProtocolMapperModel protocolMapper : model.getGrantedProtocolMappers()) {
- String protocol = protocolMapper.getProtocol();
- List<String> currentProtocolMappers = grantedProtocolMappers.computeIfAbsent(protocol, k -> new LinkedList<String>());
- currentProtocolMappers.add(protocolMapper.getName());
+ List<String> grantedClientScopes = new LinkedList<>();
+ for (ClientScopeModel clientScope : model.getGrantedClientScopes()) {
+ grantedClientScopes.add(clientScope.getName());
}
- List<String> grantedRealmRoles = new LinkedList<String>();
- Map<String, List<String>> grantedClientRoles = new HashMap<String, List<String>>();
- for (RoleModel role : model.getGrantedRoles()) {
- if (role.getContainer() instanceof RealmModel) {
- grantedRealmRoles.add(role.getName());
- } else {
- ClientModel client2 = (ClientModel) role.getContainer();
-
- String clientId2 = client2.getClientId();
- List<String> currentClientRoles = grantedClientRoles.computeIfAbsent(clientId2, k -> new LinkedList<String>());
- currentClientRoles.add(role.getName());
- }
- }
-
-
UserConsentRepresentation consentRep = new UserConsentRepresentation();
consentRep.setClientId(clientId);
- consentRep.setGrantedProtocolMappers(grantedProtocolMappers);
- consentRep.setGrantedRealmRoles(grantedRealmRoles);
- consentRep.setGrantedClientRoles(grantedClientRoles);
+ consentRep.setGrantedClientScopes(grantedClientScopes);
consentRep.setCreatedDate(model.getCreatedDate());
consentRep.setLastUpdatedDate(model.getLastUpdatedDate());
return consentRep;
diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 99d3405..f3f57b8 100755
--- a/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -32,6 +32,7 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
import org.keycloak.authorization.AuthorizationProvider;
import org.keycloak.authorization.AuthorizationProviderFactory;
import org.keycloak.authorization.model.PermissionTicket;
@@ -61,7 +62,7 @@ import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
@@ -91,6 +92,7 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
import org.keycloak.representations.idm.ClaimRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ClientTemplateRepresentation;
import org.keycloak.representations.idm.ComponentExportRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
@@ -144,6 +146,7 @@ public class RepresentationToModel {
public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm, boolean skipUserDependent) {
convertDeprecatedSocialProviders(rep);
convertDeprecatedApplications(session, rep);
+ convertDeprecatedClientTemplates(rep);
newRealm.setName(rep.getRealm());
if (rep.getDisplayName() != null) newRealm.setDisplayName(rep.getDisplayName());
@@ -258,8 +261,29 @@ public class RepresentationToModel {
importIdentityProviders(rep, newRealm);
importIdentityProviderMappers(rep, newRealm);
- if (rep.getClientTemplates() != null) {
- createClientTemplates(session, rep, newRealm);
+ Map<String, ClientScopeModel> clientScopes = new HashMap<>();
+ if (rep.getClientScopes() != null) {
+ clientScopes = createClientScopes(session, rep.getClientScopes(), newRealm);
+ }
+ if (rep.getDefaultDefaultClientScopes() != null) {
+ for (String clientScopeName : rep.getDefaultDefaultClientScopes()) {
+ ClientScopeModel clientScope = clientScopes.get(clientScopeName);
+ if (clientScope != null) {
+ newRealm.addDefaultClientScope(clientScope, true);
+ } else {
+ logger.warnf("Referenced client scope '%s' doesn't exists", clientScopeName);
+ }
+ }
+ }
+ if (rep.getDefaultOptionalClientScopes() != null) {
+ for (String clientScopeName : rep.getDefaultOptionalClientScopes()) {
+ ClientScopeModel clientScope = clientScopes.get(clientScopeName);
+ if (clientScope != null) {
+ newRealm.addDefaultClientScope(clientScope, false);
+ } else {
+ logger.warnf("Referenced client scope '%s' doesn't exists", clientScopeName);
+ }
+ }
}
if (rep.getClients() != null) {
@@ -471,8 +495,6 @@ public class RepresentationToModel {
// Application role may already exists (for example if it is defaultRole)
RoleModel role = roleRep.getId() != null ? client.addRole(roleRep.getId(), roleRep.getName()) : client.addRole(roleRep.getName());
role.setDescription(roleRep.getDescription());
- boolean scopeParamRequired = roleRep.isScopeParamRequired() == null ? false : roleRep.isScopeParamRequired();
- role.setScopeParamRequired(scopeParamRequired);
}
}
}
@@ -762,6 +784,28 @@ public class RepresentationToModel {
}
}
+ private static void convertDeprecatedClientTemplates(RealmRepresentation realm) {
+ if (realm.getClientTemplates() != null) {
+
+ logger.warnf("Using deprecated 'clientTemplates' configuration in JSON representation for realm '%s'. It will be removed in future versions", realm.getRealm());
+
+ List<ClientScopeRepresentation> clientScopes = new LinkedList<>();
+ for (ClientTemplateRepresentation template : realm.getClientTemplates()) {
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setId(template.getId());
+ scopeRep.setName(template.getName());
+ scopeRep.setProtocol(template.getProtocol());
+ scopeRep.setDescription(template.getDescription());
+ scopeRep.setAttributes(template.getAttributes());
+ scopeRep.setProtocolMappers(template.getProtocolMappers());
+
+ clientScopes.add(scopeRep);
+ }
+
+ realm.setClientScopes(clientScopes);
+ }
+ }
+
public static void renameRealm(RealmModel realm, String name) {
if (name.equals(realm.getName())) return;
@@ -973,8 +1017,6 @@ public class RepresentationToModel {
public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
RoleModel role = roleRep.getId() != null ? newRealm.addRole(roleRep.getId(), roleRep.getName()) : newRealm.addRole(roleRep.getName());
if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
- boolean scopeParamRequired = roleRep.isScopeParamRequired() == null ? false : roleRep.isScopeParamRequired();
- role.setScopeParamRequired(scopeParamRequired);
}
private static void addComposites(RoleModel role, RoleRepresentation roleRep, RealmModel realm) {
@@ -1159,40 +1201,51 @@ public class RepresentationToModel {
}
if (resourceRep.getClientTemplate() != null) {
- for (ClientTemplateModel template : realm.getClientTemplates()) {
- if (template.getName().equals(resourceRep.getClientTemplate())) {
- client.setClientTemplate(template);
- break;
- }
- MigrationUtils.updateProtocolMappers(template);
- }
+ String clientTemplateName = KeycloakModelUtils.convertClientScopeName(resourceRep.getClientTemplate());
+ addClientScopeToClient(realm, client, clientTemplateName, true);
}
- if (resourceRep.isFullScopeAllowed() != null) {
- client.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
- } else {
- if (client.getClientTemplate() != null) {
- client.setFullScopeAllowed(!client.isConsentRequired() && client.getClientTemplate().isFullScopeAllowed());
+ if (resourceRep.getDefaultClientScopes() != null) {
+ // First remove all default/built in client scopes
+ for (ClientScopeModel clientScope : client.getClientScopes(true, false).values()) {
+ client.removeClientScope(clientScope);
+ }
- } else {
- client.setFullScopeAllowed(!client.isConsentRequired());
+ for (String clientScopeName : resourceRep.getDefaultClientScopes()) {
+ addClientScopeToClient(realm, client, clientScopeName, true);
}
}
- if (resourceRep.isUseTemplateConfig() != null) client.setUseTemplateConfig(resourceRep.isUseTemplateConfig());
- else client.setUseTemplateConfig(false); // default to false for now
+ if (resourceRep.getOptionalClientScopes() != null) {
+ // First remove all default/built in client scopes
+ for (ClientScopeModel clientScope : client.getClientScopes(false, false).values()) {
+ client.removeClientScope(clientScope);
+ }
- if (resourceRep.isUseTemplateScope() != null) client.setUseTemplateScope(resourceRep.isUseTemplateScope());
- else client.setUseTemplateScope(resourceRep.getClientTemplate() != null);
+ for (String clientScopeName : resourceRep.getOptionalClientScopes()) {
+ addClientScopeToClient(realm, client, clientScopeName, false);
+ }
+ }
- if (resourceRep.isUseTemplateMappers() != null)
- client.setUseTemplateMappers(resourceRep.isUseTemplateMappers());
- else client.setUseTemplateMappers(resourceRep.getClientTemplate() != null);
+ if (resourceRep.isFullScopeAllowed() != null) {
+ client.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
+ } else {
+ client.setFullScopeAllowed(!client.isConsentRequired());
+ }
client.updateClient();
return client;
}
+ private static void addClientScopeToClient(RealmModel realm, ClientModel client, String clientScopeName, boolean defaultScope) {
+ ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, clientScopeName);
+ if (clientScope != null) {
+ client.addClientScope(clientScope, defaultScope);
+ } else {
+ logger.warnf("Referenced client scope '%s' doesn't exists. Ignoring", clientScopeName);
+ }
+ }
+
public static void updateClient(ClientRepresentation rep, ClientModel resource) {
if (rep.getClientId() != null) resource.setClientId(rep.getClientId());
if (rep.getName() != null) resource.setName(rep.getName());
@@ -1267,108 +1320,57 @@ public class RepresentationToModel {
}
}
- if (rep.isUseTemplateConfig() != null) resource.setUseTemplateConfig(rep.isUseTemplateConfig());
- if (rep.isUseTemplateScope() != null) resource.setUseTemplateScope(rep.isUseTemplateScope());
- if (rep.isUseTemplateMappers() != null) resource.setUseTemplateMappers(rep.isUseTemplateMappers());
-
if (rep.getSecret() != null) resource.setSecret(rep.getSecret());
- if (rep.getClientTemplate() != null) {
- if (rep.getClientTemplate().equals(ClientTemplateRepresentation.NONE)) {
- resource.setClientTemplate(null);
- } else {
- RealmModel realm = resource.getRealm();
- for (ClientTemplateModel template : realm.getClientTemplates()) {
-
- if (template.getName().equals(rep.getClientTemplate())) {
- resource.setClientTemplate(template);
- if (rep.isUseTemplateConfig() == null) resource.setUseTemplateConfig(true);
- if (rep.isUseTemplateScope() == null) resource.setUseTemplateScope(true);
- if (rep.isUseTemplateMappers() == null) resource.setUseTemplateMappers(true);
- break;
- }
- }
- }
- }
-
resource.updateClient();
}
- // CLIENT TEMPLATES
+ // CLIENT SCOPES
- private static Map<String, ClientTemplateModel> createClientTemplates(KeycloakSession session, RealmRepresentation rep, RealmModel realm) {
- Map<String, ClientTemplateModel> appMap = new HashMap<>();
- for (ClientTemplateRepresentation resourceRep : rep.getClientTemplates()) {
- ClientTemplateModel app = createClientTemplate(session, realm, resourceRep);
+ private static Map<String, ClientScopeModel> createClientScopes(KeycloakSession session, List<ClientScopeRepresentation> clientScopes, RealmModel realm) {
+ Map<String, ClientScopeModel> appMap = new HashMap<>();
+ for (ClientScopeRepresentation resourceRep : clientScopes) {
+ ClientScopeModel app = createClientScope(session, realm, resourceRep);
appMap.put(app.getName(), app);
}
return appMap;
}
- public static ClientTemplateModel createClientTemplate(KeycloakSession session, RealmModel realm, ClientTemplateRepresentation resourceRep) {
- logger.debug("Create client template: {0}" + resourceRep.getName());
+ public static ClientScopeModel createClientScope(KeycloakSession session, RealmModel realm, ClientScopeRepresentation resourceRep) {
+ logger.debug("Create client scope: {0}" + resourceRep.getName());
- ClientTemplateModel client = resourceRep.getId() != null ? realm.addClientTemplate(resourceRep.getId(), resourceRep.getName()) : realm.addClientTemplate(resourceRep.getName());
- if (resourceRep.getName() != null) client.setName(resourceRep.getName());
- if (resourceRep.getDescription() != null) client.setDescription(resourceRep.getDescription());
- if (resourceRep.getProtocol() != null) client.setProtocol(resourceRep.getProtocol());
- if (resourceRep.isFullScopeAllowed() != null) client.setFullScopeAllowed(resourceRep.isFullScopeAllowed());
+ ClientScopeModel clientScope = resourceRep.getId() != null ? realm.addClientScope(resourceRep.getId(), resourceRep.getName()) : realm.addClientScope(resourceRep.getName());
+ if (resourceRep.getName() != null) clientScope.setName(resourceRep.getName());
+ if (resourceRep.getDescription() != null) clientScope.setDescription(resourceRep.getDescription());
+ if (resourceRep.getProtocol() != null) clientScope.setProtocol(resourceRep.getProtocol());
if (resourceRep.getProtocolMappers() != null) {
// first, remove all default/built in mappers
- Set<ProtocolMapperModel> mappers = client.getProtocolMappers();
- for (ProtocolMapperModel mapper : mappers) client.removeProtocolMapper(mapper);
+ Set<ProtocolMapperModel> mappers = clientScope.getProtocolMappers();
+ for (ProtocolMapperModel mapper : mappers) clientScope.removeProtocolMapper(mapper);
for (ProtocolMapperRepresentation mapper : resourceRep.getProtocolMappers()) {
- client.addProtocolMapper(toModel(mapper));
+ clientScope.addProtocolMapper(toModel(mapper));
}
+ MigrationUtils.updateProtocolMappers(clientScope);
}
- if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly());
- if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired());
-
- if (resourceRep.isStandardFlowEnabled() != null)
- client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled());
- if (resourceRep.isImplicitFlowEnabled() != null)
- client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled());
- if (resourceRep.isDirectAccessGrantsEnabled() != null)
- client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled());
- if (resourceRep.isServiceAccountsEnabled() != null)
- client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled());
-
- if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient());
- if (resourceRep.isFrontchannelLogout() != null)
- client.setFrontchannelLogout(resourceRep.isFrontchannelLogout());
if (resourceRep.getAttributes() != null) {
for (Map.Entry<String, String> entry : resourceRep.getAttributes().entrySet()) {
- client.setAttribute(entry.getKey(), entry.getValue());
+ clientScope.setAttribute(entry.getKey(), entry.getValue());
}
}
- return client;
+ return clientScope;
}
- public static void updateClientTemplate(ClientTemplateRepresentation rep, ClientTemplateModel resource) {
+ public static void updateClientScope(ClientScopeRepresentation rep, ClientScopeModel resource) {
if (rep.getName() != null) resource.setName(rep.getName());
if (rep.getDescription() != null) resource.setDescription(rep.getDescription());
- if (rep.isFullScopeAllowed() != null) {
- resource.setFullScopeAllowed(rep.isFullScopeAllowed());
- }
if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol());
- if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly());
- if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired());
- if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled());
- if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled());
- if (rep.isDirectAccessGrantsEnabled() != null)
- resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled());
- if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled());
- if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient());
- if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed());
- if (rep.isFrontchannelLogout() != null) resource.setFrontchannelLogout(rep.isFrontchannelLogout());
-
if (rep.getAttributes() != null) {
for (Map.Entry<String, String> entry : rep.getAttributes().entrySet()) {
resource.setAttribute(entry.getKey(), entry.getValue());
@@ -1456,14 +1458,21 @@ public class RepresentationToModel {
throw new RuntimeException("Unknown client specification in scope mappings: " + scope.getClient());
}
return client;
- } else if (scope.getClientTemplate() != null) {
- ClientTemplateModel clientTemplate = KeycloakModelUtils.getClientTemplateByName(realm, scope.getClientTemplate());
+ } else if (scope.getClientScope() != null) {
+ ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, scope.getClientScope());
+ if (clientScope == null) {
+ throw new RuntimeException("Unknown clientScope specification in scope mappings: " + scope.getClientScope());
+ }
+ return clientScope;
+ } else if (scope.getClientTemplate() != null) { // Backwards compatibility
+ String templateName = KeycloakModelUtils.convertClientScopeName(scope.getClientTemplate());
+ ClientScopeModel clientTemplate = KeycloakModelUtils.getClientScopeByName(realm, templateName);
if (clientTemplate == null) {
- throw new RuntimeException("Unknown clientTemplate specification in scope mappings: " + scope.getClientTemplate());
+ throw new RuntimeException("Unknown clientScope specification in scope mappings: " + templateName);
}
return clientTemplate;
} else {
- throw new RuntimeException("Either client or clientTemplate needs to be specified in scope mappings");
+ throw new RuntimeException("Either client or clientScope needs to be specified in scope mappings");
}
}
@@ -1734,8 +1743,6 @@ public class RepresentationToModel {
ProtocolMapperModel model = new ProtocolMapperModel();
model.setId(rep.getId());
model.setName(rep.getName());
- model.setConsentRequired(rep.isConsentRequired());
- model.setConsentText(rep.getConsentText());
model.setProtocol(rep.getProtocol());
model.setProtocolMapper(rep.getProtocolMapper());
model.setConfig(removeEmptyString(rep.getConfig()));
@@ -1762,44 +1769,27 @@ public class RepresentationToModel {
consentModel.setCreatedDate(consentRep.getCreatedDate());
consentModel.setLastUpdatedDate(consentRep.getLastUpdatedDate());
- if (consentRep.getGrantedRealmRoles() != null) {
- for (String roleName : consentRep.getGrantedRealmRoles()) {
- RoleModel role = newRealm.getRole(roleName);
- if (role == null) {
- throw new RuntimeException("Unable to find realm role referenced in consent mappings of user. Role name: " + roleName);
- }
- consentModel.addGrantedRole(role);
- }
- }
- if (consentRep.getGrantedClientRoles() != null) {
- for (Map.Entry<String, List<String>> entry : consentRep.getGrantedClientRoles().entrySet()) {
- String clientId2 = entry.getKey();
- ClientModel client2 = newRealm.getClientByClientId(clientId2);
- if (client2 == null) {
- throw new RuntimeException("Unable to find client referenced in consent mappings. Client ID: " + clientId2);
- }
- for (String clientRoleName : entry.getValue()) {
- RoleModel clientRole = client2.getRole(clientRoleName);
- if (clientRole == null) {
- throw new RuntimeException("Unable to find client role referenced in consent mappings of user. Role name: " + clientRole + ", Client: " + clientId2);
- }
- consentModel.addGrantedRole(clientRole);
+ if (consentRep.getGrantedClientScopes() != null) {
+ for (String scopeName : consentRep.getGrantedClientScopes()) {
+ ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(newRealm, scopeName);
+ if (clientScope == null) {
+ throw new RuntimeException("Unable to find client scope referenced in consent mappings of user. Client scope name: " + scopeName);
}
+ consentModel.addGrantedClientScope(clientScope);
}
}
- if (consentRep.getGrantedProtocolMappers() != null) {
- for (Map.Entry<String, List<String>> protocolEntry : consentRep.getGrantedProtocolMappers().entrySet()) {
- String protocol = protocolEntry.getKey();
- for (String protocolMapperName : protocolEntry.getValue()) {
- ProtocolMapperModel protocolMapper = client.getProtocolMapperByName(protocol, protocolMapperName);
- if (protocolMapper == null) {
- throw new RuntimeException("Unable to find protocol mapper for protocol " + protocol + ", mapper name " + protocolMapperName);
- }
- consentModel.addGrantedProtocolMapper(protocolMapper);
+ // Backwards compatibility. If user had consent for "offline_access" role, we treat it as he has consent for "offline_access" client scope
+ if (consentRep.getGrantedRealmRoles() != null) {
+ if (consentRep.getGrantedRealmRoles().contains(OAuth2Constants.OFFLINE_ACCESS)) {
+ ClientScopeModel offlineScope = client.getClientScopes(false, true).get(OAuth2Constants.OFFLINE_ACCESS);
+ if (offlineScope == null) {
+ logger.warn("Unable to find offline_access scope referenced in grantedRoles of user");
}
+ consentModel.addGrantedClientScope(offlineScope);
}
}
+
return consentModel;
}
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java
index 4d39f8a..106f073 100755
--- a/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java
@@ -19,6 +19,7 @@ package org.keycloak.protocol;
import org.keycloak.Config;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderEvent;
@@ -41,12 +42,45 @@ public abstract class AbstractLoginProtocolFactory implements LoginProtocolFacto
public void onEvent(ProviderEvent event) {
if (event instanceof RealmModel.ClientCreationEvent) {
ClientModel client = ((RealmModel.ClientCreationEvent)event).getCreatedClient();
+ addDefaultClientScopes(client.getRealm(), client);
addDefaults(client);
}
}
});
}
+
+ @Override
+ public void createDefaultClientScopes(RealmModel newRealm, boolean addScopesToExistingClients) {
+ createDefaultClientScopesImpl(newRealm);
+
+ // Create default client scopes for realm built-in clients too
+ if (addScopesToExistingClients) {
+ for (ClientModel client : newRealm.getClients()) {
+ addDefaultClientScopes(newRealm, client);
+ }
+ }
+ }
+
+ /**
+ * Impl should create default client scopes. This is called usually when new realm is created
+ */
+ protected abstract void createDefaultClientScopesImpl(RealmModel newRealm);
+
+
+ protected void addDefaultClientScopes(RealmModel realm, ClientModel newClient) {
+ for (ClientScopeModel clientScope : realm.getDefaultClientScopes(true)) {
+ if (getId().equals(clientScope.getProtocol())) {
+ newClient.addClientScope(clientScope, true);
+ }
+ }
+ for (ClientScopeModel clientScope : realm.getDefaultClientScopes(false)) {
+ if (getId().equals(clientScope.getProtocol())) {
+ newClient.addClientScope(clientScope, false);
+ }
+ }
+ }
+
protected abstract void addDefaults(ClientModel realm);
@Override
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java
index e367bf2..88542ce 100755
--- a/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java
@@ -20,6 +20,7 @@ package org.keycloak.protocol;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
@@ -67,7 +68,7 @@ public interface LoginProtocol extends Provider {
LoginProtocol setEventBuilder(EventBuilder event);
- Response authenticated(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession);
+ Response authenticated(UserSessionModel userSession, ClientSessionContext clientSessionCtx);
Response sendError(AuthenticationSessionModel authSession, Error error);
diff --git a/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
index 931a00d..fca30e7 100755
--- a/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
+++ b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
@@ -19,14 +19,11 @@ package org.keycloak.protocol;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
-
-import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -38,15 +35,20 @@ public interface LoginProtocolFactory extends ProviderFactory<LoginProtocol> {
*
* @return
*/
- List<ProtocolMapperModel> getBuiltinMappers();
+ Map<String, ProtocolMapperModel> getBuiltinMappers();
+
+
+ Object createProtocolEndpoint(RealmModel realm, EventBuilder event);
+
/**
- * List of mappers, which are added to new clients by default
- * @return
+ * Called when new realm is created
+ *
+ * @param newRealm
+ * @param addScopesToExistingClients If true, then existing realm clients will be updated (created realm default scopes will be added to them)
*/
- List<ProtocolMapperModel> getDefaultBuiltinMappers();
+ void createDefaultClientScopes(RealmModel newRealm, boolean addScopesToExistingClients);
- Object createProtocolEndpoint(RealmModel realm, EventBuilder event);
/**
* Setup default values for new clients. This expects that the representation has already set up the client
@@ -56,11 +58,4 @@ public interface LoginProtocolFactory extends ProviderFactory<LoginProtocol> {
*/
void setupClientDefaults(ClientRepresentation rep, ClientModel newClient);
- /**
- * Setup default values for new templates. This expects that the representation has already set up the template
- *
- * @param clientRep
- * @param newClient
- */
- void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient);
}
diff --git a/server-spi-private/src/main/java/org/keycloak/storage/client/AbstractReadOnlyClientStorageAdapter.java b/server-spi-private/src/main/java/org/keycloak/storage/client/AbstractReadOnlyClientStorageAdapter.java
index d8e6bd4..eb9279f 100644
--- a/server-spi-private/src/main/java/org/keycloak/storage/client/AbstractReadOnlyClientStorageAdapter.java
+++ b/server-spi-private/src/main/java/org/keycloak/storage/client/AbstractReadOnlyClientStorageAdapter.java
@@ -16,7 +16,7 @@
*/
package org.keycloak.storage.client;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -214,27 +214,13 @@ public abstract class AbstractReadOnlyClientStorageAdapter extends AbstractClien
}
@Override
- public void setClientTemplate(ClientTemplateModel template) {
- throw new ReadOnlyException("client is read only for this update");
-
- }
-
- @Override
- public void setUseTemplateScope(boolean flag) {
-
+ public void addClientScope(ClientScopeModel clientScope, boolean defaultScope) {
throw new ReadOnlyException("client is read only for this update");
}
@Override
- public void setUseTemplateMappers(boolean flag) {
+ public void removeClientScope(ClientScopeModel clientScope) {
throw new ReadOnlyException("client is read only for this update");
-
- }
-
- @Override
- public void setUseTemplateConfig(boolean flag) {
- throw new ReadOnlyException("client is read only for this update");
-
}
@Override
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 502ac6d..58a672b 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -19,7 +19,6 @@ package org.keycloak.authentication;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
-import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.authentication.authenticators.client.ClientAuthUtil;
import org.keycloak.common.ClientConnection;
@@ -33,6 +32,7 @@ import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -48,7 +48,6 @@ import org.keycloak.services.ErrorPage;
import org.keycloak.services.ErrorPageException;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.AuthenticationManager;
-import org.keycloak.services.managers.AuthenticationSessionManager;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
@@ -57,7 +56,6 @@ import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.services.util.AuthenticationFlowURLHelper;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.CommonClientSessionModel;
-import org.keycloak.sessions.RootAuthenticationSessionModel;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
@@ -918,18 +916,18 @@ public class AuthenticationProcessor {
}
// May create userSession too
- public AuthenticatedClientSessionModel attachSession() {
- AuthenticatedClientSessionModel clientSession = attachSession(authenticationSession, userSession, session, realm, connection, event);
+ public ClientSessionContext attachSession() {
+ ClientSessionContext clientSessionCtx = attachSession(authenticationSession, userSession, session, realm, connection, event);
if (userSession == null) {
- userSession = clientSession.getUserSession();
+ userSession = clientSessionCtx.getClientSession().getUserSession();
}
- return clientSession;
+ return clientSessionCtx;
}
// May create new userSession too (if userSession argument is null)
- public static AuthenticatedClientSessionModel attachSession(AuthenticationSessionModel authSession, UserSessionModel userSession, KeycloakSession session, RealmModel realm, ClientConnection connection, EventBuilder event) {
+ public static ClientSessionContext attachSession(AuthenticationSessionModel authSession, UserSessionModel userSession, KeycloakSession session, RealmModel realm, ClientConnection connection, EventBuilder event) {
String username = authSession.getAuthenticatedUser().getUsername();
String attemptedUsername = authSession.getAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME);
if (attemptedUsername != null) username = attemptedUsername;
@@ -964,13 +962,13 @@ public class AuthenticationProcessor {
event.detail(Details.REMEMBER_ME, "true");
}
- AuthenticatedClientSessionModel clientSession = TokenManager.attachAuthenticationSession(session, userSession, authSession);
+ ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, userSession, authSession);
event.user(userSession.getUser())
.detail(Details.USERNAME, username)
.session(userSession);
- return clientSession;
+ return clientSessionCtx;
}
public void evaluateRequiredActionTriggers() {
@@ -980,8 +978,8 @@ public class AuthenticationProcessor {
public Response finishAuthentication(LoginProtocol protocol) {
event.success();
RealmModel realm = authenticationSession.getRealm();
- AuthenticatedClientSessionModel clientSession = attachSession();
- return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, userSession,clientSession, request, uriInfo, connection, event, protocol);
+ ClientSessionContext clientSessionCtx = attachSession();
+ return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, userSession, clientSessionCtx, request, uriInfo, connection, event, protocol);
}
@@ -998,7 +996,7 @@ public class AuthenticationProcessor {
protected Response authenticationComplete() {
// attachSession(); // Session will be attached after requiredActions + consents are finished.
- AuthenticationManager.setRolesAndMappersInSession(authenticationSession);
+ AuthenticationManager.setClientScopesInSession(authenticationSession);
String nextRequiredAction = nextRequiredAction();
if (nextRequiredAction != null) {
diff --git a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
index 3e3fa27..7aac436 100644
--- a/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
+++ b/services/src/main/java/org/keycloak/authorization/admin/PolicyEvaluationService.java
@@ -54,6 +54,7 @@ import org.keycloak.authorization.store.StoreFactory;
import org.keycloak.authorization.util.Permissions;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@@ -213,7 +214,6 @@ public class PolicyEvaluationService {
String subject = representation.getUserId();
- AuthenticatedClientSessionModel clientSession = null;
UserSessionModel userSession = null;
if (subject != null) {
UserModel userModel = keycloakSession.users().getUserById(subject, realm);
@@ -234,17 +234,10 @@ public class PolicyEvaluationService {
authSession.setAuthenticatedUser(userModel);
userSession = keycloakSession.sessions().createUserSession(authSession.getParentSession().getId(), realm, userModel, userModel.getUsername(), "127.0.0.1", "passwd", false, null, null);
- AuthenticationManager.setRolesAndMappersInSession(authSession);
- clientSession = TokenManager.attachAuthenticationSession(keycloakSession, userSession, authSession);
+ AuthenticationManager.setClientScopesInSession(authSession);
+ ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(keycloakSession, userSession, authSession);
- Set<RoleModel> requestedRoles = new HashSet<>();
- for (String roleId : clientSession.getRoles()) {
- RoleModel role = realm.getRoleById(roleId);
- if (role != null) {
- requestedRoles.add(role);
- }
- }
- accessToken = new TokenManager().createClientAccessToken(keycloakSession, requestedRoles, realm, clientModel, userModel, userSession, clientSession);
+ accessToken = new TokenManager().createClientAccessToken(keycloakSession, realm, clientModel, userModel, userSession, clientSessionCtx);
}
}
}
diff --git a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
index a0351e1..3b9b3de 100644
--- a/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
+++ b/services/src/main/java/org/keycloak/authorization/authorization/AuthorizationTokenService.java
@@ -61,6 +61,7 @@ import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
@@ -80,6 +81,7 @@ import org.keycloak.services.CorsErrorResponseException;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.Cors;
import org.keycloak.util.JsonSerialization;
+import org.keycloak.services.util.DefaultClientSessionContext;
/**
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
@@ -224,7 +226,10 @@ public class AuthorizationTokenService {
UserSessionModel userSessionModel = keycloakSession.sessions().getUserSession(getRealm(), accessToken.getSessionState());
ClientModel client = getRealm().getClientByClientId(accessToken.getIssuedFor());
AuthenticatedClientSessionModel clientSession = userSessionModel.getAuthenticatedClientSessionByClient(client.getId());
- AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(getRealm(), clientSession.getClient(), event, keycloakSession, userSessionModel, clientSession)
+
+ ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession);
+
+ AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(getRealm(), clientSession.getClient(), event, keycloakSession, userSessionModel, clientSessionCtx)
.generateAccessToken()
.generateRefreshToken();
diff --git a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
index c02ecb5..6393dc8 100644
--- a/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
+++ b/services/src/main/java/org/keycloak/authorization/common/KeycloakIdentity.java
@@ -19,11 +19,14 @@ package org.keycloak.authorization.common;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.keycloak.OAuth2Constants;
import org.keycloak.authorization.attribute.Attributes;
import org.keycloak.authorization.identity.Identity;
import org.keycloak.authorization.util.Tokens;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@@ -34,6 +37,7 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.saml.common.util.StringUtil;
import org.keycloak.services.ErrorResponseException;
+import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.util.JsonSerialization;
import javax.ws.rs.core.Response.Status;
@@ -115,14 +119,9 @@ public class KeycloakIdentity implements Identity {
UserSessionModel userSession = keycloakSession.sessions().getUserSession(realm, token.getSessionState());
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
AuthenticatedClientSessionModel clientSessionModel = userSession.getAuthenticatedClientSessions().get(client.getId());
- Set<RoleModel> requestedRoles = new HashSet<>();
- for (String roleId : clientSessionModel.getRoles()) {
- RoleModel role = realm.getRoleById(roleId);
- if (role != null) {
- requestedRoles.add(role);
- }
- }
- this.accessToken = new TokenManager().createClientAccessToken(keycloakSession, requestedRoles, realm, client, userSession.getUser(), userSession, clientSessionModel);
+
+ ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSessionModel);
+ this.accessToken = new TokenManager().createClientAccessToken(keycloakSession, realm, client, userSession.getUser(), userSession, clientSessionCtx);
}
AccessToken.Access realmAccess = this.accessToken.getRealmAccess();
diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index 169b5bf..5b9fb90 100755
--- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -46,7 +46,7 @@ import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@@ -57,7 +57,7 @@ import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ComponentExportRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
@@ -101,14 +101,24 @@ public class ExportUtils {
// Project/product version
rep.setKeycloakVersion(Version.VERSION);
- // Client Templates
- List<ClientTemplateModel> templates = realm.getClientTemplates();
- List<ClientTemplateRepresentation> templateReps = new ArrayList<>();
- for (ClientTemplateModel app : templates) {
- ClientTemplateRepresentation clientRep = ModelToRepresentation.toRepresentation(app);
- templateReps.add(clientRep);
+ // Client Scopes
+ List<ClientScopeModel> clientScopeModels = realm.getClientScopes();
+ List<ClientScopeRepresentation> clientScopesReps = new ArrayList<>();
+ for (ClientScopeModel app : clientScopeModels) {
+ ClientScopeRepresentation clientRep = ModelToRepresentation.toRepresentation(app);
+ clientScopesReps.add(clientRep);
}
- rep.setClientTemplates(templateReps);
+ rep.setClientScopes(clientScopesReps);
+
+ List<String> defaultClientScopeNames = realm.getDefaultClientScopes(true).stream().map((ClientScopeModel clientScope) -> {
+ return clientScope.getName();
+ }).collect(Collectors.toList());
+ rep.setDefaultDefaultClientScopes(defaultClientScopeNames);
+
+ List<String> optionalClientScopeNames = realm.getDefaultClientScopes(false).stream().map((ClientScopeModel clientScope) -> {
+ return clientScope.getName();
+ }).collect(Collectors.toList());
+ rep.setDefaultOptionalClientScopes(optionalClientScopeNames);
// Clients
List<ClientModel> clients = Collections.emptyList();
@@ -196,14 +206,14 @@ public class ExportUtils {
}
}
- // Scopes of client templates
- for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) {
- Set<RoleModel> clientScopes = clientTemplate.getScopeMappings();
+ // Scopes of client scopes
+ for (ClientScopeModel clientScope : realm.getClientScopes()) {
+ Set<RoleModel> clientScopes = clientScope.getScopeMappings();
ScopeMappingRepresentation scopeMappingRep = null;
for (RoleModel scope : clientScopes) {
if (scope.getContainer() instanceof RealmModel) {
if (scopeMappingRep == null) {
- scopeMappingRep = rep.clientTemplateScopeMapping(clientTemplate.getName());
+ scopeMappingRep = rep.clientScopeScopeMapping(clientScope.getName());
}
scopeMappingRep.role(scope.getName());
} else {
@@ -217,14 +227,14 @@ public class ExportUtils {
ScopeMappingRepresentation currentClientTemplateScope = null;
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
- if (clientTemplate.getName().equals(scopeMapping.getClientTemplate())) {
+ if (clientScope.getName().equals(scopeMapping.getClientScope())) {
currentClientTemplateScope = scopeMapping;
break;
}
}
if (currentClientTemplateScope == null) {
currentClientTemplateScope = new ScopeMappingRepresentation();
- currentClientTemplateScope.setClientTemplate(clientTemplate.getName());
+ currentClientTemplateScope.setClientScope(clientScope.getName());
currentAppScopes.add(currentClientTemplateScope);
}
currentClientTemplateScope.role(scope.getName());
diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java
index 2669c29..0e20559 100755
--- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java
+++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/ApplicationsBean.java
@@ -19,6 +19,7 @@ package org.keycloak.forms.account.freemarker.model;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@@ -60,27 +61,26 @@ public class ApplicationsBean {
if (!AdminPermissions.realms(session, realm, user).isAdmin()) continue;
} else {
- availableRoles = TokenManager.getAccess(null, false, client, user);
- // Don't show applications, which user doesn't have access into (any available roles)
- if (availableRoles.isEmpty()) {
- continue;
- }
+ // Construct scope parameter with all optional scopes to see all potentially available roles
+ Set<ClientScopeModel> allClientScopes = new HashSet<>(client.getClientScopes(true, true).values());
+ allClientScopes.addAll(client.getClientScopes(false, true).values());
+ allClientScopes.add(client);
+
+ availableRoles = TokenManager.getAccess(user, client, allClientScopes);
}
List<RoleModel> realmRolesAvailable = new LinkedList<RoleModel>();
MultivaluedHashMap<String, ClientRoleEntry> resourceRolesAvailable = new MultivaluedHashMap<String, ClientRoleEntry>();
processRoles(availableRoles, realmRolesAvailable, resourceRolesAvailable);
- List<RoleModel> realmRolesGranted = new LinkedList<RoleModel>();
- MultivaluedHashMap<String, ClientRoleEntry> resourceRolesGranted = new MultivaluedHashMap<String, ClientRoleEntry>();
- List<String> claimsGranted = new LinkedList<String>();
+ List<String> clientScopesGranted = new LinkedList<String>();
if (client.isConsentRequired()) {
UserConsentModel consent = session.users().getConsentByClient(realm, user.getId(), client.getId());
if (consent != null) {
- processRoles(consent.getGrantedRoles(), realmRolesGranted, resourceRolesGranted);
- for (ProtocolMapperModel protocolMapper : consent.getGrantedProtocolMappers()) {
- claimsGranted.add(protocolMapper.getConsentText());
+ for (ClientScopeModel clientScope : consent.getGrantedClientScopes()) {
+ String consentText = clientScope.getConsentScreenText();
+ clientScopesGranted.add(consentText);
}
}
}
@@ -90,8 +90,8 @@ public class ApplicationsBean {
additionalGrants.add("${offlineToken}");
}
- ApplicationEntry appEntry = new ApplicationEntry(realmRolesAvailable, resourceRolesAvailable, realmRolesGranted, resourceRolesGranted, client,
- claimsGranted, additionalGrants);
+ ApplicationEntry appEntry = new ApplicationEntry(realmRolesAvailable, resourceRolesAvailable, client,
+ clientScopesGranted, additionalGrants);
applications.add(appEntry);
}
}
@@ -117,21 +117,16 @@ public class ApplicationsBean {
private final List<RoleModel> realmRolesAvailable;
private final MultivaluedHashMap<String, ClientRoleEntry> resourceRolesAvailable;
- private final List<RoleModel> realmRolesGranted;
- private final MultivaluedHashMap<String, ClientRoleEntry> resourceRolesGranted;
private final ClientModel client;
- private final List<String> claimsGranted;
+ private final List<String> clientScopesGranted;
private final List<String> additionalGrants;
public ApplicationEntry(List<RoleModel> realmRolesAvailable, MultivaluedHashMap<String, ClientRoleEntry> resourceRolesAvailable,
- List<RoleModel> realmRolesGranted, MultivaluedHashMap<String, ClientRoleEntry> resourceRolesGranted,
- ClientModel client, List<String> claimsGranted, List<String> additionalGrants) {
+ ClientModel client, List<String> clientScopesGranted, List<String> additionalGrants) {
this.realmRolesAvailable = realmRolesAvailable;
this.resourceRolesAvailable = resourceRolesAvailable;
- this.realmRolesGranted = realmRolesGranted;
- this.resourceRolesGranted = resourceRolesGranted;
this.client = client;
- this.claimsGranted = claimsGranted;
+ this.clientScopesGranted = clientScopesGranted;
this.additionalGrants = additionalGrants;
}
@@ -143,14 +138,10 @@ public class ApplicationsBean {
return resourceRolesAvailable;
}
- public List<RoleModel> getRealmRolesGranted() {
- return realmRolesGranted;
+ public List<String> getClientScopesGranted() {
+ return clientScopesGranted;
}
- public MultivaluedHashMap<String, ClientRoleEntry> getResourceRolesGranted() {
- return resourceRolesGranted;
- }
-
public String getEffectiveUrl() {
String rootUrl = getClient().getRootUrl();
String baseUrl = getClient().getBaseUrl();
@@ -196,10 +187,6 @@ public class ApplicationsBean {
return client;
}
- public List<String> getClaimsGranted() {
- return claimsGranted;
- }
-
public List<String> getAdditionalGrants() {
return additionalGrants;
}
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
index 3d0f52d..35743fa 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -45,7 +45,6 @@ import org.keycloak.theme.BrowserSecurityHeaderSetup;
import org.keycloak.theme.FreeMarkerException;
import org.keycloak.theme.FreeMarkerUtil;
import org.keycloak.theme.Theme;
-import org.keycloak.theme.ThemeProvider;
import org.keycloak.theme.beans.AdvancedMessageFormatterMethod;
import org.keycloak.theme.beans.LocaleBean;
import org.keycloak.theme.beans.MessageBean;
@@ -75,11 +74,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
protected String accessCode;
protected Response.Status status;
protected javax.ws.rs.core.MediaType contentType;
- protected List<RoleModel> realmRolesRequested;
- protected MultivaluedMap<String, RoleModel> resourceRolesRequested;
- protected List<ProtocolMapperModel> protocolMappersRequested;
+ protected List<ClientScopeModel> clientScopesRequested;
protected Map<String, String> httpResponseHeaders = new HashMap<String, String>();
- protected String accessRequestMessage;
protected URI actionUri;
protected String execution;
@@ -202,7 +198,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
break;
case OAUTH_GRANT:
attributes.put("oauth",
- new OAuthGrantBean(accessCode, client, realmRolesRequested, resourceRolesRequested, protocolMappersRequested, this.accessRequestMessage));
+ new OAuthGrantBean(accessCode, client, clientScopesRequested));
attributes.put("advancedMsg", new AdvancedMessageFormatterMethod(locale, messagesBundle));
break;
case CODE:
@@ -613,17 +609,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
}
@Override
- public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested,
- List<ProtocolMapperModel> protocolMappersRequested) {
- this.realmRolesRequested = realmRolesRequested;
- this.resourceRolesRequested = resourceRolesRequested;
- this.protocolMappersRequested = protocolMappersRequested;
- return this;
- }
-
- @Override
- public LoginFormsProvider setAccessRequest(String accessRequestMessage) {
- this.accessRequestMessage = accessRequestMessage;
+ public LoginFormsProvider setAccessRequest(List<ClientScopeModel> clientScopesRequested) {
+ this.clientScopesRequested = clientScopesRequested;
return this;
}
diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/OAuthGrantBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/OAuthGrantBean.java
index bf424cb..97d8e44 100755
--- a/services/src/main/java/org/keycloak/forms/login/freemarker/model/OAuthGrantBean.java
+++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/OAuthGrantBean.java
@@ -16,12 +16,9 @@
*/
package org.keycloak.forms.login.freemarker.model;
-import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.models.RoleModel;
+import org.keycloak.models.ClientScopeModel;
-import javax.ws.rs.core.MultivaluedMap;
import java.util.LinkedList;
import java.util.List;
@@ -30,93 +27,45 @@ import java.util.List;
*/
public class OAuthGrantBean {
- private final String accessRequestMessage;
- private List<RoleModel> realmRolesRequested;
- private MultivaluedMap<String, ClientRoleEntry> resourceRolesRequested;
+ private List<ClientScopeEntry> clientScopesRequested = new LinkedList<>();
private String code;
private ClientModel client;
- private List<String> claimsRequested;
- public OAuthGrantBean(String code, ClientModel client, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested,
- List<ProtocolMapperModel> protocolMappersRequested, String accessRequestMessage) {
+ public OAuthGrantBean(String code, ClientModel client, List<ClientScopeModel> clientScopesRequested) {
this.code = code;
this.client = client;
- this.realmRolesRequested = realmRolesRequested;
- if (resourceRolesRequested != null) {
- this.resourceRolesRequested = new MultivaluedMapImpl<String, ClientRoleEntry>();
- for (List<RoleModel> clientRoles : resourceRolesRequested.values()) {
- for (RoleModel role : clientRoles) {
- ClientModel currentClient = (ClientModel) role.getContainer();
- ClientRoleEntry roleEntry = new ClientRoleEntry(currentClient.getClientId(), currentClient.getName(), role.getName(), role.getDescription());
- this.resourceRolesRequested.add(currentClient.getClientId(), roleEntry);
- }
- }
- }
-
- this.accessRequestMessage = accessRequestMessage;
- List<String> claims = new LinkedList<String>();
- if (protocolMappersRequested != null) {
- for (ProtocolMapperModel model : protocolMappersRequested) {
- claims.add(model.getConsentText());
- }
+ for (ClientScopeModel clientScope : clientScopesRequested) {
+ this.clientScopesRequested.add(new ClientScopeEntry(clientScope.getConsentScreenText()));
}
- if (claims.size() > 0) this.claimsRequested = claims;
}
public String getCode() {
return code;
}
- public MultivaluedMap<String, ClientRoleEntry> getResourceRolesRequested() {
- return resourceRolesRequested;
- }
-
- public List<RoleModel> getRealmRolesRequested() {
- return realmRolesRequested;
- }
public String getClient() {
return client.getClientId();
}
- public List<String> getClaimsRequested() {
- return claimsRequested;
- }
- public String getAccessRequestMessage() {
- return this.accessRequestMessage;
+ public List<ClientScopeEntry> getClientScopesRequested() {
+ return clientScopesRequested;
}
- // Same class used in ConsentBean in account as well. Maybe should be merged into common-freemarker...
- public static class ClientRoleEntry {
- private final String clientId;
- private final String clientName;
- private final String roleName;
- private final String roleDescription;
+ // Converting ClientScopeModel due the freemarker limitations. It's not able to read "getConsentScreenText" default method defined on interface
+ public static class ClientScopeEntry {
- public ClientRoleEntry(String clientId, String clientName, String roleName, String roleDescription) {
- this.clientId = clientId;
- this.clientName = clientName;
- this.roleName = roleName;
- this.roleDescription = roleDescription;
- }
-
- public String getClientId() {
- return clientId;
- }
-
- public String getClientName() {
- return clientName;
- }
+ private final String consentScreenText;
- public String getRoleName() {
- return roleName;
+ private ClientScopeEntry(String consentScreenText) {
+ this.consentScreenText = consentScreenText;
}
- public String getRoleDescription() {
- return roleDescription;
+ public String getConsentScreenText() {
+ return consentScreenText;
}
}
}
diff --git a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
index da2e376..518d487 100755
--- a/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
+++ b/services/src/main/java/org/keycloak/protocol/AuthorizationEndpointBase.java
@@ -121,7 +121,7 @@ public abstract class AuthorizationEndpointBase {
return response;
}
- AuthenticationManager.setRolesAndMappersInSession(authSession);
+ AuthenticationManager.setClientScopesInSession(authSession);
if (processor.nextRequiredAction() != null) {
Response response = protocol.sendError(authSession, Error.PASSIVE_INTERACTION_REQUIRED);
diff --git a/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2Protocol.java b/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2Protocol.java
index 3a7a324..fc42d8d 100644
--- a/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2Protocol.java
+++ b/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2Protocol.java
@@ -7,6 +7,7 @@ import org.keycloak.events.EventType;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@@ -89,9 +90,11 @@ public class DockerAuthV2Protocol implements LoginProtocol {
}
@Override
- public Response authenticated(final UserSessionModel userSession, final AuthenticatedClientSessionModel clientSession) {
+ public Response authenticated(final UserSessionModel userSession, final ClientSessionContext clientSessionCtx) {
// First, create a base response token with realm + user values populated
+ final AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
final ClientModel client = clientSession.getClient();
+
DockerResponseToken responseToken = new DockerResponseToken()
.id(KeycloakModelUtils.generateId())
.type(TokenUtil.TOKEN_TYPE_BEARER)
@@ -107,8 +110,7 @@ public class DockerAuthV2Protocol implements LoginProtocol {
.expiration(responseToken.getIssuedAt() + accessTokenLifespan);
// Next, allow mappers to decorate the token to add/remove scopes as appropriate
- final ClientSessionCode<AuthenticatedClientSessionModel> accessCode = new ClientSessionCode<>(session, realm, clientSession);
- final Set<ProtocolMapperModel> mappings = accessCode.getRequestedProtocolMappers();
+ final Set<ProtocolMapperModel> mappings = clientSessionCtx.getProtocolMappers();
for (final ProtocolMapperModel mapping : mappings) {
final ProtocolMapper mapper = (ProtocolMapper) session.getKeycloakSessionFactory().getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
if (mapper instanceof DockerAuthV2AttributeMapper) {
diff --git a/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2ProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2ProtocolFactory.java
index be4c6c0..f721760 100644
--- a/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2ProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/docker/DockerAuthV2ProtocolFactory.java
@@ -3,7 +3,7 @@ package org.keycloak.protocol.docker;
import org.keycloak.common.Profile;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -12,15 +12,17 @@ import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.docker.mapper.AllowAllDockerProtocolMapper;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class DockerAuthV2ProtocolFactory extends AbstractLoginProtocolFactory implements EnvironmentDependentProviderFactory {
- static List<ProtocolMapperModel> builtins = new ArrayList<>();
+ static Map<String, ProtocolMapperModel> builtins = new HashMap<>();
static List<ProtocolMapperModel> defaultBuiltins = new ArrayList<>();
static {
@@ -28,25 +30,24 @@ public class DockerAuthV2ProtocolFactory extends AbstractLoginProtocolFactory im
addAllRequestedScopeMapper.setName(AllowAllDockerProtocolMapper.PROVIDER_ID);
addAllRequestedScopeMapper.setProtocolMapper(AllowAllDockerProtocolMapper.PROVIDER_ID);
addAllRequestedScopeMapper.setProtocol(DockerAuthV2Protocol.LOGIN_PROTOCOL);
- addAllRequestedScopeMapper.setConsentRequired(false);
addAllRequestedScopeMapper.setConfig(Collections.EMPTY_MAP);
- builtins.add(addAllRequestedScopeMapper);
+ builtins.put(AllowAllDockerProtocolMapper.PROVIDER_ID, addAllRequestedScopeMapper);
defaultBuiltins.add(addAllRequestedScopeMapper);
}
@Override
- protected void addDefaults(final ClientModel client) {
- defaultBuiltins.forEach(builtinMapper -> client.addProtocolMapper(builtinMapper));
+ protected void createDefaultClientScopesImpl(RealmModel newRealm) {
+ // no-op
}
@Override
- public List<ProtocolMapperModel> getBuiltinMappers() {
- return builtins;
+ protected void addDefaults(final ClientModel client) {
+ defaultBuiltins.forEach(builtinMapper -> client.addProtocolMapper(builtinMapper));
}
@Override
- public List<ProtocolMapperModel> getDefaultBuiltinMappers() {
- return defaultBuiltins;
+ public Map<String, ProtocolMapperModel> getBuiltinMappers() {
+ return builtins;
}
@Override
@@ -59,10 +60,6 @@ public class DockerAuthV2ProtocolFactory extends AbstractLoginProtocolFactory im
// no-op
}
- @Override
- public void setupTemplateDefaults(final ClientTemplateRepresentation clientRep, final ClientTemplateModel newClient) {
- // no-op
- }
@Override
public LoginProtocol create(final KeycloakSession session) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 981f316..ad92195 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -49,6 +49,8 @@ import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
@@ -81,6 +83,7 @@ import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.services.resources.admin.AdminAuth;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.services.util.MtlsHoKTokenUtil;
+import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.validation.Validation;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;
@@ -399,9 +402,20 @@ public class TokenEndpoint {
updateClientSession(clientSession);
updateUserSessionFromClientAuth(userSession);
- AccessToken token = tokenManager.createClientAccessToken(session, parseResult.getCode().getRequestedRoles(), realm, client, user, userSession, clientSession);
+ // Compute client scopes again from scope parameter. Check if user still has them granted
+ // (but in code-to-token request, it could just theoretically happen that they are not available)
+ String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
+ Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client);
+ if (!TokenManager.verifyConsentStillAvailable(session, user, client, clientScopes)) {
+ event.error(Errors.NOT_ALLOWED);
+ throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user", Response.Status.BAD_REQUEST);
+ }
+
+ ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndClientScopes(clientSession, clientScopes);
+
+ AccessToken token = tokenManager.createClientAccessToken(session, realm, client, user, userSession, clientSessionCtx);
- TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession)
+ TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx)
.accessToken(token)
.generateRefreshToken();
@@ -418,7 +432,6 @@ public class TokenEndpoint {
}
}
- String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
if (TokenUtil.isOIDCRequest(scopeParam)) {
responseBuilder.generateIDToken();
}
@@ -544,17 +557,17 @@ public class TokenEndpoint {
}
- AuthenticationManager.setRolesAndMappersInSession(authSession);
+ AuthenticationManager.setClientScopesInSession(authSession);
- AuthenticatedClientSessionModel clientSession = processor.attachSession();
+ ClientSessionContext clientSessionCtx = processor.attachSession();
UserSessionModel userSession = processor.getUserSession();
updateUserSessionFromClientAuth(userSession);
- TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession)
+ TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx)
.generateAccessToken()
.generateRefreshToken();
- String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
+ String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
if (TokenUtil.isOIDCRequest(scopeParam)) {
responseBuilder.generateIDToken();
}
@@ -613,8 +626,8 @@ public class TokenEndpoint {
clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
event.session(userSession);
- AuthenticationManager.setRolesAndMappersInSession(authSession);
- AuthenticatedClientSessionModel clientSession = TokenManager.attachAuthenticationSession(session, userSession, authSession);
+ AuthenticationManager.setClientScopesInSession(authSession);
+ ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, userSession, authSession);
// Notes about client details
userSession.setNote(ServiceAccountConstants.CLIENT_ID, client.getClientId());
@@ -623,11 +636,11 @@ public class TokenEndpoint {
updateUserSessionFromClientAuth(userSession);
- TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession)
+ TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSessionCtx)
.generateAccessToken()
.generateRefreshToken();
- String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
+ String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
if (TokenUtil.isOIDCRequest(scopeParam)) {
responseBuilder.generateIDToken();
}
@@ -830,12 +843,12 @@ public class TokenEndpoint {
event.session(targetUserSession);
- AuthenticationManager.setRolesAndMappersInSession(authSession);
- AuthenticatedClientSessionModel clientSession = TokenManager.attachAuthenticationSession(this.session, targetUserSession, authSession);
+ AuthenticationManager.setClientScopesInSession(authSession);
+ ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(this.session, targetUserSession, authSession);
updateUserSessionFromClientAuth(targetUserSession);
- TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, this.session, targetUserSession, clientSession)
+ TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, targetClient, event, this.session, targetUserSession, clientSessionCtx)
.generateAccessToken();
responseBuilder.getAccessToken().issuedFor(client.getClientId());
@@ -844,7 +857,7 @@ public class TokenEndpoint {
responseBuilder.getRefreshToken().issuedFor(client.getClientId());
}
- String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
+ String scopeParam = clientSessionCtx.getClientSession().getNote(OAuth2Constants.SCOPE);
if (TokenUtil.isOIDCRequest(scopeParam)) {
responseBuilder.generateIDToken();
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
index 3666ec8..4063667 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/UserInfoEndpoint.java
@@ -31,6 +31,7 @@ import org.keycloak.jose.jws.Algorithm;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
@@ -45,6 +46,7 @@ import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.MtlsHoKTokenUtil;
+import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.utils.MediaType;
import javax.ws.rs.GET;
@@ -58,6 +60,7 @@ import javax.ws.rs.core.UriInfo;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
/**
* @author pedroigor
@@ -162,6 +165,9 @@ public class UserInfoEndpoint {
throw new ErrorResponseException(OAuthErrorException.INVALID_REQUEST, "User not found", Response.Status.BAD_REQUEST);
}
+ event.user(userModel)
+ .detail(Details.USERNAME, userModel.getUsername());
+
// KEYCLOAK-6771 Certificate Bound Token
// https://tools.ietf.org/html/draft-ietf-oauth-mtls-08#section-3
if (OIDCAdvancedConfigWrapper.fromClientModel(clientModel).isUseMtlsHokToken()) {
@@ -171,15 +177,14 @@ public class UserInfoEndpoint {
}
}
- event.user(userModel)
- .detail(Details.USERNAME, userModel.getUsername());
-
-
// Existence of authenticatedClientSession for our client already handled before
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(clientModel.getId());
+ // Retrieve by latest scope parameter
+ ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession);
+
AccessToken userInfo = new AccessToken();
- tokenManager.transformUserInfoAccessToken(session, userInfo, realm, clientModel, userModel, userSession, clientSession);
+ tokenManager.transformUserInfoAccessToken(session, userInfo, userSession, clientSessionCtx);
Map<String, Object> claims = new HashMap<String, Object>();
claims.put("sub", userModel.getId());
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java
index 374581d..3f124e2 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AbstractPairwiseSubMapper.java
@@ -51,7 +51,7 @@ public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapp
*
* @param session
* @param realm
- * @param mapperContainer client or clientTemplate
+ * @param mapperContainer client or clientScope
* @param mapperModel
* @throws ProtocolMapperConfigException if configuration provided in mapperModel is not valid
*/
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
index 2f40c21..df56bb5 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
@@ -69,20 +69,19 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
public static final String PROVIDER_ID = "oidc-address-mapper";
public static ProtocolMapperModel createAddressMapper() {
- return createAddressMapper(true, true);
+ return createAddressMapper(true, true, true);
}
- public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken) {
+ public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) {
Map<String, String> config;
ProtocolMapperModel address = new ProtocolMapperModel();
address.setName("address");
address.setProtocolMapper(PROVIDER_ID);
address.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- address.setConsentRequired(true);
- address.setConsentText("${address}");
config = new HashMap<String, String>();
- config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(idToken));
- config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(accessToken));
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(accessToken));
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(idToken));
+ config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.toString(userInfo));
config.put(getModelPropertyName(STREET), STREET);
config.put(getModelPropertyName(AddressClaimSet.LOCALITY), AddressClaimSet.LOCALITY);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
index c156092..a196641 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/FullNameMapper.java
@@ -83,18 +83,15 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
}
}
- public static ProtocolMapperModel create(String name,
- boolean consentRequired, String consentText,
- boolean accessToken, boolean idToken) {
+ public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(consentRequired);
- mapper.setConsentText(consentText);
Map<String, String> config = new HashMap<String, String>();
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
+ if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
mapper.setConfig(config);
return mapper;
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java
index b733f5c..6ffd976 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/GroupMembershipMapper.java
@@ -116,8 +116,6 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(consentRequired);
- mapper.setConsentText(consentText);
Map<String, String> config = new HashMap<String, String>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
index 8d48ccf..99f7a1f 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/HardcodedClaim.java
@@ -91,14 +91,11 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
public static ProtocolMapperModel create(String name,
String hardcodedName,
String hardcodedValue, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(consentRequired);
- mapper.setConsentText(consentText);
Map<String, String> config = new HashMap<String, String>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, hardcodedName);
config.put(CLAIM_VALUE, hardcodedValue);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
index 560a490..0365bc3 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
@@ -172,24 +172,20 @@ public class OIDCAttributeMapperHelper {
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken,
String mapperId) {
- return createClaimMapper(name, userAttribute,tokenClaimName, claimType, consentRequired, consentText, accessToken, idToken, true, mapperId);
+ return createClaimMapper(name, userAttribute,tokenClaimName, claimType, accessToken, idToken, true, mapperId);
}
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken, boolean userinfo,
String mapperId) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(mapperId);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(consentRequired);
- mapper.setConsentText(consentText);
Map<String, String> config = new HashMap<String, String>();
config.put(ProtocolMapperUtils.USER_ATTRIBUTE, userAttribute);
config.put(TOKEN_CLAIM_NAME, tokenClaimName);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/ScriptBasedOIDCProtocolMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/ScriptBasedOIDCProtocolMapper.java
index a00ebb1..72e833b 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/ScriptBasedOIDCProtocolMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/ScriptBasedOIDCProtocolMapper.java
@@ -169,11 +169,9 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
public static ProtocolMapperModel create(String name,
String userAttribute,
String tokenClaimName, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken, String script, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType,
- consentRequired, consentText,
accessToken, idToken,
PROVIDER_ID);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java
index f69f2b1..2b0d540 100644
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/SHA256PairwiseSubMapper.java
@@ -36,7 +36,6 @@ public class SHA256PairwiseSubMapper extends AbstractPairwiseSubMapper {
pairwise.setName("pairwise subject identifier");
pairwise.setProtocolMapper(new SHA256PairwiseSubMapper().getId());
pairwise.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- pairwise.setConsentRequired(false);
config = new HashMap<>();
config.put(PairwiseSubMapperHelper.SECTOR_IDENTIFIER_URI, sectorIdentifierUri);
if (salt == null) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
index 9b2cf0f..12a1c1c 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserAttributeMapper.java
@@ -98,11 +98,9 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken, boolean multivalued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType,
- consentRequired, consentText,
accessToken, idToken,
PROVIDER_ID);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java
index 7ce5c35..db064fa 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserClientRoleMappingMapper.java
@@ -17,13 +17,16 @@
package org.keycloak.protocol.oidc.mappers;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapperUtils;
+import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.IDToken;
@@ -116,10 +119,7 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
return RoleModel::isClientRole;
}
- ClientTemplateModel template = client.getClientTemplate();
- boolean useTemplateScope = template != null && client.useTemplateScope();
- boolean fullScopeAllowed = (useTemplateScope && template.isFullScopeAllowed()) || client.isFullScopeAllowed();
-
+ boolean fullScopeAllowed = client.isFullScopeAllowed();
Set<RoleModel> clientRoleMappings = client.getRoles();
if (fullScopeAllowed) {
return clientRoleMappings::contains;
@@ -127,16 +127,16 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
Set<RoleModel> scopeMappings = new HashSet<>();
- if (useTemplateScope) {
- Set<RoleModel> templateScopeMappings = template.getScopeMappings();
- if (templateScopeMappings != null) {
- scopeMappings.addAll(templateScopeMappings);
- }
+ // Add scope mappings of current client + all clientScopes of this client (including optional scopes if scope parameter matches)
+ String scopeParam = null;
+ AuthenticatedClientSessionModel authClientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
+ if (authClientSession != null) {
+ scopeParam = authClientSession.getNote(OAuth2Constants.SCOPE);
}
- Set<RoleModel> clientScopeMappings = client.getScopeMappings();
- if (clientScopeMappings != null) {
- scopeMappings.addAll(clientScopeMappings);
+ Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client);
+ for (ClientScopeModel clientScope : clientScopes) {
+ scopeMappings.addAll(clientScope.getScopeMappings());
}
return role -> clientRoleMappings.contains(role) && scopeMappings.contains(role);
@@ -155,10 +155,9 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
- tokenClaimName, "String",
- true, name,
- accessToken, idToken,
- PROVIDER_ID);
+ tokenClaimName, "String",
+ accessToken, idToken,
+ PROVIDER_ID);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_CLIENT_ROLE_MAPPING_CLIENT_ID, clientId);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
index 2fc84ff..62b5c4a 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserPropertyMapper.java
@@ -87,11 +87,9 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken) {
return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType,
- consentRequired, consentText,
accessToken, idToken,
PROVIDER_ID);
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java
index 3b7ffe5..59eaf4b 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserRealmRoleMappingMapper.java
@@ -100,7 +100,6 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
String tokenClaimName, boolean accessToken, boolean idToken, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String",
- true, name,
accessToken, idToken,
PROVIDER_ID);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
index aadee6c..80cbf08 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/UserSessionNoteMapper.java
@@ -87,14 +87,11 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
public static ProtocolMapperModel createClaimMapper(String name,
String userSessionNote,
String tokenClaimName, String jsonType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(consentRequired);
- mapper.setConsentText(consentText);
Map<String, String> config = new HashMap<String, String>();
config.put(ProtocolMapperUtils.USER_SESSION_NOTE, userSessionNote);
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
index 148d840..9480651 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocol.java
@@ -28,6 +28,7 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
@@ -175,7 +176,8 @@ public class OIDCLoginProtocol implements LoginProtocol {
@Override
- public Response authenticated(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
+ public Response authenticated(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
+ AuthenticatedClientSessionModel clientSession= clientSessionCtx.getClientSession();
ClientSessionCode<AuthenticatedClientSessionModel> accessCode = new ClientSessionCode<>(session, realm, clientSession);
String responseTypeParam = clientSession.getNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM);
@@ -204,7 +206,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
// Implicit or hybrid flow
if (responseType.isImplicitOrHybridFlow()) {
TokenManager tokenManager = new TokenManager();
- TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, clientSession.getClient(), event, session, userSession, clientSession)
+ TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, clientSession.getClient(), event, session, userSession, clientSessionCtx)
.generateAccessToken();
if (responseType.hasResponseType(OIDCResponseType.ID_TOKEN)) {
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
index 03328a0..ca4ddc4 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -17,14 +17,19 @@
package org.keycloak.protocol.oidc;
import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.common.util.UriUtils;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.DefaultClientScopes;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.AbstractLoginProtocolFactory;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.oidc.mappers.AddressMapper;
@@ -33,13 +38,16 @@ import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
+import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.services.ServicesLogger;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -56,15 +64,26 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
public static final String EMAIL_VERIFIED = "email verified";
public static final String GIVEN_NAME = "given name";
public static final String FAMILY_NAME = "family name";
+ public static final String MIDDLE_NAME = "middle name";
+ public static final String NICKNAME = "nickname";
+ public static final String PROFILE_CLAIM = "profile";
+ public static final String PICTURE = "picture";
+ public static final String WEBSITE = "website";
+ public static final String GENDER = "gender";
+ public static final String BIRTHDATE = "birthdate";
+ public static final String ZONEINFO = "zoneinfo";
+ public static final String UPDATED_AT = "updated at";
public static final String FULL_NAME = "full name";
public static final String LOCALE = "locale";
- public static final String USERNAME_CONSENT_TEXT = "${username}";
- public static final String EMAIL_CONSENT_TEXT = "${email}";
- public static final String EMAIL_VERIFIED_CONSENT_TEXT = "${emailVerified}";
- public static final String GIVEN_NAME_CONSENT_TEXT = "${givenName}";
- public static final String FAMILY_NAME_CONSENT_TEXT = "${familyName}";
- public static final String FULL_NAME_CONSENT_TEXT = "${fullName}";
- public static final String LOCALE_CONSENT_TEXT = "${locale}";
+ public static final String ADDRESS = "address";
+ public static final String PHONE_NUMBER = "phone number";
+ public static final String PHONE_NUMBER_VERIFIED = "phone number verified";
+
+ public static final String PROFILE_SCOPE_CONSENT_TEXT = "${profileScopeConsentText}";
+ public static final String EMAIL_SCOPE_CONSENT_TEXT = "${emailScopeConsentText}";
+ public static final String ADDRESS_SCOPE_CONSENT_TEXT = "${addressScopeConsentText}";
+ public static final String PHONE_SCOPE_CONSENT_TEXT = "${phoneScopeConsentText}";
+ public static final String OFFLINE_ACCESS_SCOPE_CONSENT_TEXT = Constants.OFFLINE_ACCESS_SCOPE_CONSENT_TEXT;
@Override
@@ -73,89 +92,142 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
}
@Override
- public List<ProtocolMapperModel> getBuiltinMappers() {
+ public Map<String, ProtocolMapperModel> getBuiltinMappers() {
return builtins;
}
- @Override
- public List<ProtocolMapperModel> getDefaultBuiltinMappers() {
- return defaultBuiltins;
- }
-
- static List<ProtocolMapperModel> builtins = new ArrayList<>();
- static List<ProtocolMapperModel> defaultBuiltins = new ArrayList<>();
+ static Map<String, ProtocolMapperModel> builtins = new HashMap<>();
static {
-
- ProtocolMapperModel model;
+ ProtocolMapperModel model;
model = UserPropertyMapper.createClaimMapper(USERNAME,
"username",
"preferred_username", "String",
- true, USERNAME_CONSENT_TEXT,
true, true);
- builtins.add(model);
- defaultBuiltins.add(model);
+ builtins.put(USERNAME, model);
+
model = UserPropertyMapper.createClaimMapper(EMAIL,
"email",
"email", "String",
- true, EMAIL_CONSENT_TEXT,
true, true);
- builtins.add(model);
- defaultBuiltins.add(model);
+ builtins.put(EMAIL, model);
+
model = UserPropertyMapper.createClaimMapper(GIVEN_NAME,
"firstName",
"given_name", "String",
- true, GIVEN_NAME_CONSENT_TEXT,
true, true);
- builtins.add(model);
- defaultBuiltins.add(model);
+ builtins.put(GIVEN_NAME, model);
+
model = UserPropertyMapper.createClaimMapper(FAMILY_NAME,
"lastName",
"family_name", "String",
- true, FAMILY_NAME_CONSENT_TEXT,
true, true);
- builtins.add(model);
- defaultBuiltins.add(model);
+ builtins.put(FAMILY_NAME, model);
+
+ createUserAttributeMapper(MIDDLE_NAME, "middleName", IDToken.MIDDLE_NAME, "String");
+ createUserAttributeMapper(NICKNAME, "nickname", IDToken.NICKNAME, "String");
+ createUserAttributeMapper(PROFILE_CLAIM, "profile", IDToken.PROFILE, "String");
+ createUserAttributeMapper(PICTURE, "picture", IDToken.PICTURE, "String");
+ createUserAttributeMapper(WEBSITE, "website", IDToken.WEBSITE, "String");
+ createUserAttributeMapper(GENDER, "gender", IDToken.GENDER, "String");
+ createUserAttributeMapper(BIRTHDATE, "birthdate", IDToken.BIRTHDATE, "String");
+ createUserAttributeMapper(ZONEINFO, "zoneinfo", IDToken.ZONEINFO, "String");
+ createUserAttributeMapper(UPDATED_AT, "updatedAt", IDToken.UPDATED_AT, "String");
+ createUserAttributeMapper(LOCALE, "locale", IDToken.LOCALE, "String");
+
+ createUserAttributeMapper(PHONE_NUMBER, "phoneNumber", IDToken.PHONE_NUMBER, "String");
+ createUserAttributeMapper(PHONE_NUMBER_VERIFIED, "phoneNumberVerified", IDToken.PHONE_NUMBER_VERIFIED, "boolean");
+
model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
"emailVerified",
"email_verified", "boolean",
- false, EMAIL_VERIFIED_CONSENT_TEXT,
true, true);
- builtins.add(model);
- model = UserAttributeMapper.createClaimMapper(LOCALE,
- "locale",
- "locale", "String",
- false, LOCALE_CONSENT_TEXT,
- true, true, false);
- builtins.add(model);
-
- ProtocolMapperModel fullName = new ProtocolMapperModel();
- fullName.setName(FULL_NAME);
- fullName.setProtocolMapper(FullNameMapper.PROVIDER_ID);
- fullName.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- fullName.setConsentRequired(true);
- fullName.setConsentText(FULL_NAME_CONSENT_TEXT);
- Map<String, String> config = new HashMap<String, String>();
- config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
- config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
- fullName.setConfig(config);
- builtins.add(fullName);
- defaultBuiltins.add(fullName);
+ builtins.put(EMAIL_VERIFIED, model);
+
+ ProtocolMapperModel fullName = FullNameMapper.create(FULL_NAME, true, true, true);
+ builtins.put(FULL_NAME, fullName);
ProtocolMapperModel address = AddressMapper.createAddressMapper();
- builtins.add(address);
+ builtins.put(ADDRESS, address);
model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
- true, "${gssDelegationCredential}",
true, false);
- builtins.add(model);
+ builtins.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, model);
+ }
+
+ private static void createUserAttributeMapper(String name, String attrName, String claimName, String type) {
+ ProtocolMapperModel model = UserAttributeMapper.createClaimMapper(name,
+ attrName,
+ claimName, type,
+ true, true, false);
+ builtins.put(name, model);
+ }
+
+ @Override
+ protected void createDefaultClientScopesImpl(RealmModel newRealm) {
+ //name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
+ ClientScopeModel profileScope = newRealm.addClientScope(OAuth2Constants.SCOPE_PROFILE);
+ profileScope.setDescription("OpenID Connect built-in scope: profile");
+ profileScope.setDisplayOnConsentScreen(true);
+ profileScope.setConsentScreenText(PROFILE_SCOPE_CONSENT_TEXT);
+ profileScope.setProtocol(getId());
+ profileScope.addProtocolMapper(builtins.get(FULL_NAME));
+ profileScope.addProtocolMapper(builtins.get(FAMILY_NAME));
+ profileScope.addProtocolMapper(builtins.get(GIVEN_NAME));
+ profileScope.addProtocolMapper(builtins.get(MIDDLE_NAME));
+ profileScope.addProtocolMapper(builtins.get(NICKNAME));
+ profileScope.addProtocolMapper(builtins.get(USERNAME));
+ profileScope.addProtocolMapper(builtins.get(PROFILE_CLAIM));
+ profileScope.addProtocolMapper(builtins.get(PICTURE));
+ profileScope.addProtocolMapper(builtins.get(WEBSITE));
+ profileScope.addProtocolMapper(builtins.get(GENDER));
+ profileScope.addProtocolMapper(builtins.get(BIRTHDATE));
+ profileScope.addProtocolMapper(builtins.get(ZONEINFO));
+ profileScope.addProtocolMapper(builtins.get(LOCALE));
+ profileScope.addProtocolMapper(builtins.get(UPDATED_AT));
+
+ ClientScopeModel emailScope = newRealm.addClientScope(OAuth2Constants.SCOPE_EMAIL);
+ emailScope.setDescription("OpenID Connect built-in scope: email");
+ emailScope.setDisplayOnConsentScreen(true);
+ emailScope.setConsentScreenText(EMAIL_SCOPE_CONSENT_TEXT);
+ emailScope.setProtocol(getId());
+ emailScope.addProtocolMapper(builtins.get(EMAIL));
+ emailScope.addProtocolMapper(builtins.get(EMAIL_VERIFIED));
+
+ ClientScopeModel addressScope = newRealm.addClientScope(OAuth2Constants.SCOPE_ADDRESS);
+ addressScope.setDescription("OpenID Connect built-in scope: address");
+ addressScope.setDisplayOnConsentScreen(true);
+ addressScope.setConsentScreenText(ADDRESS_SCOPE_CONSENT_TEXT);
+ addressScope.setProtocol(getId());
+ addressScope.addProtocolMapper(builtins.get(ADDRESS));
+
+ ClientScopeModel phoneScope = newRealm.addClientScope(OAuth2Constants.SCOPE_PHONE);
+ phoneScope.setDescription("OpenID Connect built-in scope: phone");
+ phoneScope.setDisplayOnConsentScreen(true);
+ phoneScope.setConsentScreenText(PHONE_SCOPE_CONSENT_TEXT);
+ phoneScope.setProtocol(getId());
+ phoneScope.addProtocolMapper(builtins.get(PHONE_NUMBER));
+ phoneScope.addProtocolMapper(builtins.get(PHONE_NUMBER_VERIFIED));
+
+ // 'profile' and 'email' will be default scopes for now. 'address' and 'phone' will be optional scopes
+ newRealm.addDefaultClientScope(profileScope, true);
+ newRealm.addDefaultClientScope(emailScope, true);
+ newRealm.addDefaultClientScope(addressScope, false);
+ newRealm.addDefaultClientScope(phoneScope, false);
+
+ RoleModel offlineRole = newRealm.getRole(OAuth2Constants.OFFLINE_ACCESS);
+ if (offlineRole != null) {
+ ClientScopeModel offlineAccessScope = KeycloakModelUtils.getClientScopeByName(newRealm, OAuth2Constants.OFFLINE_ACCESS);
+ if (offlineAccessScope == null) {
+ DefaultClientScopes.createOfflineAccessClientScope(newRealm, offlineRole);
+ }
+ }
}
@Override
protected void addDefaults(ClientModel client) {
- for (ProtocolMapperModel model : defaultBuiltins) client.addProtocolMapper(model);
}
@Override
@@ -208,8 +280,4 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
if (rep.isFrontchannelLogout() == null) newClient.setFrontchannelLogout(false);
}
- @Override
- public void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient) {
-
- }
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
index 9f4cccd..6897428 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
@@ -21,6 +21,7 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.ClientAuthenticator;
import org.keycloak.authentication.ClientAuthenticatorFactory;
import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.protocol.oidc.endpoints.TokenEndpoint;
@@ -66,9 +67,6 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
public static final List<String> DEFAULT_CLAIM_TYPES_SUPPORTED= list("normal");
- // TODO: Add more of OIDC scopes
- public static final List<String> SCOPES_SUPPORTED= list(OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS);
-
// KEYCLOAK-7451 OAuth Authorization Server Metadata for Proof Key for Code Exchange
public static final List<String> DEFAULT_CODE_CHALLENGE_METHODS_SUPPORTED = list(OAuth2Constants.PKCE_METHOD_PLAIN, OAuth2Constants.PKCE_METHOD_S256);
@@ -111,7 +109,15 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
config.setClaimTypesSupported(DEFAULT_CLAIM_TYPES_SUPPORTED);
config.setClaimsParameterSupported(false);
- config.setScopesSupported(SCOPES_SUPPORTED);
+ List<ClientScopeModel> scopes = realm.getClientScopes();
+ List<String> scopeNames = new LinkedList<>();
+ for (ClientScopeModel clientScope : scopes) {
+ if (clientScope.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
+ scopeNames.add(clientScope.getName());
+ }
+ }
+ scopeNames.add(0, OAuth2Constants.SCOPE_OPENID);
+ config.setScopesSupported(scopeNames);
config.setRequestParameterSupported(true);
config.setRequestUriParameterSupported(true);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index ba4e137..7cf3622 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -32,9 +32,11 @@ import org.keycloak.jose.jws.JWSInput;
import org.keycloak.jose.jws.JWSInputException;
import org.keycloak.jose.jws.crypto.HashProvider;
import org.keycloak.jose.jws.crypto.RSAProvider;
+import org.keycloak.migration.migrators.MigrationUtils;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
@@ -42,6 +44,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
@@ -59,10 +62,10 @@ import org.keycloak.representations.RefreshToken;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationSessionManager;
-import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.util.MtlsHoKTokenUtil;
+import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.util.TokenUtil;
import org.keycloak.common.util.Time;
@@ -72,13 +75,9 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.security.PublicKey;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -112,18 +111,19 @@ public class TokenManager {
public static class TokenValidation {
public final UserModel user;
public final UserSessionModel userSession;
- public final AuthenticatedClientSessionModel clientSession;
+ public final ClientSessionContext clientSessionCtx;
public final AccessToken newToken;
- public TokenValidation(UserModel user, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession, AccessToken newToken) {
+ public TokenValidation(UserModel user, UserSessionModel userSession, ClientSessionContext clientSessionCtx, AccessToken newToken) {
this.user = user;
this.userSession = userSession;
- this.clientSession = clientSession;
+ this.clientSessionCtx = clientSessionCtx;
this.newToken = newToken;
}
}
- public TokenValidation validateToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, AccessToken oldToken, HttpHeaders headers) throws OAuthErrorException {
+ public TokenValidation validateToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm,
+ RefreshToken oldToken, HttpHeaders headers) throws OAuthErrorException {
UserSessionModel userSession = null;
boolean offline = TokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType());
@@ -188,13 +188,27 @@ public class TokenManager {
}
+ // Setup clientScopes from refresh token to the context
+ String oldTokenScope = oldToken.getScope();
+
+ // Case when offline token is migrated from previous version
+ if (oldTokenScope == null && userSession.isOffline()) {
+ logger.debugf("Migrating offline token of user '%s' for client '%s' of realm '%s'", user.getUsername(), client.getClientId(), realm.getName());
+ MigrationUtils.migrateOldOfflineToken(session, realm, client, user);
+ }
+
+ ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionAndScopeParameter(clientSession, oldTokenScope);
+
+ // Check user didn't revoke granted consent
+ if (!verifyConsentStillAvailable(session, user, client, clientSessionCtx.getClientScopes())) {
+ throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "Client no longer has requested consent from user");
+ }
+
// recreate token.
- String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
- Set<RoleModel> requestedRoles = TokenManager.getAccess(scopeParam, true, clientSession.getClient(), user);
- AccessToken newToken = createClientAccessToken(session, requestedRoles, realm, client, user, userSession, clientSession);
+ AccessToken newToken = createClientAccessToken(session, realm, client, user, userSession, clientSessionCtx);
verifyAccess(oldToken, newToken);
- return new TokenValidation(user, userSession, clientSession, newToken);
+ return new TokenValidation(user, userSession, clientSessionCtx, newToken);
}
public boolean isTokenValid(KeycloakSession session, RealmModel realm, AccessToken token) throws OAuthErrorException {
@@ -248,22 +262,24 @@ public class TokenManager {
.detail(Details.REFRESH_TOKEN_TYPE, refreshToken.getType());
TokenValidation validation = validateToken(session, uriInfo, connection, realm, refreshToken, headers);
+ AuthenticatedClientSessionModel clientSession = validation.clientSessionCtx.getClientSession();
+
// validate authorizedClient is same as validated client
- if (!validation.clientSession.getClient().getId().equals(authorizedClient.getId())) {
+ if (!clientSession.getClient().getId().equals(authorizedClient.getId())) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token. Token client and authorized client don't match");
}
validateTokenReuse(session, realm, refreshToken, validation);
int currentTime = Time.currentTime();
- validation.clientSession.setTimestamp(currentTime);
+ clientSession.setTimestamp(currentTime);
validation.userSession.setLastSessionRefresh(currentTime);
if (refreshToken.getAuthorization() != null) {
validation.newToken.setAuthorization(refreshToken.getAuthorization());
}
- AccessTokenResponseBuilder responseBuilder = responseBuilder(realm, authorizedClient, event, session, validation.userSession, validation.clientSession)
+ AccessTokenResponseBuilder responseBuilder = responseBuilder(realm, authorizedClient, event, session, validation.userSession, validation.clientSessionCtx)
.accessToken(validation.newToken)
.generateRefreshToken();
@@ -276,7 +292,7 @@ public class TokenManager {
responseBuilder.getRefreshToken().setCertConf(certConf);
}
- String scopeParam = validation.clientSession.getNote(OAuth2Constants.SCOPE);
+ String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
if (TokenUtil.isOIDCRequest(scopeParam)) {
responseBuilder.generateIDToken();
}
@@ -289,27 +305,29 @@ public class TokenManager {
private void validateTokenReuse(KeycloakSession session, RealmModel realm, RefreshToken refreshToken,
TokenValidation validation) throws OAuthErrorException {
if (realm.isRevokeRefreshToken()) {
+ AuthenticatedClientSessionModel clientSession = validation.clientSessionCtx.getClientSession();
+
int clusterStartupTime = session.getProvider(ClusterProvider.class).getClusterStartupTime();
- if (validation.clientSession.getCurrentRefreshToken() != null &&
- !refreshToken.getId().equals(validation.clientSession.getCurrentRefreshToken()) &&
- refreshToken.getIssuedAt() < validation.clientSession.getTimestamp() &&
- clusterStartupTime != validation.clientSession.getTimestamp()) {
+ if (clientSession.getCurrentRefreshToken() != null &&
+ !refreshToken.getId().equals(clientSession.getCurrentRefreshToken()) &&
+ refreshToken.getIssuedAt() < clientSession.getTimestamp() &&
+ clusterStartupTime != clientSession.getTimestamp()) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale token");
}
- if (!refreshToken.getId().equals(validation.clientSession.getCurrentRefreshToken())) {
- validation.clientSession.setCurrentRefreshToken(refreshToken.getId());
- validation.clientSession.setCurrentRefreshTokenUseCount(0);
+ if (!refreshToken.getId().equals(clientSession.getCurrentRefreshToken())) {
+ clientSession.setCurrentRefreshToken(refreshToken.getId());
+ clientSession.setCurrentRefreshTokenUseCount(0);
}
- int currentCount = validation.clientSession.getCurrentRefreshTokenUseCount();
+ int currentCount = clientSession.getCurrentRefreshTokenUseCount();
if (currentCount > realm.getRefreshTokenMaxReuse()) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Maximum allowed refresh token reuse exceeded",
"Maximum allowed refresh token reuse exceeded");
}
- validation.clientSession.setCurrentRefreshTokenUseCount(currentCount + 1);
+ clientSession.setCurrentRefreshTokenUseCount(currentCount + 1);
}
}
@@ -409,18 +427,19 @@ public class TokenManager {
}
}
- public AccessToken createClientAccessToken(KeycloakSession session, Set<RoleModel> requestedRoles, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession,
- AuthenticatedClientSessionModel clientSession) {
- AccessToken token = initToken(realm, client, user, userSession, clientSession, session.getContext().getUri());
+ public AccessToken createClientAccessToken(KeycloakSession session, RealmModel realm, ClientModel client, UserModel user, UserSessionModel userSession,
+ ClientSessionContext clientSessionCtx) {
+ Set<RoleModel> requestedRoles = clientSessionCtx.getRoles();
+ AccessToken token = initToken(realm, client, user, userSession, clientSessionCtx, session.getContext().getUri());
for (RoleModel role : requestedRoles) {
addComposites(token, role);
}
- token = transformAccessToken(session, token, realm, client, user, userSession, clientSession);
+ token = transformAccessToken(session, token, userSession, clientSessionCtx);
return token;
}
- public static AuthenticatedClientSessionModel attachAuthenticationSession(KeycloakSession session, UserSessionModel userSession, AuthenticationSessionModel authSession) {
+ public static ClientSessionContext attachAuthenticationSession(KeycloakSession session, UserSessionModel userSession, AuthenticationSessionModel authSession) {
ClientModel client = authSession.getClient();
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
@@ -431,8 +450,7 @@ public class TokenManager {
clientSession.setRedirectUri(authSession.getRedirectUri());
clientSession.setProtocol(authSession.getProtocol());
- clientSession.setRoles(authSession.getRoles());
- clientSession.setProtocolMappers(authSession.getProtocolMappers());
+ Set<String> clientScopeIds = authSession.getClientScopes();
Map<String, String> transferredNotes = authSession.getClientNotes();
for (Map.Entry<String, String> entry : transferredNotes.entrySet()) {
@@ -449,7 +467,7 @@ public class TokenManager {
// Remove authentication session now
new AuthenticationSessionManager(session).removeAuthenticationSession(userSession.getRealm(), authSession, true);
- return clientSession;
+ return DefaultClientSessionContext.fromClientSessionAndClientScopeIds(clientSession, clientScopeIds);
}
@@ -467,13 +485,15 @@ public class TokenManager {
}
}
- public static void addGroupRoles(GroupModel group, Set<RoleModel> roleMappings) {
+
+ private static void addGroupRoles(GroupModel group, Set<RoleModel> roleMappings) {
roleMappings.addAll(group.getRoleMappings());
if (group.getParentId() == null) return;
addGroupRoles(group.getParent(), roleMappings);
}
- public static Set<RoleModel> getAccess(String scopeParam, boolean applyScopeParam, ClientModel client, UserModel user) {
+
+ public static Set<RoleModel> getAccess(UserModel user, ClientModel client, Set<ClientScopeModel> clientScopes) {
Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
Set<RoleModel> mappings = user.getRoleMappings();
@@ -483,23 +503,25 @@ public class TokenManager {
addGroupRoles(group, roleMappings);
}
-
- ClientTemplateModel template = client.getClientTemplate();
-
- boolean useTemplateScope = template != null && client.useTemplateScope();
-
- if ( (useTemplateScope && template.isFullScopeAllowed()) || (client.isFullScopeAllowed())) {
- logger.debug("Using full scope for client");
+ if (client.isFullScopeAllowed()) {
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Using full scope for client %s", client.getClientId());
+ }
requestedRoles = roleMappings;
} else {
Set<RoleModel> scopeMappings = new HashSet<>();
- if (useTemplateScope) {
- logger.debug("Adding template scope mappings");
- scopeMappings.addAll(template.getScopeMappings());
- }
+
+ // 1 - Client roles of this client itself
scopeMappings.addAll(client.getRoles());
- Set<RoleModel> clientScopeMappings = client.getScopeMappings();
- scopeMappings.addAll(clientScopeMappings);
+
+ // 2 - Role mappings of client itself + default client scopes + optional client scopes requested by scope parameter (if applyScopeParam is true)
+ for (ClientScopeModel clientScope : clientScopes) {
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Adding client scope role mappings of client scope '%s' to client '%s'", clientScope.getName(), client.getClientId());
+ }
+ scopeMappings.addAll(clientScope.getScopeMappings());
+ }
+
for (RoleModel role : roleMappings) {
for (RoleModel desiredRole : scopeMappings) {
Set<RoleModel> visited = new HashSet<RoleModel>();
@@ -507,69 +529,62 @@ public class TokenManager {
}
}
}
- if (applyScopeParam) {
- Collection<String> scopeParamRoles;
- if (scopeParam != null) {
- String[] scopes = scopeParam.split(" ");
- scopeParamRoles = Arrays.asList(scopes);
- } else {
- scopeParamRoles = Collections.emptyList();
- }
- Set<RoleModel> roles = new HashSet<>();
- for (RoleModel role : requestedRoles) {
- String roleName = getRoleNameForScopeParam(role);
- if (!role.isScopeParamRequired() || scopeParamRoles.contains(roleName)) {
- roles.add(role);
- } else {
- if (logger.isTraceEnabled()) {
- logger.tracef("Role '%s' excluded by scope param. Client is '%s', User is '%s', Scope param is '%s' ", role.getName(), client.getClientId(), user.getUsername(), scopeParam);
- }
- }
- }
+ return requestedRoles;
+ }
- // Add all roles specified in scope parameter directly into requestedRoles, even if they are available just through composite role
- List<RoleModel> scopeRoles = new LinkedList<>();
- for (String scopeParamPart : scopeParamRoles) {
- RoleModel scopeParamRole = getRoleFromScopeParam(client.getRealm(), scopeParamPart);
- if (scopeParamRole != null) {
- for (RoleModel role : roles) {
- if (role.hasRole(scopeParamRole)) {
- scopeRoles.add(scopeParamRole);
- }
- }
- }
- }
- roles.addAll(scopeRoles);
- requestedRoles = roles;
+ /** Return client itself + all default client scopes of client + optional client scopes requested by scope parameter **/
+ public static Set<ClientScopeModel> getRequestedClientScopes(String scopeParam, ClientModel client) {
+ // Add all default client scopes automatically
+ Set<ClientScopeModel> clientScopes = new HashSet<>(client.getClientScopes(true, true).values());
+
+ // Add client itself
+ clientScopes.add(client);
+
+ if (scopeParam == null) {
+ return clientScopes;
}
- return requestedRoles;
+ // Add optional client scopes requested by scope parameter
+ String[] scopes = scopeParam.split(" ");
+ Collection<String> scopeParamParts = Arrays.asList(scopes);
+ Map<String, ClientScopeModel> allOptionalScopes = client.getClientScopes(false, true);
+ for (String scopeParamPart : scopeParamParts) {
+ ClientScopeModel scope = allOptionalScopes.get(scopeParamPart);
+ if (scope != null) {
+ clientScopes.add(scope);
+ }
+ }
+
+ return clientScopes;
}
- // For now, just use "roleName" for realm roles and "clientId/roleName" for client roles
- private static String getRoleNameForScopeParam(RoleModel role) {
- if (role.getContainer() instanceof RealmModel) {
- return role.getName();
- } else {
- ClientModel client = (ClientModel) role.getContainer();
- return client.getClientId() + "/" + role.getName();
+ // Check if user still has granted consents to all requested client scopes
+ public static boolean verifyConsentStillAvailable(KeycloakSession session, UserModel user, ClientModel client, Set<ClientScopeModel> requestedClientScopes) {
+ if (!client.isConsentRequired()) {
+ return true;
}
- }
- // For now, just use "roleName" for realm roles and "clientId/roleName" for client roles
- private static RoleModel getRoleFromScopeParam(RealmModel realm, String scopeParamRole) {
- String[] parts = scopeParamRole.split("/");
- if (parts.length == 1) {
- return realm.getRole(parts[0]);
- } else {
- ClientModel roleClient = realm.getClientByClientId(parts[0]);
- return roleClient!=null ? roleClient.getRole(parts[1]) : null;
+ UserConsentModel grantedConsent = session.users().getConsentByClient(client.getRealm(), user.getId(), client.getId());
+
+ for (ClientScopeModel requestedScope : requestedClientScopes) {
+ if (!requestedScope.isDisplayOnConsentScreen()) {
+ continue;
+ }
+
+ if (!grantedConsent.getGrantedClientScopes().contains(requestedScope)) {
+ logger.debugf("Client '%s' no longer has requested consent from user '%s' for client scope '%s'",
+ client.getClientId(), user.getUsername(), requestedScope.getName());
+ return false;
+ }
}
+
+ return true;
}
- public void verifyAccess(AccessToken token, AccessToken newToken) throws OAuthErrorException {
+ // TODO: Remove this check entirely? It should be sufficient to check granted consents (client scopes) during refresh token
+ private void verifyAccess(AccessToken token, AccessToken newToken) throws OAuthErrorException {
if (token.getRealmAccess() != null) {
if (newToken.getRealmAccess() == null) throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for realm roles");
@@ -595,50 +610,51 @@ public class TokenManager {
}
}
- public AccessToken transformAccessToken(KeycloakSession session, AccessToken token, RealmModel realm, ClientModel client, UserModel user,
- UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
- Set<ProtocolMapperModel> mappings = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), client);
+ public AccessToken transformAccessToken(KeycloakSession session, AccessToken token,
+ UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
+ Set<ProtocolMapperModel> mappings = clientSessionCtx.getProtocolMappers();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
if (mapper instanceof OIDCAccessTokenMapper) {
- token = ((OIDCAccessTokenMapper) mapper).transformAccessToken(token, mapping, session, userSession, clientSession);
+ token = ((OIDCAccessTokenMapper) mapper).transformAccessToken(token, mapping, session, userSession, clientSessionCtx.getClientSession());
}
}
return token;
}
- public AccessToken transformUserInfoAccessToken(KeycloakSession session, AccessToken token, RealmModel realm, ClientModel client, UserModel user,
- UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
- Set<ProtocolMapperModel> mappings = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), client);
+ public AccessToken transformUserInfoAccessToken(KeycloakSession session, AccessToken token,
+ UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
+ Set<ProtocolMapperModel> mappings = clientSessionCtx.getProtocolMappers();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
if (mapper instanceof UserInfoTokenMapper) {
- token = ((UserInfoTokenMapper) mapper).transformUserInfoToken(token, mapping, session, userSession, clientSession);
+ token = ((UserInfoTokenMapper) mapper).transformUserInfoToken(token, mapping, session, userSession, clientSessionCtx.getClientSession());
}
}
return token;
}
- public void transformIDToken(KeycloakSession session, IDToken token, RealmModel realm, ClientModel client, UserModel user,
- UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
- Set<ProtocolMapperModel> mappings = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), client);
+ public void transformIDToken(KeycloakSession session, IDToken token,
+ UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
+ Set<ProtocolMapperModel> mappings = clientSessionCtx.getProtocolMappers();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
for (ProtocolMapperModel mapping : mappings) {
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
if (mapper instanceof OIDCIDTokenMapper) {
- token = ((OIDCIDTokenMapper) mapper).transformIDToken(token, mapping, session, userSession, clientSession);
+ token = ((OIDCIDTokenMapper) mapper).transformIDToken(token, mapping, session, userSession, clientSessionCtx.getClientSession());
}
}
}
- protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session, AuthenticatedClientSessionModel clientSession, UriInfo uriInfo) {
+ protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session,
+ ClientSessionContext clientSessionCtx, UriInfo uriInfo) {
AccessToken token = new AccessToken();
token.id(KeycloakModelUtils.generateId());
token.type(TokenUtil.TOKEN_TYPE_BEARER);
@@ -646,8 +662,11 @@ public class TokenManager {
token.audience(client.getClientId());
token.issuedNow();
token.issuedFor(client.getClientId());
+
+ AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
token.issuer(clientSession.getNote(OIDCLoginProtocol.ISSUER));
token.setNonce(clientSession.getNote(OIDCLoginProtocol.NONCE_PARAM));
+ token.setScope(clientSessionCtx.getScopeString());
// Best effort for "acr" value. Use 0 if clientSession was authenticated through cookie ( SSO )
// TODO: Add better acr support. See KEYCLOAK-3314
@@ -721,8 +740,9 @@ public class TokenManager {
return new JWSBuilder().type(JWT).kid(activeRsaKey.getKid()).jsonContent(token).sign(jwsAlgorithm, activeRsaKey.getPrivateKey());
}
- public AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
- return new AccessTokenResponseBuilder(realm, client, event, session, userSession, clientSession);
+ public AccessTokenResponseBuilder responseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session,
+ UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
+ return new AccessTokenResponseBuilder(realm, client, event, session, userSession, clientSessionCtx);
}
public class AccessTokenResponseBuilder {
@@ -731,7 +751,7 @@ public class TokenManager {
EventBuilder event;
KeycloakSession session;
UserSessionModel userSession;
- AuthenticatedClientSessionModel clientSession;
+ ClientSessionContext clientSessionCtx;
AccessToken accessToken;
RefreshToken refreshToken;
@@ -740,17 +760,16 @@ public class TokenManager {
boolean generateAccessTokenHash = false;
String codeHash;
- // Financial API - Part 2: Read and Write API Security Profile
- // http://openid.net/specs/openid-financial-api-part-2.html#authorization-server
String stateHash;
-
- public AccessTokenResponseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
+
+ public AccessTokenResponseBuilder(RealmModel realm, ClientModel client, EventBuilder event, KeycloakSession session,
+ UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
this.realm = realm;
this.client = client;
this.event = event;
this.session = session;
this.userSession = userSession;
- this.clientSession = clientSession;
+ this.clientSessionCtx = clientSessionCtx;
}
public AccessToken getAccessToken() {
@@ -776,9 +795,7 @@ public class TokenManager {
public AccessTokenResponseBuilder generateAccessToken() {
UserModel user = userSession.getUser();
- String scopeParam = clientSession.getNote(OIDCLoginProtocol.SCOPE_PARAM);
- Set<RoleModel> requestedRoles = getAccess(scopeParam, true, client, user);
- accessToken = createClientAccessToken(session, requestedRoles, realm, client, user, userSession, clientSession);
+ accessToken = createClientAccessToken(session, realm, client, user, userSession, clientSessionCtx);
return this;
}
@@ -787,18 +804,18 @@ public class TokenManager {
throw new IllegalStateException("accessToken not set");
}
- String scopeParam = clientSession.getNote(OIDCLoginProtocol.SCOPE_PARAM);
- boolean offlineTokenRequested = TokenUtil.isOfflineTokenRequested(scopeParam);
+ ClientScopeModel offlineAccessScope = KeycloakModelUtils.getClientScopeByName(realm, OAuth2Constants.OFFLINE_ACCESS);
+ boolean offlineTokenRequested = offlineAccessScope==null ? false : clientSessionCtx.getClientScopeIds().contains(offlineAccessScope.getId());
if (offlineTokenRequested) {
UserSessionManager sessionManager = new UserSessionManager(session);
- if (!sessionManager.isOfflineTokenAllowed(clientSession)) {
+ if (!sessionManager.isOfflineTokenAllowed(clientSessionCtx)) {
event.error(Errors.NOT_ALLOWED);
throw new ErrorResponseException("not_allowed", "Offline tokens not allowed for the user or client", Response.Status.BAD_REQUEST);
}
refreshToken = new RefreshToken(accessToken);
refreshToken.type(TokenUtil.TOKEN_TYPE_OFFLINE);
- sessionManager.createOrUpdateOfflineSession(clientSession, userSession);
+ sessionManager.createOrUpdateOfflineSession(clientSessionCtx.getClientSession(), userSession);
} else {
refreshToken = new RefreshToken(accessToken);
refreshToken.expiration(getRefreshExpiration());
@@ -831,7 +848,7 @@ public class TokenManager {
idToken.setSessionState(accessToken.getSessionState());
idToken.expiration(accessToken.getExpiration());
idToken.setAcr(accessToken.getAcr());
- transformIDToken(session, idToken, realm, client, userSession.getUser(), userSession, clientSession);
+ transformIDToken(session, idToken, userSession, clientSessionCtx);
return this;
}
@@ -910,56 +927,13 @@ public class TokenManager {
res.setNotBeforePolicy(notBefore);
// OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
- String requestedScope = clientSession.getNote(OAuth2Constants.SCOPE);
- if (accessToken != null && requestedScope != null) {
- List<String> returnedScopes = new ArrayList<String>();
- // at attachAuthenticationSession(), take over notes from AuthenticationSessionModel to AuthenticatedClientSessionModel
- List<String> requestedScopes = Arrays.asList(requestedScope.split(" "));
-
- // distinguish between so called role scope and oauth scope
- // only pick up oauth scope following https://tools.ietf.org/html/rfc6749#section-5.1
-
- // for realm role - scope
- if (accessToken.getRealmAccess() != null && accessToken.getRealmAccess().getRoles() != null) {
- addRolesAsScopes(returnedScopes, requestedScopes, accessToken.getRealmAccess().getRoles());
- }
- // for client role - scope
- if (accessToken.getResourceAccess() != null) {
- for (String clientId : accessToken.getResourceAccess().keySet()) {
- if (accessToken.getResourceAccess(clientId).getRoles() != null) {
- addRolesAsScopes(returnedScopes, requestedScopes, accessToken.getResourceAccess(clientId).getRoles(), clientId);
- }
- }
- }
- StringBuilder builder = new StringBuilder();
- for (String s : returnedScopes) {
- builder.append(s).append(" ");
- }
- res.setScope(builder.toString().trim());
- }
+ String responseScope = clientSessionCtx.getScopeString();
+ res.setScope(responseScope);
+ event.detail(Details.SCOPE, responseScope);
return res;
}
- private void addRolesAsScopes(List<String> returnedScopes, List<String> requestedScopes, Set<String> roles) {
- for (String r : roles) {
- for (String s : requestedScopes) {
- if (s.equals(r)) {
- returnedScopes.add(s);
- }
- }
- }
- }
-
- private void addRolesAsScopes(List<String> returnedScopes, List<String> requestedScopes, Set<String> roles, String clientId) {
- for (String r : roles) {
- for (String s : requestedScopes) {
- if (s.equals(clientId + "/" + r)) {
- returnedScopes.add(s);
- }
- }
- }
- }
}
public class RefreshResult {
diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
index 5c12615..48c595c 100755
--- a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
@@ -86,17 +86,13 @@ public class ProtocolMapperUtils {
* @return The builtin locale mapper.
*/
public static ProtocolMapperModel findLocaleMapper(KeycloakSession session) {
- ProtocolMapperModel found = null;
for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) {
LoginProtocolFactory factory = (LoginProtocolFactory) p;
- for (ProtocolMapperModel mapper : factory.getBuiltinMappers()) {
- if (mapper.getName().equals(OIDCLoginProtocolFactory.LOCALE) && mapper.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
- found = mapper;
- break;
- }
+ ProtocolMapperModel found = factory.getBuiltinMappers().get(OIDCLoginProtocolFactory.LOCALE);
+ if (found != null && found.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
+ return found;
}
- if (found != null) break;
}
- return found;
+ return null;
}
}
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java
index 423cab9..d61966e 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/AttributeStatementHelper.java
@@ -98,13 +98,11 @@ public class AttributeStatementHelper {
configProperties.add(property);
}
- public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute, String samlAttributeName, String nameFormat, String friendlyName, boolean consentRequired, String consentText, String mapperId) {
+ public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute, String samlAttributeName, String nameFormat, String friendlyName, String mapperId) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(mapperId);
mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(consentRequired);
- mapper.setConsentText(consentText);
Map<String, String> config = new HashMap<String, String>();
if (userAttribute != null) config.put(ProtocolMapperUtils.USER_ATTRIBUTE, userAttribute);
config.put(SAML_ATTRIBUTE_NAME, samlAttributeName);
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java
index d193ee3..96d4fcf 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/GroupMembershipMapper.java
@@ -150,7 +150,6 @@ public class GroupMembershipMapper extends AbstractSAMLProtocolMapper implements
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(false);
Map<String, String> config = new HashMap<String, String>();
config.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, samlAttributeName);
if (friendlyName != null) {
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
index 43c0241..f557e27 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/HardcodedAttributeMapper.java
@@ -83,11 +83,10 @@ public class HardcodedAttributeMapper extends AbstractSAMLProtocolMapper impleme
}
public static ProtocolMapperModel create(String name,
- String samlAttributeName, String nameFormat, String friendlyName, String value,
- boolean consentRequired, String consentText) {
+ String samlAttributeName, String nameFormat, String friendlyName, String value) {
String mapperId = PROVIDER_ID;
ProtocolMapperModel model = AttributeStatementHelper.createAttributeMapper(name, null, samlAttributeName, nameFormat, friendlyName,
- consentRequired, consentText, mapperId);
+ mapperId);
model.getConfig().put(ATTRIBUTE_VALUE, value);
return model;
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java
index 3227350..835c24c 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/RoleListMapper.java
@@ -19,17 +19,15 @@ package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
-import org.keycloak.models.AuthenticatedClientSessionModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.provider.ProviderConfigProperty;
-import org.keycloak.services.managers.ClientSessionCode;
import java.util.ArrayList;
import java.util.HashMap;
@@ -111,14 +109,14 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
}
@Override
- public void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
+ public void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
String single = mappingModel.getConfig().get(SINGLE_ROLE_ATTRIBUTE);
boolean singleAttribute = Boolean.parseBoolean(single);
List<SamlProtocol.ProtocolMapperProcessor<SAMLRoleNameMapper>> roleNameMappers = new LinkedList<>();
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
AttributeType singleAttributeType = null;
- Set<ProtocolMapperModel> requestedProtocolMappers = ClientSessionCode.getRequestedProtocolMappers(clientSession.getProtocolMappers(), clientSession.getClient());
+ Set<ProtocolMapperModel> requestedProtocolMappers = clientSessionCtx.getProtocolMappers();
for (ProtocolMapperModel mapping : requestedProtocolMappers) {
ProtocolMapper mapper = (ProtocolMapper)sessionFactory.getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
@@ -145,11 +143,8 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
}
}
- RealmModel realm = clientSession.getRealm();
- List<String> allRoleNames = clientSession.getRoles().stream()
+ List<String> allRoleNames = clientSessionCtx.getRoles().stream()
// todo need a role mapping
- .map(realm::getRoleById)
- .filter(Objects::nonNull)
.flatMap(RoleUtils::expandCompositeRolesStream)
.map(roleModel -> roleNameMappers.stream()
.map(entry -> entry.mapper.mapName(entry.model, roleModel))
@@ -181,7 +176,6 @@ public class RoleListMapper extends AbstractSAMLProtocolMapper implements SAMLRo
mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(false);
Map<String, String> config = new HashMap<>();
config.put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, samlAttributeName);
if (friendlyName != null) {
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/SAMLRoleListMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/SAMLRoleListMapper.java
index 991c223..24e59b1 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/SAMLRoleListMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/SAMLRoleListMapper.java
@@ -18,7 +18,7 @@
package org.keycloak.protocol.saml.mappers;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
-import org.keycloak.models.AuthenticatedClientSessionModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
@@ -30,5 +30,5 @@ import org.keycloak.models.UserSessionModel;
public interface SAMLRoleListMapper {
void mapRoles(AttributeStatementType roleAttributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session,
- UserSessionModel userSession, AuthenticatedClientSessionModel clientSession);
+ UserSessionModel userSession, ClientSessionContext clientSessionCtx );
}
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java
index 2579af1..ab8a5e4 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/UserAttributeStatementMapper.java
@@ -86,11 +86,10 @@ public class UserAttributeStatementMapper extends AbstractSAMLProtocolMapper imp
}
public static ProtocolMapperModel createAttributeMapper(String name, String userAttribute,
- String samlAttributeName, String nameFormat, String friendlyName,
- boolean consentRequired, String consentText) {
+ String samlAttributeName, String nameFormat, String friendlyName) {
String mapperId = PROVIDER_ID;
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, nameFormat, friendlyName,
- consentRequired, consentText, mapperId);
+ mapperId);
}
diff --git a/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java b/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
index 1d7d038..685387c 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/mappers/UserPropertyAttributeStatementMapper.java
@@ -90,7 +90,7 @@ public class UserPropertyAttributeStatementMapper extends AbstractSAMLProtocolMa
boolean consentRequired, String consentText) {
String mapperId = PROVIDER_ID;
return AttributeStatementHelper.createAttributeMapper(name, userAttribute, samlAttributeName, nameFormat, friendlyName,
- consentRequired, consentText, mapperId);
+ mapperId);
}
}
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
index d53d1e5..49d2e3c 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java
@@ -34,6 +34,7 @@ import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeyManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@@ -353,8 +354,8 @@ public class SamlProtocol implements LoginProtocol {
}
@Override
- public Response authenticated(UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
- ClientSessionCode<AuthenticatedClientSessionModel> accessCode = new ClientSessionCode<>(session, realm, clientSession);
+ public Response authenticated(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
+ AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
ClientModel client = clientSession.getClient();
SamlClient samlClient = new SamlClient(client);
String requestID = clientSession.getNote(SAML_REQUEST_ID);
@@ -386,7 +387,7 @@ public class SamlProtocol implements LoginProtocol {
List<ProtocolMapperProcessor<SAMLLoginResponseMapper>> loginResponseMappers = new LinkedList<>();
ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper = null;
- Set<ProtocolMapperModel> mappings = accessCode.getRequestedProtocolMappers();
+ Set<ProtocolMapperModel> mappings = clientSessionCtx.getProtocolMappers();
for (ProtocolMapperModel mapping : mappings) {
ProtocolMapper mapper = (ProtocolMapper) session.getKeycloakSessionFactory().getProviderFactory(ProtocolMapper.class, mapping.getProtocolMapper());
@@ -416,7 +417,7 @@ public class SamlProtocol implements LoginProtocol {
ResponseType samlModel = builder.buildModel();
final AttributeStatementType attributeStatement = populateAttributeStatements(attributeStatementMappers, session, userSession, clientSession);
- populateRoles(roleListMapper, session, userSession, clientSession, attributeStatement);
+ populateRoles(roleListMapper, session, userSession, clientSessionCtx, attributeStatement);
// SAML Spec 2.7.3 AttributeStatement must contain one or more Attribute or EncryptedAttribute
if (attributeStatement.getAttributes().size() > 0) {
@@ -501,11 +502,11 @@ public class SamlProtocol implements LoginProtocol {
return response;
}
- public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession,
- final AttributeStatementType existingAttributeStatement) {
+ public void populateRoles(ProtocolMapperProcessor<SAMLRoleListMapper> roleListMapper, KeycloakSession session, UserSessionModel userSession,
+ ClientSessionContext clientSessionCtx, final AttributeStatementType existingAttributeStatement) {
if (roleListMapper == null)
return;
- roleListMapper.mapper.mapRoles(existingAttributeStatement, roleListMapper.model, session, userSession, clientSession);
+ roleListMapper.mapper.mapRoles(existingAttributeStatement, roleListMapper.model, session, userSession, clientSessionCtx);
}
public static String getLogoutServiceUrl(UriInfo uriInfo, ClientModel client, String bindingType) {
diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
index 87d6615..32212a3 100755
--- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
@@ -18,9 +18,10 @@
package org.keycloak.protocol.saml;
import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -32,7 +33,7 @@ import org.keycloak.protocol.saml.mappers.RoleListMapper;
import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
import org.keycloak.representations.idm.CertificateRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
@@ -54,6 +55,9 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
private static final Pattern PROTOCOL_MAP_PATTERN = Pattern.compile("\\s*([a-zA-Z][a-zA-Z\\d+-.]*)\\s*=\\s*(\\d+)\\s*");
private static final String[] DEFAULT_PROTOCOL_TO_PORT_MAP = new String[] { "http=80", "https=443" };
+ public static final String SCOPE_ROLE_LIST = "role_list";
+ private static final String ROLE_LIST_CONSENT_TEXT = "${samlRoleListScopeConsentText}";
+
private final Map<Integer, String> knownPorts = new HashMap<>();
private final Map<String, Integer> knownProtocols = new HashMap<>();
@@ -99,16 +103,11 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
}
@Override
- public List<ProtocolMapperModel> getBuiltinMappers() {
+ public Map<String, ProtocolMapperModel> getBuiltinMappers() {
return builtins;
}
- @Override
- public List<ProtocolMapperModel> getDefaultBuiltinMappers() {
- return defaultBuiltins;
- }
-
- static List<ProtocolMapperModel> builtins = new ArrayList<>();
+ static Map<String, ProtocolMapperModel> builtins = new HashMap<>();
static List<ProtocolMapperModel> defaultBuiltins = new ArrayList<>();
static {
@@ -119,34 +118,41 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
X500SAMLProfileConstants.EMAIL.getFriendlyName(),
true, "${email}");
- builtins.add(model);
+ builtins.put("X500 email", model);
model = UserPropertyAttributeStatementMapper.createAttributeMapper("X500 givenName",
"firstName",
X500SAMLProfileConstants.GIVEN_NAME.get(),
JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(),
true, "${givenName}");
- builtins.add(model);
+ builtins.put("X500 givenName", model);
model = UserPropertyAttributeStatementMapper.createAttributeMapper("X500 surname",
"lastName",
X500SAMLProfileConstants.SURNAME.get(),
JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
X500SAMLProfileConstants.SURNAME.getFriendlyName(),
true, "${familyName}");
- builtins.add(model);
+ builtins.put("X500 surname", model);
model = RoleListMapper.create("role list", "Role", AttributeStatementHelper.BASIC, null, false);
- builtins.add(model);
+ builtins.put("role list", model);
defaultBuiltins.add(model);
}
@Override
+ protected void createDefaultClientScopesImpl(RealmModel newRealm) {
+ ClientScopeModel roleListScope = newRealm.addClientScope(SCOPE_ROLE_LIST);
+ roleListScope.setDescription("SAML role list");
+ roleListScope.setDisplayOnConsentScreen(true);
+ roleListScope.setConsentScreenText(ROLE_LIST_CONSENT_TEXT);
+ roleListScope.setProtocol(getId());
+ roleListScope.addProtocolMapper(builtins.get("role list"));
+ newRealm.addDefaultClientScope(roleListScope, true);
+ }
+
+ @Override
protected void addDefaults(ClientModel client) {
- for (ProtocolMapperModel model : defaultBuiltins) {
- model.setProtocol(getId());
- client.addProtocolMapper(model);
- }
}
@Override
@@ -196,43 +202,4 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
}
}
- @Override
- public void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient) {
- SamlRepresentationAttributes rep = new SamlRepresentationAttributes(clientRep.getAttributes());
- SamlClientTemplate client = new SamlClientTemplate(newClient);
- if (clientRep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true);
- if (rep.getCanonicalizationMethod() == null) {
- client.setCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE);
- }
- if (rep.getSignatureAlgorithm() == null) {
- client.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256);
- }
-
- if (rep.getNameIDFormat() == null) {
- client.setNameIDFormat("username");
- }
-
- if (rep.getIncludeAuthnStatement() == null) {
- client.setIncludeAuthnStatement(true);
- }
-
- if (rep.getForceNameIDFormat() == null) {
- client.setForceNameIDFormat(false);
- }
-
- if (rep.getSamlServerSignature() == null) {
- client.setRequiresRealmSignature(true);
- }
- if (rep.getForcePostBinding() == null) {
- client.setForcePostBinding(true);
- }
-
- if (rep.getClientSignature() == null) {
- client.setRequiresClientSignature(true);
- }
-
- if (clientRep.isFrontchannelLogout() == null) {
- newClient.setFrontchannelLogout(true);
- }
- }
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java
index 6fefa93..54d2be6 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/DefaultClientRegistrationPolicies.java
@@ -31,9 +31,8 @@ import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.protocol.saml.mappers.RoleListMapper;
import org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper;
import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
-import org.keycloak.services.clientregistration.policy.impl.ClientTemplatesClientRegistrationPolicyFactory;
+import org.keycloak.services.clientregistration.policy.impl.ClientScopesClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ConsentRequiredClientRegistrationPolicyFactory;
-import org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicy;
import org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ProtocolMappersClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ScopeClientRegistrationPolicyFactory;
@@ -107,11 +106,11 @@ public class DefaultClientRegistrationPolicies {
private static void addGenericPolicies(RealmModel realm, String policyTypeKey) {
ComponentModel protMapperModel = createModelInstance("Allowed Protocol Mapper Types", realm, ProtocolMappersClientRegistrationPolicyFactory.PROVIDER_ID, policyTypeKey);
protMapperModel.getConfig().put(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES, Arrays.asList(DEFAULT_ALLOWED_PROTOCOL_MAPPERS));
- protMapperModel.getConfig().putSingle(ProtocolMappersClientRegistrationPolicyFactory.CONSENT_REQUIRED_FOR_ALL_MAPPERS, "true");
realm.addComponentModel(protMapperModel);
- ComponentModel clientTemplatesModel = createModelInstance("Allowed Client Templates", realm, ClientTemplatesClientRegistrationPolicyFactory.PROVIDER_ID, policyTypeKey);
- clientTemplatesModel.getConfig().put(ClientTemplatesClientRegistrationPolicyFactory.ALLOWED_CLIENT_TEMPLATES, Collections.emptyList());
+ ComponentModel clientTemplatesModel = createModelInstance("Allowed Client Scopes", realm, ClientScopesClientRegistrationPolicyFactory.PROVIDER_ID, policyTypeKey);
+ clientTemplatesModel.getConfig().put(ClientScopesClientRegistrationPolicyFactory.ALLOWED_CLIENT_SCOPES, Collections.emptyList());
+ clientTemplatesModel.put(ClientScopesClientRegistrationPolicyFactory.ALLOW_DEFAULT_SCOPES, true);
realm.addComponentModel(clientTemplatesModel);
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java
index f273def..40cfc0d 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicy.java
@@ -70,25 +70,6 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
}
}
-
- protected void enableConsentRequiredForAll(ClientModel clientModel) {
- if (isConsentRequiredForMappers()) {
- logger.debugf("Enable consentRequired for all protocol mappers of client %s", clientModel.getClientId());
-
- Set<ProtocolMapperModel> mappers = clientModel.getProtocolMappers();
-
- for (ProtocolMapperModel mapper : mappers) {
- mapper.setConsentRequired(true);
-
- if (mapper.getConsentText() == null) {
- mapper.setConsentText(mapper.getName());
- }
-
- clientModel.updateProtocolMapper(mapper);
- }
- }
- }
-
// Remove builtin mappers of unsupported types too
@Override
public void afterRegister(ClientRegistrationContext context, ClientModel clientModel) {
@@ -107,9 +88,6 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
});
- // Enable consentRequired for all protocolMappers
- enableConsentRequiredForAll(clientModel);
-
}
// We don't take already existing protocolMappers into consideration for now
@@ -120,8 +98,6 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
@Override
public void afterUpdate(ClientRegistrationContext context, ClientModel clientModel) {
- // Enable consentRequired for all protocolMappers
- enableConsentRequiredForAll(clientModel);
}
@Override
@@ -138,8 +114,4 @@ public class ProtocolMappersClientRegistrationPolicy implements ClientRegistrati
return componentModel.getConfig().getList(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES);
}
- private boolean isConsentRequiredForMappers() {
- String s = componentModel.getConfig().getFirst(ProtocolMappersClientRegistrationPolicyFactory.CONSENT_REQUIRED_FOR_ALL_MAPPERS);
- return s==null || Boolean.parseBoolean(s);
- }
}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java
index a9aea14..ab292f6 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/policy/impl/ProtocolMappersClientRegistrationPolicyFactory.java
@@ -41,8 +41,6 @@ public class ProtocolMappersClientRegistrationPolicyFactory extends AbstractClie
public static final String ALLOWED_PROTOCOL_MAPPER_TYPES = "allowed-protocol-mapper-types";
- public static final String CONSENT_REQUIRED_FOR_ALL_MAPPERS = "consent-required-for-all-mappers";
-
@Override
public ClientRegistrationPolicy create(KeycloakSession session, ComponentModel model) {
return new ProtocolMappersClientRegistrationPolicy(session, model);
@@ -60,14 +58,6 @@ public class ProtocolMappersClientRegistrationPolicyFactory extends AbstractClie
property.setType(ProviderConfigProperty.MULTIVALUED_LIST_TYPE);
property.setOptions(getProtocolMapperFactoryIds());
configProperties.add(property);
-
- property = new ProviderConfigProperty();
- property.setName(CONSENT_REQUIRED_FOR_ALL_MAPPERS);
- property.setLabel("consent-required-for-all-mappers.label");
- property.setHelpText("consent-required-for-all-mappers.tooltip");
- property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
- property.setDefaultValue("true");
- configProperties.add(property);
}
private List<String> getProtocolMapperFactoryIds() {
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 6a1e89b..3753bc2 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -17,7 +17,6 @@
package org.keycloak.services.managers;
import org.jboss.logging.Logger;
-import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.OAuth2Constants;
import org.keycloak.TokenVerifier;
@@ -58,7 +57,6 @@ import org.keycloak.sessions.RootAuthenticationSessionModel;
import javax.crypto.SecretKey;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
@@ -696,7 +694,7 @@ public class AuthenticationManager {
public static Response redirectAfterSuccessfulFlow(KeycloakSession session, RealmModel realm, UserSessionModel userSession,
- AuthenticatedClientSessionModel clientSession,
+ ClientSessionContext clientSessionCtx,
HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection,
EventBuilder event, String protocol) {
LoginProtocol protocolImpl = session.getProvider(LoginProtocol.class, protocol);
@@ -704,12 +702,12 @@ public class AuthenticationManager {
.setHttpHeaders(request.getHttpHeaders())
.setUriInfo(uriInfo)
.setEventBuilder(event);
- return redirectAfterSuccessfulFlow(session, realm, userSession, clientSession, request, uriInfo, clientConnection, event, protocolImpl);
+ return redirectAfterSuccessfulFlow(session, realm, userSession, clientSessionCtx, request, uriInfo, clientConnection, event, protocolImpl);
}
public static Response redirectAfterSuccessfulFlow(KeycloakSession session, RealmModel realm, UserSessionModel userSession,
- AuthenticatedClientSessionModel clientSession,
+ ClientSessionContext clientSessionCtx,
HttpRequest request, UriInfo uriInfo, ClientConnection clientConnection,
EventBuilder event, LoginProtocol protocol) {
Cookie sessionCookie = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_SESSION_COOKIE);
@@ -740,6 +738,8 @@ public class AuthenticationManager {
expireRememberMeCookie(realm, uriInfo, clientConnection);
}
+ AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
+
// Update userSession note with authTime. But just if flag SSO_AUTH is not set
boolean isSSOAuthentication = "true".equals(session.getAttribute(SSO_AUTH));
if (isSSOAuthentication) {
@@ -750,7 +750,7 @@ public class AuthenticationManager {
clientSession.removeNote(SSO_AUTH);
}
- return protocol.authenticated(userSession, clientSession);
+ return protocol.authenticated(userSession, clientSessionCtx);
}
@@ -830,12 +830,13 @@ public class AuthenticationManager {
}
RealmModel realm = authSession.getRealm();
- AuthenticatedClientSessionModel clientSession = AuthenticationProcessor.attachSession(authSession, userSession, session, realm, clientConnection, event);
+ ClientSessionContext clientSessionCtx = AuthenticationProcessor.attachSession(authSession, userSession, session, realm, clientConnection, event);
+ userSession = clientSessionCtx.getClientSession().getUserSession();
event.event(EventType.LOGIN);
- event.session(clientSession.getUserSession());
+ event.session(userSession);
event.success();
- return redirectAfterSuccessfulFlow(session, realm, clientSession.getUserSession(), clientSession, request, uriInfo, clientConnection, event, authSession.getProtocol());
+ return redirectAfterSuccessfulFlow(session, realm, userSession, clientSessionCtx, request, uriInfo, clientConnection, event, authSession.getProtocol());
}
// Return null if action is not required. Or the name of the requiredAction in case it is required.
@@ -859,23 +860,12 @@ public class AuthenticationManager {
UserConsentModel grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId());
- ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, realm, authSession);
- for (RoleModel r : accessCode.getRequestedRoles()) {
-
- // Consent already granted by user
- if (grantedConsent != null && grantedConsent.isRoleGranted(r)) {
- continue;
- }
+ // See if any clientScopes need to be approved on consent screen
+ List<ClientScopeModel> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(realm, grantedConsent, authSession);
+ if (!clientScopesToApprove.isEmpty()) {
return CommonClientSessionModel.Action.OAUTH_GRANT.name();
- }
-
- for (ProtocolMapperModel protocolMapper : accessCode.getRequestedProtocolMappers()) {
- if (protocolMapper.isConsentRequired() && protocolMapper.getConsentText() != null) {
- if (grantedConsent == null || !grantedConsent.isProtocolMapperGranted(protocolMapper)) {
- return CommonClientSessionModel.Action.OAUTH_GRANT.name();
- }
- }
}
+
String consentDetail = (grantedConsent != null) ? Details.CONSENT_VALUE_PERSISTED_CONSENT : Details.CONSENT_VALUE_NO_CONSENT_REQUIRED;
event.detail(Details.CONSENT, consentDetail);
} else {
@@ -913,46 +903,21 @@ public class AuthenticationManager {
UserConsentModel grantedConsent = session.users().getConsentByClient(realm, user.getId(), client.getId());
- List<RoleModel> realmRoles = new LinkedList<>();
- MultivaluedMap<String, RoleModel> resourceRoles = new MultivaluedMapImpl<>();
- ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, realm, authSession);
- for (RoleModel r : accessCode.getRequestedRoles()) {
-
- // Consent already granted by user
- if (grantedConsent != null && grantedConsent.isRoleGranted(r)) {
- continue;
- }
-
- if (r.getContainer() instanceof RealmModel) {
- realmRoles.add(r);
- } else {
- resourceRoles.add(((ClientModel) r.getContainer()).getClientId(), r);
- }
- }
-
- List<ProtocolMapperModel> protocolMappers = new LinkedList<>();
- for (ProtocolMapperModel protocolMapper : accessCode.getRequestedProtocolMappers()) {
- if (protocolMapper.isConsentRequired() && protocolMapper.getConsentText() != null) {
- if (grantedConsent == null || !grantedConsent.isProtocolMapperGranted(protocolMapper)) {
- protocolMappers.add(protocolMapper);
- }
- }
- }
+ List<ClientScopeModel> clientScopesToApprove = getClientScopesToApproveOnConsentScreen(realm, grantedConsent, authSession);
// Skip grant screen if everything was already approved by this user
- if (realmRoles.size() > 0 || resourceRoles.size() > 0 || protocolMappers.size() > 0) {
+ if (clientScopesToApprove.size() > 0) {
String execution = AuthenticatedClientSessionModel.Action.OAUTH_GRANT.name();
- accessCode.
-
- setAction(AuthenticatedClientSessionModel.Action.REQUIRED_ACTIONS.name());
+ ClientSessionCode<AuthenticationSessionModel> accessCode = new ClientSessionCode<>(session, realm, authSession);
+ accessCode.setAction(AuthenticatedClientSessionModel.Action.REQUIRED_ACTIONS.name());
authSession.setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution);
return session.getProvider(LoginFormsProvider.class)
.setAuthenticationSession(authSession)
.setExecution(execution)
.setClientSessionCode(accessCode.getOrGenerateCode())
- .setAccessRequest(realmRoles, resourceRoles, protocolMappers)
+ .setAccessRequest(clientScopesToApprove)
.createOAuthGrant();
} else {
String consentDetail = (grantedConsent != null) ? Details.CONSENT_VALUE_PERSISTED_CONSENT : Details.CONSENT_VALUE_NO_CONSENT_REQUIRED;
@@ -965,35 +930,40 @@ public class AuthenticationManager {
}
+ private static List<ClientScopeModel> getClientScopesToApproveOnConsentScreen(RealmModel realm, UserConsentModel grantedConsent,
+ AuthenticationSessionModel authSession) {
+ // Client Scopes to be displayed on consent screen
+ List<ClientScopeModel> clientScopesToDisplay = new LinkedList<>();
+
+ for (String clientScopeId : authSession.getClientScopes()) {
+ ClientScopeModel clientScope = KeycloakModelUtils.findClientScopeById(realm, clientScopeId);
+
+ if (clientScope == null || !clientScope.isDisplayOnConsentScreen()) {
+ continue;
+ }
+
+ // Check if consent already granted by user
+ if (grantedConsent == null || !grantedConsent.isClientScopeGranted(clientScope)) {
+ clientScopesToDisplay.add(clientScope);
+ }
+ }
+
+ return clientScopesToDisplay;
+ }
+
- public static void setRolesAndMappersInSession(AuthenticationSessionModel authSession) {
+ public static void setClientScopesInSession(AuthenticationSessionModel authSession) {
ClientModel client = authSession.getClient();
UserModel user = authSession.getAuthenticatedUser();
- Set<String> requestedRoles = new HashSet<String>();
// todo scope param protocol independent
String scopeParam = authSession.getClientNote(OAuth2Constants.SCOPE);
- for (RoleModel r : TokenManager.getAccess(scopeParam, true, client, user)) {
- requestedRoles.add(r.getId());
- }
- authSession.setRoles(requestedRoles);
-
- Set<String> requestedProtocolMappers = new HashSet<String>();
- ClientTemplateModel clientTemplate = client.getClientTemplate();
- if (clientTemplate != null && client.useTemplateMappers()) {
- for (ProtocolMapperModel protocolMapper : clientTemplate.getProtocolMappers()) {
- if (protocolMapper.getProtocol().equals(authSession.getProtocol())) {
- requestedProtocolMappers.add(protocolMapper.getId());
- }
- }
+ Set<String> requestedClientScopes = new HashSet<String>();
+ for (ClientScopeModel clientScope : TokenManager.getRequestedClientScopes(scopeParam, client)) {
+ requestedClientScopes.add(clientScope.getId());
}
- for (ProtocolMapperModel protocolMapper : client.getProtocolMappers()) {
- if (protocolMapper.getProtocol().equals(authSession.getProtocol())) {
- requestedProtocolMappers.add(protocolMapper.getId());
- }
- }
- authSession.setProtocolMappers(requestedProtocolMappers);
+ authSession.setClientScopes(requestedClientScopes);
}
public static RequiredActionProvider createRequiredAction(RequiredActionContextResult context) {
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
index 1bcfaf5..d391e04 100644
--- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
@@ -174,7 +174,6 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_ID,
ServiceAccountConstants.CLIENT_ID, "String",
- false, "",
true, true);
client.addProtocolMapper(protocolMapper);
}
@@ -185,7 +184,6 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_HOST,
ServiceAccountConstants.CLIENT_HOST, "String",
- false, "",
true, true);
client.addProtocolMapper(protocolMapper);
}
@@ -195,7 +193,6 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_ADDRESS,
ServiceAccountConstants.CLIENT_ADDRESS, "String",
- false, "",
true, true);
client.addProtocolMapper(protocolMapper);
}
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
index 46a4508..913028c 100755
--- a/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
@@ -20,15 +20,10 @@ package org.keycloak.services.managers;
import org.keycloak.common.util.Time;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.RoleModel;
import org.keycloak.sessions.CommonClientSessionModel;
-import java.util.HashSet;
-import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -198,42 +193,6 @@ public class ClientSessionCode<CLIENT_SESSION extends CommonClientSessionModel>
}
- public Set<RoleModel> getRequestedRoles() {
- return getRequestedRoles(commonLoginSession, realm);
- }
-
- public static Set<RoleModel> getRequestedRoles(CommonClientSessionModel clientSession, RealmModel realm) {
- Set<RoleModel> requestedRoles = new HashSet<>();
- for (String roleId : clientSession.getRoles()) {
- RoleModel role = realm.getRoleById(roleId);
- if (role != null) {
- requestedRoles.add(role);
- }
- }
- return requestedRoles;
- }
-
- public Set<ProtocolMapperModel> getRequestedProtocolMappers() {
- return getRequestedProtocolMappers(commonLoginSession.getProtocolMappers(), commonLoginSession.getClient());
- }
-
- public static Set<ProtocolMapperModel> getRequestedProtocolMappers(Set<String> protocolMappers, ClientModel client) {
- Set<ProtocolMapperModel> requestedProtocolMappers = new HashSet<>();
- ClientTemplateModel template = client.getClientTemplate();
- if (protocolMappers != null) {
- for (String protocolMapperId : protocolMappers) {
- ProtocolMapperModel protocolMapper = client.getProtocolMapperById(protocolMapperId);
- if (protocolMapper == null && template != null) {
- protocolMapper = template.getProtocolMapperById(protocolMapperId);
- }
- if (protocolMapper != null) {
- requestedProtocolMappers.add(protocolMapper);
- }
- }
- }
- return requestedProtocolMappers;
- }
-
public void setAction(String action) {
commonLoginSession.setAction(action);
diff --git a/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java b/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java
index bde7bd1..488e21d 100644
--- a/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java
+++ b/services/src/main/java/org/keycloak/services/managers/CodeGenerateUtil.java
@@ -93,6 +93,8 @@ class CodeGenerateUtil {
int getTimestamp(CS clientSession);
void setTimestamp(CS clientSession, int timestamp);
+ String getClientNote(CS clientSession, String noteKey);
+
}
@@ -156,6 +158,11 @@ class CodeGenerateUtil {
public void setTimestamp(AuthenticationSessionModel clientSession, int timestamp) {
clientSession.getParentSession().setTimestamp(timestamp);
}
+
+ @Override
+ public String getClientNote(AuthenticationSessionModel clientSession, String noteKey) {
+ return clientSession.getClientNote(noteKey);
+ }
}
@@ -266,6 +273,11 @@ class CodeGenerateUtil {
public void setTimestamp(AuthenticatedClientSessionModel clientSession, int timestamp) {
clientSession.setTimestamp(timestamp);
}
+
+ @Override
+ public String getClientNote(AuthenticatedClientSessionModel clientSession, String noteKey) {
+ return clientSession.getNote(noteKey);
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 74cc576..b12e2ec 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -27,7 +27,6 @@ import org.keycloak.models.Constants;
import org.keycloak.models.ImpersonationConstants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OTPPolicy;
-import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
@@ -36,6 +35,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.models.utils.DefaultClientScopes;
import org.keycloak.models.utils.DefaultRequiredActions;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RepresentationToModel;
@@ -44,6 +44,7 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -119,7 +120,8 @@ public class RealmManager {
setupImpersonationService(realm);
setupAuthenticationFlows(realm);
setupRequiredActions(realm);
- setupOfflineTokens(realm);
+ setupOfflineTokens(realm, null);
+ createDefaultClientScopes(realm);
setupAuthorizationServices(realm);
setupClientRegistrations(realm);
@@ -136,8 +138,27 @@ public class RealmManager {
if (realm.getRequiredActionProviders().size() == 0) DefaultRequiredActions.addActions(realm);
}
- protected void setupOfflineTokens(RealmModel realm) {
- KeycloakModelUtils.setupOfflineTokens(realm);
+ private void setupOfflineTokens(RealmModel realm, RealmRepresentation realmRep) {
+ RoleModel offlineRole = KeycloakModelUtils.setupOfflineRole(realm);
+
+ if (realmRep != null && hasRealmRole(realmRep, Constants.OFFLINE_ACCESS_ROLE)) {
+ // Case when realmRep had the offline_access role, but not the offline_access client scope. Need to manually remove the role
+ List<RoleRepresentation> realmRoles = realmRep.getRoles().getRealm();
+ for (RoleRepresentation role : realmRoles) {
+ if (Constants.OFFLINE_ACCESS_ROLE.equals(role.getName())) {
+ realmRoles.remove(role);
+ break;
+ }
+ }
+ }
+
+ if (realmRep == null || !hasClientScope(realmRep, Constants.OFFLINE_ACCESS_ROLE)) {
+ DefaultClientScopes.createOfflineAccessClientScope(realm, offlineRole);
+ }
+ }
+
+ protected void createDefaultClientScopes(RealmModel realm) {
+ DefaultClientScopes.createDefaultClientScopes(session, realm, true);
}
protected void setupAdminConsole(RealmModel realm) {
@@ -298,13 +319,11 @@ public class RealmManager {
RoleModel createRealmRole = realm.addRole(AdminRoles.CREATE_REALM);
adminRole.addCompositeRole(createRealmRole);
createRealmRole.setDescription("${role_" + AdminRoles.CREATE_REALM + "}");
- createRealmRole.setScopeParamRequired(false);
} else {
adminRealm = model.getRealm(Config.getAdminRealm());
adminRole = adminRealm.getRole(AdminRoles.ADMIN);
}
adminRole.setDescription("${role_"+AdminRoles.ADMIN+"}");
- adminRole.setScopeParamRequired(false);
ClientModel realmAdminApp = KeycloakModelUtils.createClient(adminRealm, KeycloakModelUtils.getMasterRealmAdminApplicationClientId(realm.getName()));
// No localized name for now
@@ -315,7 +334,6 @@ public class RealmManager {
for (String r : AdminRoles.ALL_REALM_ROLES) {
RoleModel role = realmAdminApp.addRole(r);
role.setDescription("${role_"+r+"}");
- role.setScopeParamRequired(false);
adminRole.addCompositeRole(role);
}
addQueryCompositeRoles(realmAdminApp);
@@ -347,7 +365,6 @@ public class RealmManager {
}
RoleModel adminRole = realmAdminClient.addRole(AdminRoles.REALM_ADMIN);
adminRole.setDescription("${role_" + AdminRoles.REALM_ADMIN + "}");
- adminRole.setScopeParamRequired(false);
realmAdminClient.setBearerOnly(true);
realmAdminClient.setFullScopeAllowed(false);
realmAdminClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
@@ -361,7 +378,6 @@ public class RealmManager {
private void addAndSetAdminRole(String roleName, ClientModel parentClient, RoleModel parentRole) {
RoleModel role = parentClient.addRole(roleName);
role.setDescription("${role_" + roleName + "}");
- role.setScopeParamRequired(false);
parentRole.addCompositeRole(role);
}
@@ -377,7 +393,6 @@ public class RealmManager {
if (adminRole == null) {
adminRole = realmAdminClient.addRole(AdminRoles.REALM_ADMIN);
adminRole.setDescription("${role_" + AdminRoles.REALM_ADMIN + "}");
- adminRole.setScopeParamRequired(false);
}
for (String r : AdminRoles.ALL_REALM_ROLES) {
@@ -407,11 +422,9 @@ public class RealmManager {
client.addDefaultRole(role);
RoleModel roleModel = client.getRole(role);
roleModel.setDescription("${role_" + role + "}");
- roleModel.setScopeParamRequired(false);
}
RoleModel manageAccountLinks = client.addRole(AccountRoles.MANAGE_ACCOUNT_LINKS);
manageAccountLinks.setDescription("${role_" + AccountRoles.MANAGE_ACCOUNT_LINKS + "}");
- manageAccountLinks.setScopeParamRequired(false);
RoleModel manageAccount = client.getRole(AccountRoles.MANAGE_ACCOUNT);
manageAccount.addCompositeRole(manageAccountLinks);
}
@@ -433,7 +446,6 @@ public class RealmManager {
for (String role : Constants.BROKER_SERVICE_ROLES) {
RoleModel roleModel = client.addRole(role);
roleModel.setDescription("${role_"+ role.toLowerCase().replaceAll("_", "-") +"}");
- roleModel.setScopeParamRequired(false);
}
}
}
@@ -486,7 +498,13 @@ public class RealmManager {
}
}
- if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE)) setupOfflineTokens(realm);
+ if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE) || !hasClientScope(rep, Constants.OFFLINE_ACCESS_ROLE)) {
+ setupOfflineTokens(realm, rep);
+ }
+
+ if (rep.getClientScopes() == null) {
+ createDefaultClientScopes(realm);
+ }
RepresentationToModel.importRealm(session, rep, realm, skipUserDependent);
@@ -604,6 +622,20 @@ public class RealmManager {
return false;
}
+ private boolean hasClientScope(RealmRepresentation rep, String clientScopeName) {
+ if (rep.getClientScopes() == null) {
+ return false;
+ }
+
+ for (ClientScopeRepresentation clientScope : rep.getClientScopes()) {
+ if (clientScopeName.equals(clientScope.getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Query users based on a search string:
* <p/>
diff --git a/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java b/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java
index 1bef11e..1283f5f 100644
--- a/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/UserSessionManager.java
@@ -20,6 +20,7 @@ import org.jboss.logging.Logger;
import org.keycloak.common.util.Time;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -122,14 +123,21 @@ public class UserSessionManager {
persister.removeUserSession(userSession.getId(), true);
}
- public boolean isOfflineTokenAllowed(AuthenticatedClientSessionModel clientSession) {
- RoleModel offlineAccessRole = clientSession.getRealm().getRole(Constants.OFFLINE_ACCESS_ROLE);
+ public boolean isOfflineTokenAllowed(ClientSessionContext clientSessionCtx) {
+ RoleModel offlineAccessRole = clientSessionCtx.getClientSession().getRealm().getRole(Constants.OFFLINE_ACCESS_ROLE);
if (offlineAccessRole == null) {
ServicesLogger.LOGGER.roleNotInRealm(Constants.OFFLINE_ACCESS_ROLE);
return false;
}
- return clientSession.getRoles().contains(offlineAccessRole.getId());
+ // Check if offline_access is allowed here. Even through composite roles
+ for (RoleModel role : clientSessionCtx.getRoles()) {
+ if (role.hasRole(offlineAccessRole)) {
+ return true;
+ }
+ }
+
+ return false;
}
private UserSessionModel createOfflineUserSession(UserModel user, UserSessionModel userSession) {
diff --git a/services/src/main/java/org/keycloak/services/migration/DefaultMigrationProvider.java b/services/src/main/java/org/keycloak/services/migration/DefaultMigrationProvider.java
index 0f0a944..747bdad 100755
--- a/services/src/main/java/org/keycloak/services/migration/DefaultMigrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/migration/DefaultMigrationProvider.java
@@ -31,6 +31,7 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.services.managers.RealmManager;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -72,7 +73,7 @@ public class DefaultMigrationProvider implements MigrationProvider {
}
@Override
- public List<ProtocolMapperModel> getBuiltinMappers(String protocol) {
+ public Map<String, ProtocolMapperModel> getBuiltinMappers(String protocol) {
LoginProtocolFactory providerFactory = (LoginProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, protocol);
return providerFactory.getBuiltinMappers();
}
@@ -86,21 +87,10 @@ public class DefaultMigrationProvider implements MigrationProvider {
public void close() {
}
- private static Map<String, ProtocolMapperRepresentation> getAllDefaultMappers(KeycloakSession session) {
- Map<String, ProtocolMapperRepresentation> allMappers = new HashMap<String, ProtocolMapperRepresentation>();
-
- List<ProviderFactory> loginProtocolFactories = session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class);
-
- for (ProviderFactory factory : loginProtocolFactories) {
- LoginProtocolFactory loginProtocolFactory = (LoginProtocolFactory) factory;
- List<ProtocolMapperModel> currentMappers = loginProtocolFactory.getDefaultBuiltinMappers();
- for (ProtocolMapperModel protocolMapper : currentMappers) {
- ProtocolMapperRepresentation rep = ModelToRepresentation.toRepresentation(protocolMapper);
- allMappers.put(protocolMapper.getName(), rep);
- }
- }
-
- return allMappers;
+ // With change to client scopes, there are not default protocolMappers dedicated to single client anymore. Instead, there are default client scopes
+ // and mappers dedicated to those scopes. So returning empty map for now
+ private static Map<String, ProtocolMapperRepresentation> getAllDefaultMappers(KeycloakSession session) {
+ return Collections.emptyMap();
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index a61dd05..6d0b0ca 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -21,13 +21,14 @@ import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.authorization.admin.AuthorizationService;
+import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile;
import org.keycloak.common.util.Time;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
@@ -42,7 +43,7 @@ import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.protocol.ClientInstallationProvider;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.UserRepresentation;
@@ -79,6 +80,7 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -107,6 +109,9 @@ public class ClientResource {
@Context
protected KeycloakApplication keycloak;
+ @Context
+ protected ClientConnection clientConnection;
+
protected KeycloakApplication getKeycloakApplication() {
return keycloak;
}
@@ -294,6 +299,102 @@ public class ClientResource {
return new RoleContainerResource(session, uriInfo, realm, auth, client, adminEvent);
}
+
+ /**
+ * Get default client scopes. Only name and ids are returned.
+ *
+ * @return
+ */
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("default-client-scopes")
+ public List<ClientScopeRepresentation> getDefaultClientScopes() {
+ return getDefaultClientScopes(true);
+ }
+
+ private List<ClientScopeRepresentation> getDefaultClientScopes(boolean defaultScope) {
+ auth.clients().requireView(client);
+
+ List<ClientScopeRepresentation> defaults = new LinkedList<>();
+ for (ClientScopeModel clientScope : client.getClientScopes(defaultScope, true).values()) {
+ ClientScopeRepresentation rep = new ClientScopeRepresentation();
+ rep.setId(clientScope.getId());
+ rep.setName(clientScope.getName());
+ defaults.add(rep);
+ }
+ return defaults;
+ }
+
+
+ @PUT
+ @NoCache
+ @Path("default-client-scopes/{clientScopeId}")
+ public void addDefaultClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ addDefaultClientScope(clientScopeId,true);
+ }
+
+ private void addDefaultClientScope(String clientScopeId, boolean defaultScope) {
+ auth.clients().requireManage(client);
+
+ ClientScopeModel clientScope = realm.getClientScopeById(clientScopeId);
+ if (clientScope == null) {
+ throw new org.jboss.resteasy.spi.NotFoundException("Client scope not found");
+ }
+ client.addClientScope(clientScope, defaultScope);
+
+ adminEvent.operation(OperationType.CREATE).resource(ResourceType.CLIENT).resourcePath(uriInfo).success();
+ }
+
+
+ @DELETE
+ @NoCache
+ @Path("default-client-scopes/{clientScopeId}")
+ public void removeDefaultClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ auth.clients().requireManage(client);
+
+ ClientScopeModel clientScope = realm.getClientScopeById(clientScopeId);
+ if (clientScope == null) {
+ throw new org.jboss.resteasy.spi.NotFoundException("Client scope not found");
+ }
+ client.removeClientScope(clientScope);
+
+ adminEvent.operation(OperationType.DELETE).resource(ResourceType.CLIENT).resourcePath(uriInfo).success();
+ }
+
+
+ /**
+ * Get optional client scopes. Only name and ids are returned.
+ *
+ * @return
+ */
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("optional-client-scopes")
+ public List<ClientScopeRepresentation> getOptionalClientScopes() {
+ return getDefaultClientScopes(false);
+ }
+
+ @PUT
+ @NoCache
+ @Path("optional-client-scopes/{clientScopeId}")
+ public void addOptionalClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ addDefaultClientScope(clientScopeId, false);
+ }
+
+ @DELETE
+ @NoCache
+ @Path("optional-client-scopes/{clientScopeId}")
+ public void removeOptionalClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ removeDefaultClientScope(clientScopeId);
+ }
+
+ @Path("evaluate-scopes")
+ public ClientScopeEvaluateResource clientScopeEvaluateResource() {
+ return new ClientScopeEvaluateResource(session, uriInfo, realm, auth, client, clientConnection);
+ }
+
/**
* Get a user dedicated to the service account
*
@@ -589,30 +690,10 @@ public class ClientResource {
new ClientManager(new RealmManager(session)).clientIdChanged(client, rep.getClientId());
}
- if (rep.isFullScopeAllowed() != null && rep.isFullScopeAllowed().booleanValue() != client.isFullScopeAllowed()) {
+ if (rep.isFullScopeAllowed() != null && rep.isFullScopeAllowed() != client.isFullScopeAllowed()) {
auth.clients().requireManage(client);
}
- if (rep.getClientTemplate() != null) {
- ClientTemplateModel currTemplate = client.getClientTemplate();
- if (currTemplate == null) {
- if (!rep.getClientTemplate().equals(ClientTemplateRepresentation.NONE)) {
- auth.clients().requireManage(client);
- }
- } else if (!rep.getClientTemplate().equals(currTemplate.getName())){
- auth.clients().requireManage(client);
- }
- if ((rep.isUseTemplateConfig() != null && rep.isUseTemplateConfig().booleanValue() != client.useTemplateConfig())
- || (rep.isUseTemplateScope() != null && rep.isUseTemplateScope().booleanValue() != client.useTemplateScope())
- || (rep.isUseTemplateMappers() != null && rep.isUseTemplateMappers().booleanValue() != client.useTemplateMappers())
-
- ) {
- auth.clients().requireManage(client);
- }
- }
-
-
-
RepresentationToModel.updateClient(rep, client);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateResource.java
new file mode 100644
index 0000000..770ac6a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateResource.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.resources.admin;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.common.ClientConnection;
+import org.keycloak.models.AuthenticatedClientSessionModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.ClientSessionContext;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperContainerModel;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.services.Urls;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.AuthenticationSessionManager;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+import org.keycloak.sessions.AuthenticationSessionModel;
+import org.keycloak.sessions.RootAuthenticationSessionModel;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientScopeEvaluateResource {
+
+ protected static final Logger logger = Logger.getLogger(ClientScopeEvaluateResource.class);
+
+ private final RealmModel realm;
+ private final ClientModel client;
+ private final AdminPermissionEvaluator auth;
+
+ private final UriInfo uriInfo;
+ private final KeycloakSession session;
+ private final ClientConnection clientConnection;
+
+ public ClientScopeEvaluateResource(KeycloakSession session, UriInfo uriInfo, RealmModel realm, AdminPermissionEvaluator auth,
+ ClientModel client, ClientConnection clientConnection) {
+ this.uriInfo = uriInfo;
+ this.realm = realm;
+ this.client = client;
+ this.auth = auth;
+ this.session = session;
+ this.clientConnection = clientConnection;
+ }
+
+
+ /**
+ *
+ * @param scopeParam
+ * @param roleContainerId either realm name OR client UUID
+ * @return
+ */
+ @Path("scope-mappings/{roleContainerId}")
+ public ClientScopeEvaluateScopeMappingsResource scopeMappings(@QueryParam("scope") String scopeParam, @PathParam("roleContainerId") String roleContainerId) {
+ auth.clients().requireView(client);
+
+ if (roleContainerId == null) {
+ throw new NotFoundException("No roleContainerId provided");
+ }
+
+ RoleContainerModel roleContainer = roleContainerId.equals(realm.getName()) ? realm : realm.getClientById(roleContainerId);
+ if (roleContainer == null) {
+ throw new NotFoundException("Role Container not found");
+ }
+
+ return new ClientScopeEvaluateScopeMappingsResource(roleContainer, auth, client, scopeParam, session);
+ }
+
+
+ /**
+ * Return list of all protocol mappers, which will be used when generating tokens issued for particular client. This means
+ * protocol mappers assigned to this client directly and protocol mappers assigned to all client scopes of this client.
+ *
+ * @return
+ */
+ @GET
+ @Path("protocol-mappers")
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<ProtocolMapperEvaluationRepresentation> getGrantedProtocolMappers(@QueryParam("scope") String scopeParam) {
+ auth.clients().requireView(client);
+
+ List<ProtocolMapperEvaluationRepresentation> protocolMappers = new LinkedList<>();
+
+ Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client);
+
+ for (ClientScopeModel mapperContainer : clientScopes) {
+ Set<ProtocolMapperModel> currentMappers = mapperContainer.getProtocolMappers();
+ for (ProtocolMapperModel current : currentMappers) {
+ if (current.getProtocol().equals(client.getProtocol())) {
+ ProtocolMapperEvaluationRepresentation rep = new ProtocolMapperEvaluationRepresentation();
+ rep.setMapperId(current.getId());
+ rep.setMapperName(current.getName());
+ rep.setProtocolMapper(current.getProtocolMapper());
+
+ if (mapperContainer.getId().equals(client.getId())) {
+ // Must be this client
+ rep.setContainerId(client.getId());
+ rep.setContainerName("");
+ rep.setContainerType("client");
+ } else {
+ ClientScopeModel clientScope = (ClientScopeModel) mapperContainer;
+ rep.setContainerId(clientScope.getId());
+ rep.setContainerName(clientScope.getName());
+ rep.setContainerType("client-scope");
+ }
+
+ protocolMappers.add(rep);
+ }
+ }
+ }
+
+ return protocolMappers;
+ }
+
+
+
+ /**
+ * Create JSON with payload of example access token
+ *
+ * @return
+ */
+ @GET
+ @Path("generate-example-access-token")
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public AccessToken generateExampleAccessToken(@QueryParam("scope") String scopeParam, @QueryParam("userId") String userId) {
+ auth.clients().requireView(client);
+
+ if (userId == null) {
+ throw new NotFoundException("No userId provided");
+ }
+
+ UserModel user = session.users().getUserById(userId, realm);
+ if (user == null) {
+ throw new NotFoundException("No user found");
+ }
+
+ logger.debugf("generateExampleAccessToken invoked. User: %s, Scope param: %s", user.getUsername(), scopeParam);
+
+ AccessToken token = generateToken(user, scopeParam);
+ return token;
+ }
+
+
+ private AccessToken generateToken(UserModel user, String scopeParam) {
+ AuthenticationSessionModel authSession = null;
+ UserSessionModel userSession = null;
+ AuthenticationSessionManager authSessionManager = new AuthenticationSessionManager(session);
+
+ try {
+ RootAuthenticationSessionModel rootAuthSession = authSessionManager.createAuthenticationSession(realm, false);
+ authSession = rootAuthSession.createAuthenticationSession(client);
+
+ authSession.setAuthenticatedUser(user);
+ authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
+ authSession.setClientNote(OIDCLoginProtocol.SCOPE_PARAM, scopeParam);
+
+ userSession = session.sessions().createUserSession(authSession.getParentSession().getId(), realm, user, user.getUsername(),
+ clientConnection.getRemoteAddr(), "example-auth", false, null, null);
+
+ AuthenticationManager.setClientScopesInSession(authSession);
+ ClientSessionContext clientSessionCtx = TokenManager.attachAuthenticationSession(session, userSession, authSession);
+
+ TokenManager tokenManager = new TokenManager();
+
+ TokenManager.AccessTokenResponseBuilder responseBuilder = tokenManager.responseBuilder(realm, client, null, session, userSession, clientSessionCtx)
+ .generateAccessToken();
+
+ return responseBuilder.getAccessToken();
+
+ } finally {
+ if (authSession != null) {
+ authSessionManager.removeAuthenticationSession(realm, authSession, false);
+ }
+ if (userSession != null) {
+ session.sessions().removeUserSession(realm, userSession);
+ }
+ }
+ }
+
+
+ public static class ProtocolMapperEvaluationRepresentation {
+
+ @JsonProperty("mapperId")
+ private String mapperId;
+
+ @JsonProperty("mapperName")
+ private String mapperName;
+
+ @JsonProperty("containerId")
+ private String containerId;
+
+ @JsonProperty("containerName")
+ private String containerName;
+
+ @JsonProperty("containerType")
+ private String containerType;
+
+ @JsonProperty("protocolMapper")
+ private String protocolMapper;
+
+ public String getMapperId() {
+ return mapperId;
+ }
+
+ public void setMapperId(String mapperId) {
+ this.mapperId = mapperId;
+ }
+
+ public String getMapperName() {
+ return mapperName;
+ }
+
+ public void setMapperName(String mapperName) {
+ this.mapperName = mapperName;
+ }
+
+ public String getContainerId() {
+ return containerId;
+ }
+
+ public void setContainerId(String containerId) {
+ this.containerId = containerId;
+ }
+
+ public String getContainerName() {
+ return containerName;
+ }
+
+ public void setContainerName(String containerName) {
+ this.containerName = containerName;
+ }
+
+ public String getContainerType() {
+ return containerType;
+ }
+
+ public void setContainerType(String containerType) {
+ this.containerType = containerType;
+ }
+
+ public String getProtocolMapper() {
+ return protocolMapper;
+ }
+
+ public void setProtocolMapper(String protocolMapper) {
+ this.protocolMapper = protocolMapper;
+ }
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java
new file mode 100644
index 0000000..8e514b4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientScopeEvaluateScopeMappingsResource.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.resources.admin;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RoleContainerModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.ScopeContainerModel;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientScopeEvaluateScopeMappingsResource {
+
+ private final RoleContainerModel roleContainer;
+ private final AdminPermissionEvaluator auth;
+ private final ClientModel client;
+ private final String scopeParam;
+ private final KeycloakSession session;
+
+
+ public ClientScopeEvaluateScopeMappingsResource(RoleContainerModel roleContainer, AdminPermissionEvaluator auth, ClientModel client,
+ String scopeParam, KeycloakSession session) {
+ this.roleContainer = roleContainer;
+ this.auth = auth;
+ this.client = client;
+ this.scopeParam = scopeParam;
+ this.session = session;
+ }
+
+
+ /**
+ * Get effective scope mapping of all roles of particular role container, which this client is defacto allowed to have in the accessToken issued for him.
+ *
+ * This contains scope mappings, which this client has directly, as well as scope mappings, which are granted to all client scopes,
+ * which are linked with this client.
+ *
+ * @return
+ */
+ @Path("/granted")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ public List<RoleRepresentation> getGrantedScopeMappings() {
+ return getGrantedRoles().stream().map((RoleModel role) -> {
+
+ return ModelToRepresentation.toRepresentation(role);
+
+ }).collect(Collectors.toList());
+ }
+
+
+ /**
+ * Get roles, which this client doesn't have scope for and can't have them in the accessToken issued for him. Defacto all the
+ * other roles of particular role container, which are not in {@link #getGrantedScopeMappings()}
+ *
+ * @return
+ */
+ @Path("/not-granted")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @NoCache
+ public List<RoleRepresentation> getNotGrantedScopeMappings() {
+ List<RoleModel> grantedRoles = getGrantedRoles();
+
+ return roleContainer.getRoles().stream().filter((RoleModel role) -> {
+
+ return !grantedRoles.contains(role);
+
+ }).map((RoleModel role) -> {
+
+ return ModelToRepresentation.toRepresentation(role);
+
+ }).collect(Collectors.toList());
+ }
+
+
+
+
+ private List<RoleModel> getGrantedRoles() {
+ if (client.isFullScopeAllowed()) {
+ return new LinkedList<>(roleContainer.getRoles());
+ }
+
+ Set<ClientScopeModel> clientScopes = TokenManager.getRequestedClientScopes(scopeParam, client);
+
+ List<RoleModel> result = new LinkedList<>();
+
+ for (RoleModel role : roleContainer.getRoles()) {
+ if (!auth.roles().canView(role)) continue;
+
+ for (ScopeContainerModel scopeContainer : clientScopes) {
+ if (scopeContainer.hasScope(role)) {
+ result.add(role);
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
index 51057df..297bf97 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java
@@ -269,7 +269,7 @@ public class ServerInfoAdminResource {
for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) {
LoginProtocolFactory factory = (LoginProtocolFactory)p;
List<ProtocolMapperRepresentation> mappers = new LinkedList<>();
- for (ProtocolMapperModel mapper : factory.getBuiltinMappers()) {
+ for (ProtocolMapperModel mapper : factory.getBuiltinMappers().values()) {
mappers.add(ModelToRepresentation.toRepresentation(mapper));
}
info.getBuiltinProtocolMappers().put(p.getId(), mappers);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
index 3b64a27..100c06d 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissionEvaluator.java
@@ -17,7 +17,7 @@
package org.keycloak.services.resources.admin.permissions;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import java.util.Map;
@@ -30,29 +30,29 @@ public interface ClientPermissionEvaluator {
void setPermissionsEnabled(ClientModel client, boolean enable);
- void requireListTemplates();
+ void requireListClientScopes();
boolean canManage();
void requireManage();
- boolean canManageTemplates();
+ boolean canManageClientScopes();
- void requireManageTemplates();
+ void requireManageClientScopes();
boolean canView();
boolean canList();
- boolean canViewTemplates();
+ boolean canViewClientScopes();
void requireList();
- boolean canListTemplates();
+ boolean canListClientScopes();
void requireView();
- void requireViewTemplates();
+ void requireViewClientScopes();
boolean canManage(ClientModel client);
@@ -66,13 +66,13 @@ public interface ClientPermissionEvaluator {
void requireView(ClientModel client);
- boolean canManage(ClientTemplateModel template);
+ boolean canManage(ClientScopeModel clientScope);
- void requireManage(ClientTemplateModel template);
+ void requireManage(ClientScopeModel clientScope);
- boolean canView(ClientTemplateModel template);
+ boolean canView(ClientScopeModel clientScope);
- void requireView(ClientTemplateModel template);
+ void requireView(ClientScopeModel clientScope);
boolean canMapRoles(ClientModel client);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java
index 00726e4..26ec3b5 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/permissions/ClientPermissions.java
@@ -27,7 +27,7 @@ import org.keycloak.authorization.model.Scope;
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.services.ForbiddenException;
@@ -235,13 +235,13 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
}
@Override
- public boolean canListTemplates() {
+ public boolean canListClientScopes() {
return root.hasAnyAdminRole();
}
@Override
- public void requireListTemplates() {
- if (!canListTemplates()) {
+ public void requireListClientScopes() {
+ if (!canListClientScopes()) {
throw new ForbiddenException();
}
}
@@ -460,51 +460,51 @@ class ClientPermissions implements ClientPermissionEvaluator, ClientPermissionM
}
}
- // templates
+ // client scopes
@Override
- public boolean canViewTemplates() {
+ public boolean canViewClientScopes() {
return canView();
}
@Override
- public boolean canManageTemplates() {
+ public boolean canManageClientScopes() {
return canManageClientsDefault();
}
@Override
- public void requireManageTemplates() {
- if (!canManageTemplates()) {
+ public void requireManageClientScopes() {
+ if (!canManageClientScopes()) {
throw new ForbiddenException();
}
}
@Override
- public void requireViewTemplates() {
- if (!canViewTemplates()) {
+ public void requireViewClientScopes() {
+ if (!canViewClientScopes()) {
throw new ForbiddenException();
}
}
@Override
- public boolean canManage(ClientTemplateModel template) {
+ public boolean canManage(ClientScopeModel clientScope) {
return canManageClientsDefault();
}
@Override
- public void requireManage(ClientTemplateModel template) {
- if (!canManage(template)) {
+ public void requireManage(ClientScopeModel clientScope) {
+ if (!canManage(clientScope)) {
throw new ForbiddenException();
}
}
@Override
- public boolean canView(ClientTemplateModel template) {
+ public boolean canView(ClientScopeModel clientScope) {
return canViewClientDefault();
}
@Override
- public void requireView(ClientTemplateModel template) {
- if (!canView(template)) {
+ public void requireView(ClientScopeModel clientScope) {
+ if (!canView(clientScope)) {
throw new ForbiddenException();
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 41efff8..dee4147 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -24,6 +24,8 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
import org.keycloak.KeyPairVerifier;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
@@ -101,7 +103,6 @@ import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
@@ -194,15 +195,117 @@ public class RealmAdminResource {
}
/**
- * Base path for managing client templates under this realm.
+ * This endpoint is deprecated. It's here just because of backwards compatibility. Use {@link #getClientScopes()} instead
*
* @return
*/
+ @Deprecated
@Path("client-templates")
- public ClientTemplatesResource getClientTemplates() {
- ClientTemplatesResource clientsResource = new ClientTemplatesResource(realm, auth, adminEvent);
- ResteasyProviderFactory.getInstance().injectProperties(clientsResource);
- return clientsResource;
+ public ClientScopesResource getClientTemplates() {
+ return getClientScopes();
+ }
+
+ /**
+ * Base path for managing client scopes under this realm.
+ *
+ * @return
+ */
+ @Path("client-scopes")
+ public ClientScopesResource getClientScopes() {
+ ClientScopesResource clientScopesResource = new ClientScopesResource(realm, auth, adminEvent);
+ ResteasyProviderFactory.getInstance().injectProperties(clientScopesResource);
+ return clientScopesResource;
+ }
+
+
+ /**
+ * Get realm default client scopes. Only name and ids are returned.
+ *
+ * @return
+ */
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("default-default-client-scopes")
+ public List<ClientScopeRepresentation> getDefaultDefaultClientScopes() {
+ return getDefaultClientScopes(true);
+ }
+
+ private List<ClientScopeRepresentation> getDefaultClientScopes(boolean defaultScope) {
+ auth.clients().requireViewClientScopes();
+
+ List<ClientScopeRepresentation> defaults = new LinkedList<>();
+ for (ClientScopeModel clientScope : realm.getDefaultClientScopes(defaultScope)) {
+ ClientScopeRepresentation rep = new ClientScopeRepresentation();
+ rep.setId(clientScope.getId());
+ rep.setName(clientScope.getName());
+ defaults.add(rep);
+ }
+ return defaults;
+ }
+
+
+ @PUT
+ @NoCache
+ @Path("default-default-client-scopes/{clientScopeId}")
+ public void addDefaultDefaultClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ addDefaultClientScope(clientScopeId,true);
+ }
+
+ private void addDefaultClientScope(String clientScopeId, boolean defaultScope) {
+ auth.clients().requireManageClientScopes();
+
+ ClientScopeModel clientScope = realm.getClientScopeById(clientScopeId);
+ if (clientScope == null) {
+ throw new NotFoundException("Client scope not found");
+ }
+ realm.addDefaultClientScope(clientScope, defaultScope);
+
+ adminEvent.operation(OperationType.CREATE).resource(ResourceType.CLIENT_SCOPE).resourcePath(uriInfo).success();
+ }
+
+
+ @DELETE
+ @NoCache
+ @Path("default-default-client-scopes/{clientScopeId}")
+ public void removeDefaultDefaultClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ auth.clients().requireManageClientScopes();
+
+ ClientScopeModel clientScope = realm.getClientScopeById(clientScopeId);
+ if (clientScope == null) {
+ throw new NotFoundException("Client scope not found");
+ }
+ realm.removeDefaultClientScope(clientScope);
+
+ adminEvent.operation(OperationType.DELETE).resource(ResourceType.CLIENT_SCOPE).resourcePath(uriInfo).success();
+ }
+
+
+ /**
+ * Get realm optional client scopes. Only name and ids are returned.
+ *
+ * @return
+ */
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("default-optional-client-scopes")
+ public List<ClientScopeRepresentation> getDefaultOptionalClientScopes() {
+ return getDefaultClientScopes(false);
+ }
+
+ @PUT
+ @NoCache
+ @Path("default-optional-client-scopes/{clientScopeId}")
+ public void addDefaultOptionalClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ addDefaultClientScope(clientScopeId, false);
+ }
+
+ @DELETE
+ @NoCache
+ @Path("default-optional-client-scopes/{clientScopeId}")
+ public void removeDefaultOptionalClientScope(@PathParam("clientScopeId") String clientScopeId) {
+ removeDefaultDefaultClientScope(clientScopeId);
}
/**
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
index c0914f3..addf33c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
@@ -120,8 +120,6 @@ public class RoleContainerResource extends RoleResource {
try {
RoleModel role = roleContainer.addRole(rep.getName());
role.setDescription(rep.getDescription());
- boolean scopeParamRequired = rep.isScopeParamRequired()==null ? false : rep.isScopeParamRequired();
- role.setScopeParamRequired(scopeParamRequired);
rep.setId(role.getId());
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
index 0161ee6..3af52c1 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
@@ -58,7 +58,6 @@ public abstract class RoleResource {
protected void updateRole(RoleRepresentation rep, RoleModel role) {
role.setName(rep.getName());
role.setDescription(rep.getDescription());
- if (rep.isScopeParamRequired() != null) role.setScopeParamRequired(rep.isScopeParamRequired());
}
protected void addComposites(AdminPermissionEvaluator auth, AdminEventBuilder adminEvent, UriInfo uriInfo, List<RoleRepresentation> roles, RoleModel role) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
index eaf7285..b35a7fa 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserResource.java
@@ -447,9 +447,7 @@ public class UserResource {
Map<String, Object> currentRep = new HashMap<>();
currentRep.put("clientId", client.getClientId());
- currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers()));
- currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles()));
- currentRep.put("grantedClientRoles", (rep==null ? Collections.emptyMap() : rep.getGrantedClientRoles()));
+ currentRep.put("grantedClientScopes", (rep==null ? Collections.emptyList() : rep.getGrantedClientScopes()));
currentRep.put("createdDate", (rep==null ? null : rep.getCreatedDate()));
currentRep.put("lastUpdatedDate", (rep==null ? null : rep.getLastUpdatedDate()));
diff --git a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
index 07db430..0b61c21 100755
--- a/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
+++ b/services/src/main/java/org/keycloak/services/resources/IdentityBrokerService.java
@@ -267,10 +267,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
ClientModel accountService = this.realmModel.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
if (!accountService.getId().equals(client.getId())) {
RoleModel manageAccountRole = accountService.getRole(AccountRoles.MANAGE_ACCOUNT);
+ Set<RoleModel> userAccountRoles = cookieResult.getUser().getClientRoleMappings(accountService);
- if (!clientSession.getRoles().contains(manageAccountRole.getId())) {
+ if (!userAccountRoles.contains(manageAccountRole)) {
RoleModel linkRole = accountService.getRole(AccountRoles.MANAGE_ACCOUNT_LINKS);
- if (!clientSession.getRoles().contains(linkRole.getId())) {
+ if (!userAccountRoles.contains(linkRole)) {
event.error(Errors.NOT_ALLOWED);
UriBuilder builder = UriBuilder.fromUri(redirectUri)
.queryParam(errorParam, Errors.NOT_ALLOWED)
@@ -806,7 +807,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
logger.debugf("Performing local authentication for user [%s].", federatedUser);
}
- AuthenticationManager.setRolesAndMappersInSession(authSession);
+ AuthenticationManager.setClientScopesInSession(authSession);
String nextRequiredAction = AuthenticationManager.nextRequiredAction(session, authSession, clientConnection, request, uriInfo, event);
if (nextRequiredAction != null) {
@@ -913,7 +914,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
context.getIdp().authenticationFinished(authSession, context);
- AuthenticationManager.setRolesAndMappersInSession(authSession);
+ AuthenticationManager.setClientScopesInSession(authSession);
TokenManager.attachAuthenticationSession(session, userSession, authSession);
if (isDebugEnabled()) {
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 1c9ff23..f6be313 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -41,6 +41,8 @@ import org.keycloak.models.ActionTokenKeyModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
@@ -50,6 +52,7 @@ import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.AuthenticationFlowResolver;
import org.keycloak.models.utils.FormMessage;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.SystemClientUtil;
import org.keycloak.protocol.AuthorizationEndpointBase;
import org.keycloak.protocol.LoginProtocol;
@@ -826,21 +829,23 @@ public class LoginActionsService {
grantedConsent = new UserConsentModel(client);
session.users().addConsent(realm, user.getId(), grantedConsent);
}
- for (RoleModel role : ClientSessionCode.getRequestedRoles(authSession, realm)) {
- grantedConsent.addGrantedRole(role);
- }
- for (ProtocolMapperModel protocolMapper : ClientSessionCode.getRequestedProtocolMappers(authSession.getProtocolMappers(), client)) {
- if (protocolMapper.isConsentRequired() && protocolMapper.getConsentText() != null) {
- grantedConsent.addGrantedProtocolMapper(protocolMapper);
+
+ for (String clientScopeId : authSession.getClientScopes()) {
+ ClientScopeModel clientScope = KeycloakModelUtils.findClientScopeById(realm, clientScopeId);
+ if (clientScope != null) {
+ grantedConsent.addGrantedClientScope(clientScope);
+ } else {
+ logger.warn("Client scope with ID '%s' not found");
}
}
+
session.users().updateConsent(realm, user.getId(), grantedConsent);
event.detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED);
event.success();
- AuthenticatedClientSessionModel clientSession = AuthenticationProcessor.attachSession(authSession, null, session, realm, clientConnection, event);
- return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, clientSession.getUserSession(), clientSession, request, uriInfo, clientConnection, event, authSession.getProtocol());
+ ClientSessionContext clientSessionCtx = AuthenticationProcessor.attachSession(authSession, null, session, realm, clientConnection, event);
+ return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm, clientSessionCtx.getClientSession().getUserSession(), clientSessionCtx, request, uriInfo, clientConnection, event, authSession.getProtocol());
}
private void initLoginEvent(AuthenticationSessionModel authSession) {
diff --git a/services/src/main/java/org/keycloak/services/util/DefaultClientSessionContext.java b/services/src/main/java/org/keycloak/services/util/DefaultClientSessionContext.java
new file mode 100644
index 0000000..8211698
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/util/DefaultClientSessionContext.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.models.AuthenticatedClientSessionModel;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.ClientSessionContext;
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.util.TokenUtil;
+
+/**
+ * Not thread safe. It's per-request object
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class DefaultClientSessionContext implements ClientSessionContext {
+
+ private static Logger logger = Logger.getLogger(DefaultClientSessionContext.class);
+
+ private final AuthenticatedClientSessionModel clientSession;
+ private final Set<String> clientScopeIds;
+
+ private Set<ClientScopeModel> clientScopes;
+ private Set<RoleModel> roles;
+ private Set<ProtocolMapperModel> protocolMappers;
+
+ private DefaultClientSessionContext(AuthenticatedClientSessionModel clientSession, Set<String> clientScopeIds) {
+ this.clientSession = clientSession;
+ this.clientScopeIds = clientScopeIds;
+ }
+
+
+ /**
+ * Useful if we want to "re-compute" client scopes based on the scope parameter
+ */
+ public static DefaultClientSessionContext fromClientSessionScopeParameter(AuthenticatedClientSessionModel clientSession) {
+ return fromClientSessionAndScopeParameter(clientSession, clientSession.getNote(OAuth2Constants.SCOPE));
+ }
+
+
+ public static DefaultClientSessionContext fromClientSessionAndScopeParameter(AuthenticatedClientSessionModel clientSession, String scopeParam) {
+ Set<ClientScopeModel> requestedClientScopes = TokenManager.getRequestedClientScopes(scopeParam, clientSession.getClient());
+ return fromClientSessionAndClientScopes(clientSession, requestedClientScopes);
+ }
+
+
+ public static DefaultClientSessionContext fromClientSessionAndClientScopeIds(AuthenticatedClientSessionModel clientSession, Set<String> clientScopeIds) {
+ return new DefaultClientSessionContext(clientSession, clientScopeIds);
+ }
+
+
+ public static DefaultClientSessionContext fromClientSessionAndClientScopes(AuthenticatedClientSessionModel clientSession, Set<ClientScopeModel> clientScopes) {
+ Set<String> clientScopeIds = new HashSet<>();
+ for (ClientScopeModel clientScope : clientScopes) {
+ clientScopeIds.add(clientScope.getId());
+ }
+
+ DefaultClientSessionContext ctx = new DefaultClientSessionContext(clientSession, clientScopeIds);
+ ctx.clientScopes = new HashSet<>(clientScopes);
+ return ctx;
+ }
+
+
+ @Override
+ public AuthenticatedClientSessionModel getClientSession() {
+ return clientSession;
+ }
+
+
+ @Override
+ public Set<String> getClientScopeIds() {
+ return clientScopeIds;
+ }
+
+
+ @Override
+ public Set<ClientScopeModel> getClientScopes() {
+ // Load client scopes if not yet present
+ if (clientScopes == null) {
+ clientScopes = loadClientScopes();
+ }
+ return clientScopes;
+ }
+
+
+ @Override
+ public Set<RoleModel> getRoles() {
+ // Load roles if not yet present
+ if (roles == null) {
+ roles = loadRoles();
+ }
+ return roles;
+ }
+
+
+ @Override
+ public Set<ProtocolMapperModel> getProtocolMappers() {
+ // Load roles if not yet present
+ if (protocolMappers == null) {
+ protocolMappers = loadProtocolMappers();
+ }
+ return protocolMappers;
+ }
+
+
+ @Override
+ public String getScopeString() {
+ StringBuilder builder = new StringBuilder();
+
+ // Add both default and optional scopes to scope parameter. Don't add client itself
+ boolean first = true;
+ for (ClientScopeModel clientScope : getClientScopes()) {
+ if (clientScope instanceof ClientModel) {
+ continue;
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ builder.append(" ");
+ }
+ builder.append(clientScope.getName());
+ }
+
+ String scopeParam = builder.toString();
+
+ // See if "openid" scope is requested
+ String scopeSent = clientSession.getNote(OAuth2Constants.SCOPE);
+ if (TokenUtil.isOIDCRequest(scopeSent)) {
+ scopeParam = TokenUtil.attachOIDCScope(scopeParam);
+ }
+
+ return scopeParam;
+ }
+
+
+ // Loading data
+
+ private Set<ClientScopeModel> loadClientScopes() {
+ Set<ClientScopeModel> clientScopes = new HashSet<>();
+ for (String scopeId : clientScopeIds) {
+ ClientScopeModel clientScope = KeycloakModelUtils.findClientScopeById(clientSession.getClient().getRealm(), scopeId);
+ if (clientScope != null) {
+ clientScopes.add(clientScope);
+ }
+ }
+ return clientScopes;
+ }
+
+
+ private Set<RoleModel> loadRoles() {
+ UserModel user = clientSession.getUserSession().getUser();
+ ClientModel client = clientSession.getClient();
+
+ Set<ClientScopeModel> clientScopes = getClientScopes();
+
+ return TokenManager.getAccess(user, client, clientScopes);
+ }
+
+
+ private Set<ProtocolMapperModel> loadProtocolMappers() {
+ Set<ClientScopeModel> clientScopes = getClientScopes();
+ String protocol = clientSession.getClient().getProtocol();
+
+ // Being rather defensive. But protocol should normally always be there
+ if (protocol == null) {
+ logger.warnf("Client '%s' doesn't have protocol set. Fallback to openid-connect. Please fix client configuration", clientSession.getClient().getClientId());
+ protocol = OIDCLoginProtocol.LOGIN_PROTOCOL;
+ }
+
+ Set<ProtocolMapperModel> protocolMappers = new HashSet<>();
+ for (ClientScopeModel clientScope : clientScopes) {
+ Set<ProtocolMapperModel> currentMappers = clientScope.getProtocolMappers();
+ for (ProtocolMapperModel currentMapper : currentMappers) {
+ if (protocol.equals(currentMapper.getProtocol())) {
+ protocolMappers.add(currentMapper);
+ }
+ }
+ }
+
+ return protocolMappers;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/storage/UserStorageManager.java b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
index 4801553..93b7f72 100755
--- a/services/src/main/java/org/keycloak/storage/UserStorageManager.java
+++ b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
@@ -22,6 +22,7 @@ import org.keycloak.common.util.reflections.Types;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@@ -696,6 +697,12 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo
}
@Override
+ public void preRemove(ClientScopeModel clientScope) {
+ localStorage().preRemove(clientScope);
+ if (getFederatedStorage() != null) getFederatedStorage().preRemove(clientScope);
+ }
+
+ @Override
public void preRemove(RealmModel realm, ComponentModel component) {
if (component.getProviderType().equals(ClientStorageProvider.class.getName())) {
localStorage().preRemove(realm, component);
diff --git a/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
index 44171ae..00623ab 100644
--- a/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
+++ b/services/src/main/java/org/keycloak/theme/DefaultThemeSelectorProvider.java
@@ -3,7 +3,6 @@ package org.keycloak.theme;
import org.keycloak.Config;
import org.keycloak.common.Version;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
import org.keycloak.models.KeycloakSession;
public class DefaultThemeSelectorProvider implements ThemeSelectorProvider {
@@ -28,16 +27,9 @@ public class DefaultThemeSelectorProvider implements ThemeSelectorProvider {
ClientModel client = session.getContext().getClient();
if (client != null) {
name = client.getAttribute(LOGIN_THEME_KEY);
-
- if (name == null || name.isEmpty()) {
- ClientTemplateModel clientTemplate = client.getClientTemplate();
- if (clientTemplate != null) {
- name = clientTemplate.getAttribute(LOGIN_THEME_KEY);
- }
- }
}
- if (name == null) {
+ if (name == null || name.isEmpty()) {
name = session.getContext().getRealm().getLoginTheme();
}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory
index 78a8cc9..5b3a11f 100644
--- a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyFactory
@@ -18,7 +18,7 @@
org.keycloak.services.clientregistration.policy.impl.TrustedHostClientRegistrationPolicyFactory
org.keycloak.services.clientregistration.policy.impl.ConsentRequiredClientRegistrationPolicyFactory
org.keycloak.services.clientregistration.policy.impl.ProtocolMappersClientRegistrationPolicyFactory
-org.keycloak.services.clientregistration.policy.impl.ClientTemplatesClientRegistrationPolicyFactory
+org.keycloak.services.clientregistration.policy.impl.ClientScopesClientRegistrationPolicyFactory
org.keycloak.services.clientregistration.policy.impl.ScopeClientRegistrationPolicyFactory
org.keycloak.services.clientregistration.policy.impl.ClientDisabledClientRegistrationPolicyFactory
org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicyFactory
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java
index 672cb1e..e53124a 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/HardcodedClientStorageProvider.java
@@ -17,11 +17,13 @@
package org.keycloak.testsuite.federation;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.client.AbstractReadOnlyClientStorageAdapter;
import org.keycloak.storage.client.ClientLookupProvider;
@@ -215,23 +217,13 @@ public class HardcodedClientStorageProvider implements ClientStorageProvider, Cl
}
@Override
- public ClientTemplateModel getClientTemplate() {
- return null;
- }
-
- @Override
- public boolean useTemplateScope() {
- return false;
- }
-
- @Override
- public boolean useTemplateMappers() {
- return false;
- }
-
- @Override
- public boolean useTemplateConfig() {
- return false;
+ public Map<String, ClientScopeModel> getClientScopes(boolean defaultScope, boolean filterByProtocol) {
+ if (defaultScope) {
+ return Collections.emptyMap();
+ } else {
+ ClientScopeModel offlineScope = KeycloakModelUtils.getClientScopeByName(realm, "offline_access");
+ return Collections.singletonMap("offline_access", offlineScope);
+ }
}
@Override
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
index 56f21ab..208ae91 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
@@ -19,10 +19,12 @@ package org.keycloak.testsuite.admin;
import org.jboss.logging.Logger;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.KeysMetadataRepresentation;
@@ -111,6 +113,15 @@ public class ApiUtil {
return null;
}
+ public static ClientScopeResource findClientScopeByName(RealmResource realm, String clientScopeName) {
+ for (ClientScopeRepresentation clientScope : realm.clientScopes().findAll()) {
+ if (clientScopeName.equals(clientScope.getName())) {
+ return realm.clientScopes().get(clientScope.getId());
+ }
+ }
+ return null;
+ }
+
public static RoleResource findRealmRoleByName(RealmResource realm, String role) {
return realm.roles().get(role);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
index 8c647cc..bb818ec 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
@@ -80,24 +80,15 @@ public class AccountApplicationsPage extends AbstractAccountPage {
}
break;
case 3:
- rolesStr = col.getText();
- if (rolesStr.isEmpty()) break;
- roles = rolesStr.split(",");
- for (String role : roles) {
- role = role.trim();
- currentEntry.addGrantedRole(role);
+ String clientScopesStr = col.getText();
+ if (clientScopesStr.isEmpty()) break;
+ String[] clientScopes = clientScopesStr.split(",");
+ for (String clientScope : clientScopes) {
+ clientScope = clientScope.trim();
+ currentEntry.addGrantedClientScope(clientScope);
}
break;
case 4:
- String protMappersStr = col.getText();
- if (protMappersStr.isEmpty()) break;
- String[] protMappers = protMappersStr.split(",");
- for (String protMapper : protMappers) {
- protMapper = protMapper.trim();
- currentEntry.addMapper(protMapper);
- }
- break;
- case 5:
String additionalGrant = col.getText();
if (additionalGrant.isEmpty()) break;
String[] grants = additionalGrant.split(",");
@@ -116,8 +107,7 @@ public class AccountApplicationsPage extends AbstractAccountPage {
public static class AppEntry {
private final List<String> rolesAvailable = new ArrayList<String>();
- private final List<String> rolesGranted = new ArrayList<String>();
- private final List<String> protocolMappersGranted = new ArrayList<String>();
+ private final List<String> clientScopesGranted = new ArrayList<String>();
private final List<String> additionalGrants = new ArrayList<>();
private String href = null;
@@ -125,12 +115,8 @@ public class AccountApplicationsPage extends AbstractAccountPage {
rolesAvailable.add(role);
}
- private void addGrantedRole(String role) {
- rolesGranted.add(role);
- }
-
- private void addMapper(String protocolMapper) {
- protocolMappersGranted.add(protocolMapper);
+ private void addGrantedClientScope(String clientScope) {
+ clientScopesGranted.add(clientScope);
}
private void addAdditionalGrant(String grant) {
@@ -145,16 +131,12 @@ public class AccountApplicationsPage extends AbstractAccountPage {
return this.href;
}
- public List<String> getRolesGranted() {
- return rolesGranted;
- }
-
public List<String> getRolesAvailable() {
return rolesAvailable;
}
- public List<String> getProtocolMappersGranted() {
- return protocolMappersGranted;
+ public List<String> getClientScopesGranted() {
+ return clientScopesGranted;
}
public List<String> getAdditionalGrants() {
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
index 059b398..a64df88 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/OAuthGrantPage.java
@@ -16,6 +16,11 @@
*/
package org.keycloak.testsuite.pages;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
@@ -24,6 +29,13 @@ import org.openqa.selenium.support.FindBy;
*/
public class OAuthGrantPage extends LanguageComboboxAwarePage {
+ // Locale-resolved built-in client scope consents
+ public static final String PROFILE_CONSENT_TEXT = "User profile";
+ public static final String EMAIL_CONSENT_TEXT = "Email address";
+ public static final String ADDRESS_CONSENT_TEXT = "Address";
+ public static final String PHONE_CONSENT_TEXT = "Phone number";
+ public static final String OFFLINE_ACCESS_CONSENT_TEXT = "Offline Access";
+
@FindBy(css = "input[name=\"accept\"]")
private WebElement acceptButton;
@FindBy(css = "input[name=\"cancel\"]")
@@ -47,4 +59,23 @@ public class OAuthGrantPage extends LanguageComboboxAwarePage {
public void open() {
}
+ public List<String> getDisplayedGrants() {
+ List<String> table = new LinkedList<>();
+ WebElement divKcOauth = driver.findElement(By.id("kc-oauth"));
+ for (WebElement li : divKcOauth.findElements(By.tagName("li"))) {
+ WebElement span = li.findElement(By.tagName("span"));
+ table.add(span.getText());
+ }
+ return table;
+ }
+
+
+ public void assertGrants(String... grants) {
+ List<String> displayed = getDisplayedGrants();
+ Assert.assertEquals(displayed.size(), grants.length);
+ for (String grant : grants) {
+ Assert.assertTrue("Requested grant " + grant + " not present. Displayed grants: " + displayed, displayed.contains(grant));
+ }
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java
index ee24b06..242441c 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/RequiredConsentBuilder.java
@@ -59,6 +59,7 @@ public class RequiredConsentBuilder implements Step {
assertThat(currentResponse, statusCodeIsHC(Response.Status.OK));
String consentPageText = EntityUtils.toString(currentResponse.getEntity(), "UTF-8");
assertThat(consentPageText, containsString("consent"));
+ assertThat(consentPageText, containsString("My Roles")); // Corresponding to role_list default SAML client scope
return handleConsentPage(consentPageText, currentURI);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java
index 192712e..76e37b0 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/TestCleanup.java
@@ -36,6 +36,7 @@ public class TestCleanup {
private static final String USER_IDS = "USER_IDS";
private static final String COMPONENT_IDS = "COMPONENT_IDS";
private static final String CLIENT_UUIDS = "CLIENT_UUIDS";
+ private static final String CLIENT_SCOPE_IDS = "CLIENT_SCOPE_IDS";
private static final String ROLE_IDS = "ROLE_IDS";
private static final String GROUP_IDS = "GROUP_IDS";
private static final String AUTH_FLOW_IDS = "AUTH_FLOW_IDS";
@@ -74,6 +75,11 @@ public class TestCleanup {
}
+ public void addClientScopeId(String clientScopeId) {
+ entities.add(CLIENT_SCOPE_IDS, clientScopeId);
+ }
+
+
public void addRoleId(String roleId) {
entities.add(ROLE_IDS, roleId);
}
@@ -141,6 +147,18 @@ public class TestCleanup {
}
}
+ // Client scopes should be after clients
+ List<String> clientScopeIds = entities.get(CLIENT_SCOPE_IDS);
+ if (clientScopeIds != null) {
+ for (String clientScopeId : clientScopeIds) {
+ try {
+ realm.clientScopes().get(clientScopeId).remove();
+ } catch (NotFoundException nfe) {
+ // Idp might be already deleted in the test
+ }
+ }
+ }
+
List<String> roleIds = entities.get(ROLE_IDS);
if (roleIds != null) {
for (String roleId : roleIds) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java
index 7403073..d21dcc6 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountBrokerTest.java
@@ -30,9 +30,12 @@ import org.keycloak.testsuite.broker.KcOidcBrokerConfiguration;
import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.UserBuilder;
+import org.keycloak.testsuite.util.WaitUtils;
import org.openqa.selenium.WebElement;
import javax.ws.rs.core.Response;
+
+import java.util.Collections;
import java.util.List;
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
@@ -88,6 +91,9 @@ public class AccountBrokerTest extends AbstractBaseBrokerTest {
for (ClientRepresentation client : clients) {
log.debug("adding client " + client.getName() + " to realm " + bc.providerRealmName());
+ // Remove default client scopes for this test
+ client.setDefaultClientScopes(Collections.emptyList());
+
providerRealm.clients().create(client).close();
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java
index a942ec4..76f1b06 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountFormServiceTest.java
@@ -990,46 +990,43 @@ public class AccountFormServiceTest extends AbstractTestRealmKeycloakTest {
Assert.assertTrue(applicationsPage.isCurrent());
Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
- Assert.assertThat(apps.keySet(), containsInAnyOrder("root-url-client", "Account", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant"));
+ Assert.assertThat(apps.keySet(), containsInAnyOrder("root-url-client", "Account", "Broker", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}", "direct-grant"));
AccountApplicationsPage.AppEntry accountEntry = apps.get("Account");
- Assert.assertEquals(3, accountEntry.getRolesAvailable().size());
+ Assert.assertEquals(4, accountEntry.getRolesAvailable().size());
Assert.assertTrue(accountEntry.getRolesAvailable().contains("Manage account links in Account"));
Assert.assertTrue(accountEntry.getRolesAvailable().contains("Manage account in Account"));
Assert.assertTrue(accountEntry.getRolesAvailable().contains("View profile in Account"));
- Assert.assertEquals(1, accountEntry.getRolesGranted().size());
- Assert.assertTrue(accountEntry.getRolesGranted().contains("Full Access"));
- Assert.assertEquals(1, accountEntry.getProtocolMappersGranted().size());
- Assert.assertTrue(accountEntry.getProtocolMappersGranted().contains("Full Access"));
+ Assert.assertTrue(accountEntry.getRolesAvailable().contains("Offline access"));
+ Assert.assertEquals(1, accountEntry.getClientScopesGranted().size());
+ Assert.assertTrue(accountEntry.getClientScopesGranted().contains("Full Access"));
Assert.assertEquals("http://localhost:8180/auth/realms/test/account", accountEntry.getHref());
AccountApplicationsPage.AppEntry testAppEntry = apps.get("test-app");
Assert.assertEquals(5, testAppEntry.getRolesAvailable().size());
Assert.assertTrue(testAppEntry.getRolesAvailable().contains("Offline access"));
- Assert.assertTrue(testAppEntry.getRolesGranted().contains("Full Access"));
- Assert.assertTrue(testAppEntry.getProtocolMappersGranted().contains("Full Access"));
+ Assert.assertTrue(testAppEntry.getClientScopesGranted().contains("Full Access"));
Assert.assertEquals("http://localhost:8180/auth/realms/master/app/auth", testAppEntry.getHref());
AccountApplicationsPage.AppEntry thirdPartyEntry = apps.get("third-party");
- Assert.assertEquals(2, thirdPartyEntry.getRolesAvailable().size());
+ Assert.assertEquals(3, thirdPartyEntry.getRolesAvailable().size());
Assert.assertTrue(thirdPartyEntry.getRolesAvailable().contains("Have User privileges"));
Assert.assertTrue(thirdPartyEntry.getRolesAvailable().contains("Have Customer User privileges in test-app"));
- Assert.assertEquals(0, thirdPartyEntry.getRolesGranted().size());
- Assert.assertEquals(0, thirdPartyEntry.getProtocolMappersGranted().size());
+ Assert.assertEquals(0, thirdPartyEntry.getClientScopesGranted().size());
Assert.assertEquals("http://localhost:8180/auth/realms/master/app/auth", thirdPartyEntry.getHref());
-
+
AccountApplicationsPage.AppEntry testAppNamed = apps.get("Test App Named - ${client_account}");
Assert.assertEquals("http://localhost:8180/varnamedapp/base", testAppNamed.getHref());
-
+
AccountApplicationsPage.AppEntry rootUrlClient = apps.get("root-url-client");
Assert.assertEquals("http://localhost:8180/foo/bar/baz", rootUrlClient.getHref());
-
+
AccountApplicationsPage.AppEntry authzApp = apps.get("test-app-authz");
Assert.assertEquals("http://localhost:8180/test-app-authz", authzApp.getHref());
-
+
AccountApplicationsPage.AppEntry namedApp = apps.get("My Named Test App");
Assert.assertEquals("http://localhost:8180/namedapp/base", namedApp.getHref());
-
+
AccountApplicationsPage.AppEntry testAppScope = apps.get("test-app-scope");
Assert.assertNull(testAppScope.getHref());
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
index 9530de8..011bd61 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/authorization/AbstractPhotozExampleAdapterTest.java
@@ -363,8 +363,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
RoleResource roleResource = resourceServerClient.roles().get("manage-albums");
RoleRepresentation roleRepresentation = roleResource.toRepresentation();
- roleRepresentation.setScopeParamRequired(true);
-
roleResource.update(roleRepresentation);
loginToClientPage("alice", "alice");
@@ -397,8 +395,6 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
RoleResource manageAlbumRole = resourceServerClient.roles().get("manage-albums");
RoleRepresentation roleRepresentation = manageAlbumRole.toRepresentation();
- roleRepresentation.setScopeParamRequired(true);
-
manageAlbumRole.update(roleRepresentation);
loginToClientPage("alice", "alice");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
index e686545..d20ada4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractOfflineServletsAdapterTest.java
@@ -166,7 +166,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
loginPage.login("test-user@localhost", "password");
oauthGrantPage.assertCurrent();
- waitUntilElement(By.xpath("//body")).text().contains("Offline access");
+ waitUntilElement(By.xpath("//body")).text().contains(OAuthGrantPage.OFFLINE_ACCESS_CONSENT_TEXT);
oauthGrantPage.accept();
@@ -175,7 +175,7 @@ public abstract class AbstractOfflineServletsAdapterTest extends AbstractServlet
accountAppPage.open();
AccountApplicationsPage.AppEntry offlineClient = accountAppPage.getApplications().get("offline-client");
- Assert.assertTrue(offlineClient.getRolesGranted().contains("Offline access"));
+ Assert.assertTrue(offlineClient.getClientScopesGranted().contains(OAuthGrantPage.OFFLINE_ACCESS_CONSENT_TEXT));
Assert.assertTrue(offlineClient.getAdditionalGrants().contains("Offline Token"));
//This was necessary to be introduced, otherwise other testcases will fail
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UserStorageConsentTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UserStorageConsentTest.java
index 9698c9b..3cd3403 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UserStorageConsentTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/undertow/servlet/UserStorageConsentTest.java
@@ -25,7 +25,7 @@ import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -48,6 +48,7 @@ import javax.ws.rs.core.Response;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -103,9 +104,8 @@ public class UserStorageConsentTest extends AbstractServletsAdapterTest {
RealmModel realm = session.realms().getRealmByName("demo");
ClientModel product = session.realms().getClientByClientId("product-portal", realm);
product.setConsentRequired(true);
- ClientTemplateModel clientTemplate = realm.addClientTemplate("template");
- clientTemplate.setFullScopeAllowed(true);
- System.err.println("client template protocol mappers size: " + clientTemplate.getProtocolMappers().size());
+ ClientScopeModel clientScope = realm.addClientScope("clientScope");
+ System.err.println("client scope protocol mappers size: " + clientScope.getProtocolMappers().size());
for (ProtocolMapperModel mapper : product.getProtocolMappers()) {
if (mapper.getProtocol().equals(OIDCLoginProtocol.LOGIN_PROTOCOL)) {
@@ -120,16 +120,12 @@ public class UserStorageConsentTest extends AbstractServletsAdapterTest {
config.putAll(mapper.getConfig());
copy.setConfig(config);
copy.setProtocolMapper(mapper.getProtocolMapper());
- copy.setConsentText(mapper.getConsentText());
- clientTemplate.addProtocolMapper(copy);
+ clientScope.addProtocolMapper(copy);
}
}
product.removeProtocolMapper(mapper);
}
- product.setClientTemplate(clientTemplate);
- product.setUseTemplateMappers(true);
- product.setUseTemplateScope(true);
- product.setUseTemplateConfig(false);
+ product.addClientScope(clientScope, true);
}
/**
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java
index c807f47..f6e1785 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractProtocolMapperTest.java
@@ -55,8 +55,6 @@ public abstract class AbstractProtocolMapperTest extends AbstractClientTest {
rep.setName(name);
rep.setProtocolMapper(mapperType);
rep.setConfig(config);
- rep.setConsentRequired(true);
- rep.setConsentText("Test Consent Text");
return rep;
}
@@ -77,8 +75,6 @@ public abstract class AbstractProtocolMapperTest extends AbstractClientTest {
assertNotNull(created);
assertEquals(original.getName(), created.getName());
assertEquals(original.getConfig(), created.getConfig());
- assertEquals(original.getConsentText(), created.getConsentText());
- assertEquals(original.isConsentRequired(), created.isConsentRequired());
assertEquals(original.getProtocol(), created.getProtocol());
assertEquals(original.getProtocolMapper(), created.getProtocolMapper());
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java
index 53098ff..9a087c1 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientProtocolMapperTest.java
@@ -34,6 +34,7 @@ import javax.ws.rs.core.Response;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
*
@@ -69,8 +70,9 @@ public class ClientProtocolMapperTest extends AbstractProtocolMapperTest {
@Test
public void testGetMappersList() {
- assertFalse(oidcMappersRsc.getMappers().isEmpty());
- assertFalse(samlMappersRsc.getMappers().isEmpty());
+ // Built-in protocol mappers are empty by default
+ assertTrue(oidcMappersRsc.getMappers().isEmpty());
+ assertTrue(samlMappersRsc.getMappers().isEmpty());
}
@Test
@@ -144,7 +146,6 @@ public class ClientProtocolMapperTest extends AbstractProtocolMapperTest {
rep.getConfig().put("role", "account.manage-account");
rep.setId(createdId);
- rep.setConsentRequired(false);
samlMappersRsc.update(createdId, rep);
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientProtocolMapperPath(samlClientId, createdId), rep, ResourceType.PROTOCOL_MAPPER);
@@ -163,7 +164,6 @@ public class ClientProtocolMapperTest extends AbstractProtocolMapperTest {
rep.getConfig().put("role", "myotherrole");
rep.setId(createdId);
- rep.setConsentRequired(false);
oidcMappersRsc.update(createdId, rep);
assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientProtocolMapperPath(oidcClientId, createdId), rep, ResourceType.PROTOCOL_MAPPER);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java
new file mode 100644
index 0000000..5ffe8b6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/ClientScopeTest.java
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.admin.client;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientScopesResource;
+import org.keycloak.admin.client.resource.ProtocolMappersResource;
+import org.keycloak.admin.client.resource.RoleMappingResource;
+import org.keycloak.events.admin.OperationType;
+import org.keycloak.events.admin.ResourceType;
+import org.keycloak.models.AccountRoles;
+import org.keycloak.models.Constants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.saml.SamlProtocol;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
+import org.keycloak.representations.idm.ErrorRepresentation;
+import org.keycloak.representations.idm.MappingsRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.util.AdminEventPaths;
+import org.keycloak.testsuite.util.Matchers;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClientScopeTest extends AbstractClientTest {
+
+ @Test
+ public void testAddDuplicatedClientScope() {
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("scope1");
+ String scopeId = createClientScope(scopeRep);
+
+ scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("scope1");
+ Response response = clientScopes().create(scopeRep);
+ assertEquals(409, response.getStatus());
+
+ ErrorRepresentation error = response.readEntity(ErrorRepresentation.class);
+ Assert.assertEquals("Client Scope scope1 already exists", error.getErrorMessage());
+
+ // Cleanup
+ removeClientScope(scopeId);
+ }
+
+
+ @Test (expected = NotFoundException.class)
+ public void testGetUnknownScope() {
+ clientScopes().get("unknown-id").toRepresentation();
+ }
+
+
+ private List<String> getClientScopeNames(List<ClientScopeRepresentation> scopes) {
+ return scopes.stream().map((ClientScopeRepresentation clientScope) -> {
+
+ return clientScope.getName();
+
+ }).collect(Collectors.toList());
+ }
+
+ @Test
+ public void testRemoveClientScope() {
+ // Create scope1
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("scope1");
+ String scope1Id = createClientScope(scopeRep);
+
+ List<ClientScopeRepresentation> clientScopes = clientScopes().findAll();
+ Assert.assertTrue(getClientScopeNames(clientScopes).contains("scope1"));
+
+ // Create scope2
+ scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("scope2");
+ String scope2Id = createClientScope(scopeRep);
+
+ clientScopes = clientScopes().findAll();
+ Assert.assertTrue(getClientScopeNames(clientScopes).contains("scope2"));
+
+ // Remove scope1
+ removeClientScope(scope1Id);
+
+ clientScopes = clientScopes().findAll();
+ Assert.assertFalse(getClientScopeNames(clientScopes).contains("scope1"));
+ Assert.assertTrue(getClientScopeNames(clientScopes).contains("scope2"));
+
+
+ // Remove scope2
+ removeClientScope(scope2Id);
+
+ clientScopes = clientScopes().findAll();
+ Assert.assertFalse(getClientScopeNames(clientScopes).contains("scope1"));
+ Assert.assertFalse(getClientScopeNames(clientScopes).contains("scope2"));
+ }
+
+
+ @Test
+ public void testUpdateScopeScope() {
+ // Test creating
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("scope1");
+ scopeRep.setDescription("scope1-desc");
+ scopeRep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ String scope1Id = createClientScope(scopeRep);
+
+ // Assert created attributes
+ scopeRep = clientScopes().get(scope1Id).toRepresentation();
+ Assert.assertEquals("scope1", scopeRep.getName());
+ Assert.assertEquals("scope1-desc", scopeRep.getDescription());
+ Assert.assertEquals(OIDCLoginProtocol.LOGIN_PROTOCOL, scopeRep.getProtocol());
+
+
+ // Test updating
+ scopeRep.setName("scope1-updated");
+ scopeRep.setDescription("scope1-desc-updated");
+ scopeRep.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
+
+ clientScopes().get(scope1Id).update(scopeRep);
+
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.UPDATE, AdminEventPaths.clientScopeResourcePath(scope1Id), scopeRep, ResourceType.CLIENT_SCOPE);
+
+ // Assert updated attributes
+ scopeRep = clientScopes().get(scope1Id).toRepresentation();
+ Assert.assertEquals("scope1-updated", scopeRep.getName());
+ Assert.assertEquals("scope1-desc-updated", scopeRep.getDescription());
+ Assert.assertEquals(SamlProtocol.LOGIN_PROTOCOL, scopeRep.getProtocol());
+
+ // Remove scope1
+ clientScopes().get(scope1Id).remove();
+ }
+
+
+ @Test
+ public void testRenameScope() {
+ // Create two scopes
+ ClientScopeRepresentation scope1Rep = new ClientScopeRepresentation();
+ scope1Rep.setName("scope1");
+ scope1Rep.setDescription("scope1-desc");
+ scope1Rep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ createClientScope(scope1Rep);
+
+ ClientScopeRepresentation scope2Rep = new ClientScopeRepresentation();
+ scope2Rep.setName("scope2");
+ scope2Rep.setDescription("scope2-desc");
+ scope2Rep.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ String scope2Id = createClientScope(scope2Rep);
+
+ // Test updating
+ scope2Rep.setName("scope1");
+
+ try {
+ clientScopes().get(scope2Id).update(scope2Rep);
+ } catch (ClientErrorException ex) {
+ assertThat(ex.getResponse(), Matchers.statusCodeIs(Status.CONFLICT));
+ }
+ }
+
+
+ @Test
+ public void testScopes() {
+ // Add realm role1
+ RoleRepresentation roleRep1 = createRealmRole("role1");
+
+ // Add realm role2
+ RoleRepresentation roleRep2 = createRealmRole("role2");
+
+ // Add role2 as composite to role1
+ testRealmResource().roles().get("role1").addComposites(Collections.singletonList(roleRep2));
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.roleResourceCompositesPath("role1"), Collections.singletonList(roleRep2), ResourceType.REALM_ROLE);
+
+ // create client scope
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("bar-scope");
+ String scopeId = createClientScope(scopeRep);
+
+ // update with some scopes
+ String accountMgmtId = testRealmResource().clients().findByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).get(0).getId();
+ RoleRepresentation viewAccountRoleRep = testRealmResource().clients().get(accountMgmtId).roles().get(AccountRoles.VIEW_PROFILE).toRepresentation();
+ RoleMappingResource scopesResource = clientScopes().get(scopeId).getScopeMappings();
+
+ scopesResource.realmLevel().add(Collections.singletonList(roleRep1));
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientScopeRoleMappingsRealmLevelPath(scopeId), Collections.singletonList(roleRep1), ResourceType.REALM_SCOPE_MAPPING);
+
+ scopesResource.clientLevel(accountMgmtId).add(Collections.singletonList(viewAccountRoleRep));
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientScopeRoleMappingsClientLevelPath(scopeId, accountMgmtId), Collections.singletonList(viewAccountRoleRep), ResourceType.CLIENT_SCOPE_MAPPING);
+
+ // test that scopes are available (also through composite role)
+ List<RoleRepresentation> allRealm = scopesResource.realmLevel().listAll();
+ List<RoleRepresentation> availableRealm = scopesResource.realmLevel().listAvailable();
+ List<RoleRepresentation> effectiveRealm = scopesResource.realmLevel().listEffective();
+ List<RoleRepresentation> accountRoles = scopesResource.clientLevel(accountMgmtId).listAll();
+
+ assertRolesPresent(allRealm, "role1");
+ assertRolesNotPresent(availableRealm, "role1", "role2");
+ assertRolesPresent(effectiveRealm, "role1", "role2");
+ assertRolesPresent(accountRoles, AccountRoles.VIEW_PROFILE);
+ MappingsRepresentation mappingsRep = clientScopes().get(scopeId).getScopeMappings().getAll();
+ assertRolesPresent(mappingsRep.getRealmMappings(), "role1");
+ assertRolesPresent(mappingsRep.getClientMappings().get(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getMappings(), AccountRoles.VIEW_PROFILE);
+
+
+ // remove scopes
+ scopesResource.realmLevel().remove(Collections.singletonList(roleRep1));
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientScopeRoleMappingsRealmLevelPath(scopeId), Collections.singletonList(roleRep1), ResourceType.REALM_SCOPE_MAPPING);
+
+ scopesResource.clientLevel(accountMgmtId).remove(Collections.singletonList(viewAccountRoleRep));
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientScopeRoleMappingsClientLevelPath(scopeId, accountMgmtId), Collections.singletonList(viewAccountRoleRep), ResourceType.CLIENT_SCOPE_MAPPING);
+
+ // assert scopes are removed
+ allRealm = scopesResource.realmLevel().listAll();
+ availableRealm = scopesResource.realmLevel().listAvailable();
+ effectiveRealm = scopesResource.realmLevel().listEffective();
+ accountRoles = scopesResource.clientLevel(accountMgmtId).listAll();
+ assertRolesNotPresent(allRealm, "role1");
+ assertRolesPresent(availableRealm, "role1", "role2");
+ assertRolesNotPresent(effectiveRealm, "role1", "role2");
+ assertRolesNotPresent(accountRoles, AccountRoles.VIEW_PROFILE);
+
+ // remove scope
+ removeClientScope(scopeId);
+ }
+
+ private void assertRolesPresent(List<RoleRepresentation> roles, String... expectedRoleNames) {
+ List<String> expectedList = Arrays.asList(expectedRoleNames);
+
+ Set<String> presentRoles = new HashSet<>();
+ for (RoleRepresentation roleRep : roles) {
+ presentRoles.add(roleRep.getName());
+ }
+
+ for (String expected : expectedList) {
+ if (!presentRoles.contains(expected)) {
+ Assert.fail("Expected role " + expected + " not available");
+ }
+ }
+ }
+
+ private void assertRolesNotPresent(List<RoleRepresentation> roles, String... notExpectedRoleNames) {
+ List<String> notExpectedList = Arrays.asList(notExpectedRoleNames);
+ for (RoleRepresentation roleRep : roles) {
+ if (notExpectedList.contains(roleRep.getName())) {
+ Assert.fail("Role " + roleRep.getName() + " wasn't expected to be available");
+ }
+ }
+ }
+
+
+ // KEYCLOAK-2809
+ @Test
+ public void testRemoveScopedRole() {
+ // Add realm role
+ RoleRepresentation roleRep = createRealmRole("foo-role");
+
+ // Add client scope
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("bar-scope");
+ String scopeId = createClientScope(scopeRep);
+
+ // Add realm role to scopes of clientScope
+ clientScopes().get(scopeId).getScopeMappings().realmLevel().add(Collections.singletonList(roleRep));
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientScopeRoleMappingsRealmLevelPath(scopeId), Collections.singletonList(roleRep), ResourceType.REALM_SCOPE_MAPPING);
+
+ List<RoleRepresentation> roleReps = clientScopes().get(scopeId).getScopeMappings().realmLevel().listAll();
+ Assert.assertEquals(1, roleReps.size());
+ Assert.assertEquals("foo-role", roleReps.get(0).getName());
+
+ // Remove realm role
+ testRealmResource().roles().deleteRole("foo-role");
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.roleResourcePath("foo-role"), ResourceType.REALM_ROLE);
+
+ // Get scope mappings
+ roleReps = clientScopes().get(scopeId).getScopeMappings().realmLevel().listAll();
+ Assert.assertEquals(0, roleReps.size());
+
+ // Cleanup
+ removeClientScope(scopeId);
+ }
+
+ private RoleRepresentation createRealmRole(String roleName) {
+ RoleRepresentation roleRep = new RoleRepresentation();
+ roleRep.setName(roleName);
+ testRealmResource().roles().create(roleRep);
+
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), roleRep, ResourceType.REALM_ROLE);
+
+ return testRealmResource().roles().get(roleName).toRepresentation();
+ }
+
+ // KEYCLOAK-2844
+ @Test
+ public void testRemoveClientScopeInUse() {
+ // Add client scope
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("foo-scope");
+ String scopeId = createClientScope(scopeRep);
+
+ // Add client with the clientScope
+ ClientRepresentation clientRep = new ClientRepresentation();
+ clientRep.setClientId("bar-client");
+ clientRep.setName("bar-client");
+ clientRep.setRootUrl("foo");
+ clientRep.setProtocol("openid-connect");
+ clientRep.setDefaultClientScopes(Collections.singletonList("foo-scope"));
+ String clientDbId = createClient(clientRep);
+
+ // Can't remove clientScope
+ try {
+ clientScopes().get(scopeId).remove();
+ Assert.fail("Not expected to successfully remove clientScope in use");
+ } catch (BadRequestException bre) {
+ ErrorRepresentation error = bre.getResponse().readEntity(ErrorRepresentation.class);
+ Assert.assertEquals("Cannot remove client scope, it is currently in use", error.getErrorMessage());
+ assertAdminEvents.assertEmpty();
+ }
+
+ // Remove client
+ removeClient(clientDbId);
+
+ // Can remove clientScope now
+ removeClientScope(scopeId);
+ }
+
+
+ @Test
+ public void testRealmDefaultClientScopes() {
+ // Create 2 client scopes
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("scope-def");
+ scopeRep.setProtocol("openid-connect");
+ String scopeDefId = createClientScope(scopeRep);
+ getCleanup().addClientScopeId(scopeDefId);
+
+ scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("scope-opt");
+ scopeRep.setProtocol("openid-connect");
+ String scopeOptId = createClientScope(scopeRep);
+ getCleanup().addClientScopeId(scopeOptId);
+
+ // Add scope-def as default and scope-opt as optional client scope
+ testRealmResource().addDefaultDefaultClientScope(scopeDefId);
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.defaultDefaultClientScopePath(scopeDefId), ResourceType.CLIENT_SCOPE);
+ testRealmResource().addDefaultOptionalClientScope(scopeOptId);
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.defaultOptionalClientScopePath(scopeOptId), ResourceType.CLIENT_SCOPE);
+
+ // Ensure defaults and optional scopes are here
+ List<String> realmDefaultScopes = getClientScopeNames(testRealmResource().getDefaultDefaultClientScopes());
+ List<String> realmOptionalScopes = getClientScopeNames(testRealmResource().getDefaultOptionalClientScopes());
+ Assert.assertTrue(realmDefaultScopes.contains("scope-def"));
+ Assert.assertFalse(realmOptionalScopes .contains("scope-def"));
+ Assert.assertFalse(realmDefaultScopes.contains("scope-opt"));
+ Assert.assertTrue(realmOptionalScopes .contains("scope-opt"));
+
+ // create client. Ensure that it has scope-def and scope-opt scopes assigned
+ ClientRepresentation clientRep = new ClientRepresentation();
+ clientRep.setClientId("bar-client");
+ clientRep.setProtocol("openid-connect");
+ String clientUuid = createClient(clientRep);
+ getCleanup().addClientUuid(clientUuid);
+
+ List<String> clientDefaultScopes = getClientScopeNames(testRealmResource().clients().get(clientUuid).getDefaultClientScopes());
+ List<String> clientOptionalScopes = getClientScopeNames(testRealmResource().clients().get(clientUuid).getOptionalClientScopes());
+ Assert.assertTrue(clientDefaultScopes.contains("scope-def"));
+ Assert.assertFalse(clientOptionalScopes .contains("scope-def"));
+ Assert.assertFalse(clientDefaultScopes.contains("scope-opt"));
+ Assert.assertTrue(clientOptionalScopes .contains("scope-opt"));
+
+ // Unassign scope-def and scope-opt from realm
+ testRealmResource().removeDefaultDefaultClientScope(scopeDefId);
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.defaultDefaultClientScopePath(scopeDefId), ResourceType.CLIENT_SCOPE);
+ testRealmResource().removeDefaultOptionalClientScope(scopeOptId);
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.defaultOptionalClientScopePath(scopeOptId), ResourceType.CLIENT_SCOPE);
+
+ realmDefaultScopes = getClientScopeNames(testRealmResource().getDefaultDefaultClientScopes());
+ realmOptionalScopes = getClientScopeNames(testRealmResource().getDefaultOptionalClientScopes());
+ Assert.assertFalse(realmDefaultScopes.contains("scope-def"));
+ Assert.assertFalse(realmOptionalScopes .contains("scope-def"));
+ Assert.assertFalse(realmDefaultScopes.contains("scope-opt"));
+ Assert.assertFalse(realmOptionalScopes .contains("scope-opt"));
+
+ // Create another client. Check it doesn't have scope-def and scope-opt scopes assigned
+ clientRep = new ClientRepresentation();
+ clientRep.setClientId("bar-client-2");
+ clientRep.setProtocol("openid-connect");
+ clientUuid = createClient(clientRep);
+ getCleanup().addClientUuid(clientUuid);
+
+ clientDefaultScopes = getClientScopeNames(testRealmResource().clients().get(clientUuid).getDefaultClientScopes());
+ clientOptionalScopes = getClientScopeNames(testRealmResource().clients().get(clientUuid).getOptionalClientScopes());
+ Assert.assertFalse(clientDefaultScopes.contains("scope-def"));
+ Assert.assertFalse(clientOptionalScopes .contains("scope-def"));
+ Assert.assertFalse(clientDefaultScopes.contains("scope-opt"));
+ Assert.assertFalse(clientOptionalScopes .contains("scope-opt"));
+ }
+
+
+ // KEYCLOAK-5863
+ @Test
+ public void testUpdateProtocolMappers() {
+ ClientScopeRepresentation scopeRep = new ClientScopeRepresentation();
+ scopeRep.setName("testUpdateProtocolMappers");
+ scopeRep.setProtocol("openid-connect");
+
+
+ String scopeId = createClientScope(scopeRep);
+
+ ProtocolMapperRepresentation mapper = new ProtocolMapperRepresentation();
+ mapper.setName("test");
+ mapper.setProtocol("openid-connect");
+ mapper.setProtocolMapper("oidc-usermodel-attribute-mapper");
+
+ Map<String, String> m = new HashMap<>();
+ m.put("user.attribute", "test");
+ m.put("claim.name", "");
+ m.put("jsonType.label", "");
+
+ mapper.setConfig(m);
+
+ ProtocolMappersResource protocolMappers = clientScopes().get(scopeId).getProtocolMappers();
+
+ Response response = protocolMappers.createMapper(mapper);
+ String mapperId = ApiUtil.getCreatedId(response);
+
+ mapper = protocolMappers.getMapperById(mapperId);
+
+ mapper.getConfig().put("claim.name", "claim");
+
+ protocolMappers.update(mapperId, mapper);
+
+ List<ProtocolMapperRepresentation> mappers = protocolMappers.getMappers();
+ assertEquals(1, mappers.size());
+ assertEquals(2, mappers.get(0).getConfig().size());
+ assertEquals("test", mappers.get(0).getConfig().get("user.attribute"));
+ assertEquals("claim", mappers.get(0).getConfig().get("claim.name"));
+
+ clientScopes().get(scopeId).remove();
+ }
+
+
+ private ClientScopesResource clientScopes() {
+ return testRealmResource().clientScopes();
+ }
+
+ private String createClientScope(ClientScopeRepresentation clientScopeRep) {
+ Response resp = clientScopes().create(clientScopeRep);
+ Assert.assertEquals(201, resp.getStatus());
+ resp.close();
+ String clientScopeId = ApiUtil.getCreatedId(resp);
+ getCleanup().addClientScopeId(clientScopeId);
+
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.CREATE, AdminEventPaths.clientScopeResourcePath(clientScopeId), clientScopeRep, ResourceType.CLIENT_SCOPE);
+
+ return clientScopeId;
+ }
+
+ private void removeClientScope(String clientScopeId) {
+ clientScopes().get(clientScopeId).remove();
+ assertAdminEvents.assertEvent(getRealmId(), OperationType.DELETE, AdminEventPaths.clientScopeResourcePath(clientScopeId), ResourceType.CLIENT_SCOPE);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
index 7e8be2a..0c48fa6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
@@ -474,8 +474,10 @@ public class ClientTest extends AbstractAdminTest {
}
}
- assertNotNull(emailMapperId);
- assertNotNull(usernameMapperId);
+ // Builtin mappers are not here
+ assertNull(emailMapperId);
+ assertNull(usernameMapperId);
+
assertNull(fooMapperId);
// Create foo mapper
@@ -483,7 +485,6 @@ public class ClientTest extends AbstractAdminTest {
fooMapper.setName("foo");
fooMapper.setProtocol("openid-connect");
fooMapper.setProtocolMapper("oidc-hardcoded-claim-mapper");
- fooMapper.setConsentRequired(true);
Response response = mappersResource.createMapper(fooMapper);
String location = response.getLocation().toString();
fooMapperId = location.substring(location.lastIndexOf("/") + 1);
@@ -495,13 +496,11 @@ public class ClientTest extends AbstractAdminTest {
assertEquals(fooMapper.getName(), "foo");
// Update foo mapper
- fooMapper.setConsentRequired(false);
mappersResource.update(fooMapperId, fooMapper);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientProtocolMapperPath(clientDbId, fooMapperId), fooMapper, ResourceType.PROTOCOL_MAPPER);
fooMapper = mappersResource.getMapperById(fooMapperId);
- assertFalse(fooMapper.isConsentRequired());
// Remove foo mapper
mappersResource.delete(fooMapperId);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
index 825e681..6166718 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/FineGrainAdminUnitTest.java
@@ -16,7 +16,6 @@
*/
package org.keycloak.testsuite.admin;
-import org.apache.directory.api.ldap.aci.UserPermission;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Assert;
@@ -26,8 +25,9 @@ import org.keycloak.authorization.model.Resource;
import org.keycloak.client.admin.cli.util.ConfigUtil;
import org.keycloak.models.*;
import org.keycloak.models.utils.KeycloakModelUtils;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
+import org.keycloak.models.GroupModel;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.authorization.Logic;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
@@ -48,6 +48,7 @@ import org.keycloak.testsuite.util.AdminClientUtil;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.core.Response;
+
import java.util.LinkedList;
import java.util.List;
@@ -109,7 +110,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
RoleModel realmRole = realm.addRole("realm-role");
RoleModel realmRole2 = realm.addRole("realm-role2");
ClientModel client1 = realm.addClient(CLIENT_NAME);
- ClientTemplateModel template = realm.addClientTemplate("template");
+ realm.addClientScope("scope");
client1.setFullScopeAllowed(false);
RoleModel client1Role = client1.addRole("client-role");
GroupModel group = realm.createGroup("top");
@@ -413,7 +414,7 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
List<RoleRepresentation> realmRole2Set = new LinkedList<>();
realmRole2Set.add(realmRole2);
ClientRepresentation client = adminClient.realm(TEST).clients().findByClientId(CLIENT_NAME).get(0);
- ClientTemplateRepresentation template = adminClient.realm(TEST).clientTemplates().findAll().get(0);
+ ClientScopeRepresentation scope = adminClient.realm(TEST).clientScopes().findAll().get(0);
RoleRepresentation clientRole = adminClient.realm(TEST).clients().get(client.getId()).roles().get("client-role").toRepresentation();
List<RoleRepresentation> clientRoleSet = new LinkedList<>();
clientRoleSet.add(clientRole);
@@ -435,16 +436,13 @@ public class FineGrainAdminUnitTest extends AbstractKeycloakTest {
client.setFullScopeAllowed(false);
realmClient.realm(TEST).clients().get(client.getId()).update(client);
- client.setClientTemplate(template.getName());
try {
- realmClient.realm(TEST).clients().get(client.getId()).update(client);
+ realmClient.realm(TEST).clients().get(client.getId()).addDefaultClientScope(scope.getId());
Assert.fail("should fail with forbidden exception");
} catch (ClientErrorException e) {
Assert.assertEquals(403, e.getResponse().getStatus());
}
- client.setClientTemplate(null);
- realmClient.realm(TEST).clients().get(client.getId()).update(client);
try {
realmClient.realm(TEST).clients().get(client.getId()).getScopeMappings().realmLevel().add(realmRoleSet);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java
index b1338e9..5329bde 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/group/GroupMappersTest.java
@@ -55,7 +55,6 @@ public class GroupMappersTest extends AbstractGroupTest {
mapper.setName("groups");
mapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(false);
Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
@@ -67,7 +66,6 @@ public class GroupMappersTest extends AbstractGroupTest {
mapper.setName("topAttribute");
mapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(false);
config = new HashMap<>();
config.put(ProtocolMapperUtils.USER_ATTRIBUTE, "topAttribute");
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "topAttribute");
@@ -81,7 +79,6 @@ public class GroupMappersTest extends AbstractGroupTest {
mapper.setName("level2Attribute");
mapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- mapper.setConsentRequired(false);
config = new HashMap<>();
config.put(ProtocolMapperUtils.USER_ATTRIBUTE, "level2Attribute");
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "level2Attribute");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialexport/PartialExportTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialexport/PartialExportTest.java
index ad9fbc7..63a7860 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialexport/PartialExportTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/partialexport/PartialExportTest.java
@@ -49,7 +49,7 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertNull("Realm and client roles are empty", rep.getRoles());
Assert.assertNull("Clients are empty", rep.getClients());
- Assert.assertNull("Scope mappings empty", rep.getScopeMappings());
+ checkScopeMappings(rep.getScopeMappings(), true);
Assert.assertNull("Client scope mappings empty", rep.getClientScopeMappings());
@@ -69,7 +69,7 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertNull("Client roles are empty", rep.getRoles().getClient());
Assert.assertNull("Clients are empty", rep.getClients());
- Assert.assertNull("Scope mappings empty", rep.getScopeMappings());
+ checkScopeMappings(rep.getScopeMappings(), true);
Assert.assertNull("Client scope mappings empty", rep.getClientScopeMappings());
@@ -84,7 +84,7 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertNotNull("Clients not empty", rep.getClients());
checkClients(rep.getClients());
- checkScopeMappings(rep.getScopeMappings());
+ checkScopeMappings(rep.getScopeMappings(), false);
checkClientScopeMappings(rep.getClientScopeMappings());
@@ -107,7 +107,7 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertNotNull("Clients not empty", rep.getClients());
checkClients(rep.getClients());
- checkScopeMappings(rep.getScopeMappings());
+ checkScopeMappings(rep.getScopeMappings(), false);
checkClientScopeMappings(rep.getClientScopeMappings());
@@ -172,7 +172,18 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertTrue("Client test-app-scope / test-app-scope contains test-app-allowed-by-scope", set.contains("test-app-allowed-by-scope"));
}
- private void checkScopeMappings(List<ScopeMappingRepresentation> scopeMappings) {
+ private void checkScopeMappings(List<ScopeMappingRepresentation> scopeMappings, boolean expectOnlyOfflineAccess) {
+ ScopeMappingRepresentation offlineAccessScope = scopeMappings.stream().filter((ScopeMappingRepresentation rep) -> {
+
+ return "offline_access".equals(rep.getClientScope());
+ }).findFirst().get();
+ Assert.assertTrue(offlineAccessScope.getRoles().contains("offline_access"));
+
+ if (expectOnlyOfflineAccess) {
+ Assert.assertEquals(1, scopeMappings.size());
+ return;
+ }
+
Map<String, Set<String>> map = extractScopeMappings(scopeMappings);
Set<String> set = map.get("test-app");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
index 42dbdb0..0cce989 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/PermissionsTest.java
@@ -34,7 +34,7 @@ import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
@@ -220,15 +220,11 @@ public class PermissionsTest extends AbstractKeycloakTest {
@Test
public void realms() throws Exception {
// Check returned realms
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- clients.get("master-none").realms().findAll();
- }
+ invoke((RealmResource realm) -> {
+ clients.get("master-none").realms().findAll();
}, clients.get("none"), false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- clients.get("none").realms().findAll();
- }
+ invoke((RealmResource realm) -> {
+ clients.get("none").realms().findAll();
}, clients.get("none"), false);
Assert.assertNames(clients.get("master-admin").realms().findAll(), "master", REALM_NAME, "realm2");
Assert.assertNames(clients.get(AdminRoles.REALM_ADMIN).realms().findAll(), REALM_NAME);
@@ -712,129 +708,83 @@ public class PermissionsTest extends AbstractKeycloakTest {
}
@Test
- public void clientTemplates() {
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().findAll();
- }
+ public void clientScopes() {
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().findAll();
}, Resource.CLIENT, false, true);
- invoke(new InvocationWithResponse() {
- public void invoke(RealmResource realm, AtomicReference<Response> response) {
- ClientTemplateRepresentation template = new ClientTemplateRepresentation();
- template.setName("template");
- response.set(realm.clientTemplates().create(template));
- }
+ invoke((RealmResource realm, AtomicReference<Response> response) -> {
+ ClientScopeRepresentation scope = new ClientScopeRepresentation();
+ scope.setName("scope");
+ response.set(realm.clientScopes().create(scope));
}, Resource.CLIENT, true);
- ClientTemplateRepresentation template = adminClient.realms().realm(REALM_NAME).clientTemplates().findAll().get(0);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).toRepresentation();
- }
+ ClientScopeRepresentation scope = adminClient.realms().realm(REALM_NAME).clientScopes().findAll().get(0);
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).toRepresentation();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).update(template);
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).update(scope);
}, Resource.CLIENT, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).remove();
- realm.clientTemplates().create(template);
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).remove();
+ realm.clientScopes().create(scope);
}, Resource.CLIENT, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getProtocolMappers().getMappers();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getProtocolMappers().getMappers();
}, Resource.CLIENT, false, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getProtocolMappers().getMappersPerProtocol("nosuch");
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getProtocolMappers().getMappersPerProtocol("nosuch");
}, Resource.CLIENT, false, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getProtocolMappers().getMapperById("nosuch");
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getProtocolMappers().getMapperById("nosuch");
}, Resource.CLIENT, false, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getProtocolMappers().update("nosuch", new ProtocolMapperRepresentation());
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getProtocolMappers().update("nosuch", new ProtocolMapperRepresentation());
}, Resource.CLIENT, true);
- invoke(new InvocationWithResponse() {
- public void invoke(RealmResource realm, AtomicReference<Response> response) {
- response.set(realm.clientTemplates().get(template.getId()).getProtocolMappers().createMapper(new ProtocolMapperRepresentation()));
- }
+ invoke((RealmResource realm, AtomicReference<Response> response) -> {
+ response.set(realm.clientScopes().get(scope.getId()).getProtocolMappers().createMapper(new ProtocolMapperRepresentation()));
}, Resource.CLIENT, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getProtocolMappers().createMapper(Collections.<ProtocolMapperRepresentation>emptyList());
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getProtocolMappers().createMapper(Collections.<ProtocolMapperRepresentation>emptyList());
}, Resource.CLIENT, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getProtocolMappers().delete("nosuch");
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getProtocolMappers().delete("nosuch");
}, Resource.CLIENT, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().getAll();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().getAll();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().listAll();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().realmLevel().listAll();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().listAvailable();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().realmLevel().listAvailable();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().listEffective();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().realmLevel().listEffective();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().add(Collections.<RoleRepresentation>emptyList());
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().realmLevel().add(Collections.<RoleRepresentation>emptyList());
}, Resource.CLIENT, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().realmLevel().remove(Collections.<RoleRepresentation>emptyList());
}, Resource.CLIENT, true);
ClientRepresentation realmAccessClient = adminClient.realms().realm(REALM_NAME).clients().findByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID).get(0);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listAll();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listAll();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listAvailable();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listAvailable();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listEffective();
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).listEffective();
}, Resource.CLIENT, false);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).add(Collections.<RoleRepresentation>emptyList());
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).add(Collections.<RoleRepresentation>emptyList());
}, Resource.CLIENT, true);
- invoke(new Invocation() {
- public void invoke(RealmResource realm) {
- realm.clientTemplates().get(template.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).remove(Collections.<RoleRepresentation>emptyList());
- }
+ invoke((RealmResource realm) -> {
+ realm.clientScopes().get(scope.getId()).getScopeMappings().clientLevel(realmAccessClient.getId()).remove(Collections.<RoleRepresentation>emptyList());
}, Resource.CLIENT, true);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java
index 4fc045b..3ca2a16 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/Assert.java
@@ -19,6 +19,7 @@ package org.keycloak.testsuite;
import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
@@ -81,8 +82,10 @@ public class Assert extends org.junit.Assert {
return ((UserFederationProviderFactoryRepresentation) o1).getId();
} else if (o1 instanceof GroupRepresentation) {
return ((GroupRepresentation) o1).getName();
- }else if (o1 instanceof ComponentRepresentation) {
+ } else if (o1 instanceof ComponentRepresentation) {
return ((ComponentRepresentation) o1).getName();
+ } else if (o1 instanceof ClientScopeRepresentation) {
+ return ((ClientScopeRepresentation) o1).getName();
}
throw new IllegalArgumentException();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java
index f4fcce5..cf65053 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupNamePolicyTest.java
@@ -71,7 +71,6 @@ public class GroupNamePolicyTest extends AbstractAuthzTest {
groupProtocolMapper.setName("groups");
groupProtocolMapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID);
groupProtocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- groupProtocolMapper.setConsentRequired(false);
Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java
index ab0bfe9..da52cb5 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathPolicyTest.java
@@ -71,7 +71,6 @@ public class GroupPathPolicyTest extends AbstractAuthzTest {
groupProtocolMapper.setName("groups");
groupProtocolMapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID);
groupProtocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- groupProtocolMapper.setConsentRequired(false);
Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathWithoutGroupClaimPolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathWithoutGroupClaimPolicyTest.java
index 3936308..7958307 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathWithoutGroupClaimPolicyTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/GroupPathWithoutGroupClaimPolicyTest.java
@@ -71,7 +71,6 @@ public class GroupPathWithoutGroupClaimPolicyTest extends GroupPathPolicyTest {
groupProtocolMapper.setName("groups");
groupProtocolMapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID);
groupProtocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- groupProtocolMapper.setConsentRequired(false);
Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java
index 1e5ec8f..031ec4f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/authz/PolicyEvaluationTest.java
@@ -78,7 +78,6 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
groupProtocolMapper.setName("groups");
groupProtocolMapper.setProtocolMapper(GroupMembershipMapper.PROVIDER_ID);
groupProtocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- groupProtocolMapper.setConsentRequired(false);
Map<String, String> config = new HashMap<>();
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "groups");
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
index 2f15ff6..ce92201 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
@@ -65,7 +65,6 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
emailMapper.setName("email");
emailMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
emailMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
- emailMapper.setConsentRequired(false);
Map<String, String> emailMapperConfig = emailMapper.getConfig();
emailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "email");
@@ -79,7 +78,6 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
userAttrMapper.setName("attribute - name");
userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
userAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
- userAttrMapper.setConsentRequired(false);
Map<String, String> userAttrMapperConfig = userAttrMapper.getConfig();
userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, AbstractUserAttributeMapperTest.ATTRIBUTE_TO_MAP_NAME);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
index 6e6e8b5..eabeec8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
@@ -90,7 +90,6 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
emailMapper.setName("email");
emailMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
emailMapper.setProtocolMapper(UserPropertyAttributeStatementMapper.PROVIDER_ID);
- emailMapper.setConsentRequired(false);
Map<String, String> emailMapperConfig = emailMapper.getConfig();
emailMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, "email");
@@ -102,7 +101,6 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
userAttrMapper.setName("attribute - name");
userAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
userAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID);
- userAttrMapper.setConsentRequired(false);
Map<String, String> userAttrMapperConfig = userAttrMapper.getConfig();
userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, AbstractUserAttributeMapperTest.ATTRIBUTE_TO_MAP_NAME);
@@ -114,7 +112,6 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
userFriendlyAttrMapper.setName("attribute - friendly name");
userFriendlyAttrMapper.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
userFriendlyAttrMapper.setProtocolMapper(UserAttributeStatementMapper.PROVIDER_ID);
- userFriendlyAttrMapper.setConsentRequired(false);
Map<String, String> userFriendlyAttrMapperConfig = userFriendlyAttrMapper.getConfig();
userFriendlyAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, AbstractUserAttributeMapperTest.ATTRIBUTE_TO_MAP_FRIENDLY_NAME);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java
index 2d5ca5b..a0ef18f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/KcinitTest.java
@@ -24,6 +24,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.authentication.authenticators.console.ConsoleUsernamePasswordAuthenticatorFactory;
@@ -34,6 +35,7 @@ import org.keycloak.credential.CredentialModel;
import org.keycloak.models.*;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.TimeBasedOTP;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
@@ -110,6 +112,7 @@ public class KcinitTest extends AbstractTestRealmKeycloakTest {
kcinit.setEnabled(true);
kcinit.addRedirectUri("http://localhost:*");
kcinit.setPublicClient(true);
+ kcinit.removeRole(realm.getRole(OAuth2Constants.OFFLINE_ACCESS));
ClientModel app = realm.addClient(APP);
app.setSecret("password");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java
index 1f08b8b..40783a4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cli/registration/KcRegCreateTest.java
@@ -105,7 +105,7 @@ public class KcRegCreateTest extends AbstractRegCliTest {
Assert.assertEquals("baseUrl", "http://localhost:8980/myapp", client.getBaseUrl());
Assert.assertEquals("rootUrl", "http://localhost:8980/myapp", client.getRootUrl());
Assert.assertEquals("bearerOnly", true, client.isStandardFlowEnabled());
- Assert.assertFalse("mappers not empty", client.getProtocolMappers().isEmpty());
+ Assert.assertNull("mappers are null", client.getProtocolMappers());
// create configuration from file as a template and override clientId and other attributes ... output an object
exe = execute("create --config '" + configFile.getName() + "' -o -f '" + tmpFile.getName() +
@@ -130,7 +130,7 @@ public class KcRegCreateTest extends AbstractRegCliTest {
Assert.assertEquals("baseUrl", "http://localhost:8980/myapp2", client2.getBaseUrl());
Assert.assertEquals("rootUrl", "http://localhost:8980/myapp2", client2.getRootUrl());
Assert.assertEquals("bearerOnly", true, client2.isStandardFlowEnabled());
- Assert.assertFalse("mappers not empty", client2.getProtocolMappers().isEmpty());
+ Assert.assertNull("mappers are null", client2.getProtocolMappers());
// check that using an invalid attribute key is not ignored
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java
index b59d883..ffb283d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationPoliciesTest.java
@@ -24,8 +24,9 @@ import java.util.Map;
import java.util.stream.Collectors;
import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.client.registration.Auth;
import org.keycloak.client.registration.ClientRegistrationException;
@@ -41,7 +42,7 @@ import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.ComponentTypeRepresentation;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
@@ -53,7 +54,7 @@ import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy;
import org.keycloak.services.clientregistration.policy.ClientRegistrationPolicyManager;
import org.keycloak.services.clientregistration.policy.RegistrationAuth;
import org.keycloak.services.clientregistration.policy.impl.ClientDisabledClientRegistrationPolicyFactory;
-import org.keycloak.services.clientregistration.policy.impl.ClientTemplatesClientRegistrationPolicyFactory;
+import org.keycloak.services.clientregistration.policy.impl.ClientScopesClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.MaxClientsClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.ProtocolMappersClientRegistrationPolicyFactory;
import org.keycloak.services.clientregistration.policy.impl.TrustedHostClientRegistrationPolicyFactory;
@@ -361,38 +362,42 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
UserPropertyMapper.PROVIDER_ID, HardcodedRole.PROVIDER_ID);
availableMappers.containsAll(someExpectedMappers);
- // test that clientTemplate provider doesn't contain any client templates yet
- ComponentTypeRepresentation clientTemplateRep = providersMap.get(ClientTemplatesClientRegistrationPolicyFactory.PROVIDER_ID);
- List<String> clientTemplates = getProviderConfigProperty(clientTemplateRep, ClientTemplatesClientRegistrationPolicyFactory.ALLOWED_CLIENT_TEMPLATES);
- Assert.assertTrue(clientTemplates.isEmpty());
-
- // Add some clientTemplates
- ClientTemplateRepresentation clientTemplate = new ClientTemplateRepresentation();
- clientTemplate.setName("foo");
- Response response = realmResource().clientTemplates().create(clientTemplate);
- String fooTemplateId = ApiUtil.getCreatedId(response);
+ // test that clientScope provider contains just the default client scopes
+ ComponentTypeRepresentation clientScopeRep = providersMap.get(ClientScopesClientRegistrationPolicyFactory.PROVIDER_ID);
+ List<String> clientScopes = getProviderConfigProperty(clientScopeRep, ClientScopesClientRegistrationPolicyFactory.ALLOWED_CLIENT_SCOPES);
+ Assert.assertFalse(clientScopes.isEmpty());
+ Assert.assertTrue(clientScopes.contains(OAuth2Constants.SCOPE_PROFILE));
+ Assert.assertFalse(clientScopes.contains("foo"));
+ Assert.assertFalse(clientScopes.contains("bar"));
+
+ // Add some clientScopes
+ ClientScopeRepresentation clientScope = new ClientScopeRepresentation();
+ clientScope.setName("foo");
+ Response response = realmResource().clientScopes().create(clientScope);
+ String fooScopeId = ApiUtil.getCreatedId(response);
response.close();
- clientTemplate = new ClientTemplateRepresentation();
- clientTemplate.setName("bar");
- response = realmResource().clientTemplates().create(clientTemplate);
- String barTemplateId = ApiUtil.getCreatedId(response);
+ clientScope = new ClientScopeRepresentation();
+ clientScope.setName("bar");
+ response = realmResource().clientScopes().create(clientScope);
+ String barScopeId = ApiUtil.getCreatedId(response);
response.close();
- // send request again and test that clientTemplate provider contains added client templates
+ // send request again and test that clientScope provider contains added client scopes
reps = realmResource().clientRegistrationPolicy().getProviders();
- clientTemplateRep = reps.stream().filter((ComponentTypeRepresentation rep1) -> {
+ clientScopeRep = reps.stream().filter((ComponentTypeRepresentation rep1) -> {
- return rep1.getId().equals(ClientTemplatesClientRegistrationPolicyFactory.PROVIDER_ID);
+ return rep1.getId().equals(ClientScopesClientRegistrationPolicyFactory.PROVIDER_ID);
}).findFirst().get();
- clientTemplates = getProviderConfigProperty(clientTemplateRep, ClientTemplatesClientRegistrationPolicyFactory.ALLOWED_CLIENT_TEMPLATES);
- Assert.assertNames(clientTemplates, "foo", "bar");
+ clientScopes = getProviderConfigProperty(clientScopeRep, ClientScopesClientRegistrationPolicyFactory.ALLOWED_CLIENT_SCOPES);
+ Assert.assertTrue(clientScopes.contains("foo"));
+ Assert.assertTrue(clientScopes.contains("bar"));
- // Revert client templates
- realmResource().clientTemplates().get(fooTemplateId).remove();
- realmResource().clientTemplates().get(barTemplateId).remove();
+ // Revert client scopes
+ realmResource().clientScopes().get(fooScopeId).remove();
+ realmResource().clientScopes().get(barScopeId).remove();
}
private List<String> getProviderConfigProperty(ComponentTypeRepresentation provider, String expectedConfigPropName) {
@@ -415,72 +420,71 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
@Test
- public void testClientTemplatesPolicy() throws Exception {
+ public void testClientScopesPolicy() throws Exception {
setTrustedHost("localhost");
- // Add some clientTemplate through Admin REST
- ClientTemplateRepresentation clientTemplate = new ClientTemplateRepresentation();
- clientTemplate.setName("foo");
- Response response = realmResource().clientTemplates().create(clientTemplate);
- String clientTemplateId = ApiUtil.getCreatedId(response);
+ // Add some clientScope through Admin REST
+ ClientScopeRepresentation clientScope = new ClientScopeRepresentation();
+ clientScope.setName("foo");
+ Response response = realmResource().clientScopes().create(clientScope);
+ String clientScopeId = ApiUtil.getCreatedId(response);
response.close();
- // I can't register new client with this template
+ // I can't register new client with this scope
ClientRepresentation clientRep = createRep("test-app");
- clientRep.setClientTemplate("foo");
- assertFail(ClientRegOp.CREATE, clientRep, 403, "Not permitted to use specified clientTemplate");
+ clientRep.setDefaultClientScopes(Collections.singletonList("foo"));
+ assertFail(ClientRegOp.CREATE, clientRep, 403, "Not permitted to use specified clientScope");
- // Register client without template - should success
- clientRep.setClientTemplate(null);
+ // Register client without scope - should success
+ clientRep.setDefaultClientScopes(null);
ClientRepresentation registeredClient = reg.create(clientRep);
reg.auth(Auth.token(registeredClient));
- // Try to update client with template - should fail
- registeredClient.setClientTemplate("foo");
- assertFail(ClientRegOp.UPDATE, registeredClient, 403, "Not permitted to use specified clientTemplate");
+ // Try to update client with scope - should fail
+ registeredClient.setDefaultClientScopes(Collections.singletonList("foo"));
+ assertFail(ClientRegOp.UPDATE, registeredClient, 403, "Not permitted to use specified clientScope");
- // Update client with the clientTemplate via Admin REST
- ClientRepresentation client = ApiUtil.findClientByClientId(realmResource(), "test-app").toRepresentation();
- client.setClientTemplate("foo");
- realmResource().clients().get(client.getId()).update(client);
+ // Update client with the clientScope via Admin REST
+ ClientResource client = ApiUtil.findClientByClientId(realmResource(), "test-app");
+ client.addDefaultClientScope(clientScopeId);
- // Now the update via clientRegistration is permitted too as template was already set
+ // Now the update via clientRegistration is permitted too as scope was already set
reg.update(registeredClient);
- // Revert client template
- realmResource().clients().get(client.getId()).remove();
- realmResource().clientTemplates().get(clientTemplateId).remove();
+ // Revert client scope
+ realmResource().clients().get(client.toRepresentation().getId()).remove();
+ realmResource().clientScopes().get(clientScopeId).remove();
}
@Test
- public void testClientTemplatesPolicyWithPermittedTemplate() throws Exception {
+ public void testClientScopesPolicyWithPermittedScope() throws Exception {
setTrustedHost("localhost");
- // Add some clientTemplate through Admin REST
- ClientTemplateRepresentation clientTemplate = new ClientTemplateRepresentation();
- clientTemplate.setName("foo");
- Response response = realmResource().clientTemplates().create(clientTemplate);
- String clientTemplateId = ApiUtil.getCreatedId(response);
+ // Add some clientScope through Admin REST
+ ClientScopeRepresentation clientScope = new ClientScopeRepresentation();
+ clientScope.setName("foo");
+ Response response = realmResource().clientScopes().create(clientScope);
+ String clientScopeId = ApiUtil.getCreatedId(response);
response.close();
- // I can't register new client with this template
+ // I can't register new client with this scope
ClientRepresentation clientRep = createRep("test-app");
- clientRep.setClientTemplate("foo");
- assertFail(ClientRegOp.CREATE, clientRep, 403, "Not permitted to use specified clientTemplate");
+ clientRep.setDefaultClientScopes(Collections.singletonList("foo"));
+ assertFail(ClientRegOp.CREATE, clientRep, 403, "Not permitted to use specified clientScope");
- // Update the policy to allow the "foo" template
- ComponentRepresentation clientTemplatesPolicyRep = findPolicyByProviderAndAuth(ClientTemplatesClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon());
- clientTemplatesPolicyRep.getConfig().putSingle(ClientTemplatesClientRegistrationPolicyFactory.ALLOWED_CLIENT_TEMPLATES, "foo");
- realmResource().components().component(clientTemplatesPolicyRep.getId()).update(clientTemplatesPolicyRep);
+ // Update the policy to allow the "foo" scope
+ ComponentRepresentation clientScopesPolicyRep = findPolicyByProviderAndAuth(ClientScopesClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon());
+ clientScopesPolicyRep.getConfig().putSingle(ClientScopesClientRegistrationPolicyFactory.ALLOWED_CLIENT_SCOPES, "foo");
+ realmResource().components().component(clientScopesPolicyRep.getId()).update(clientScopesPolicyRep);
// Check that I can register client now
ClientRepresentation registeredClient = reg.create(clientRep);
Assert.assertNotNull(registeredClient.getRegistrationAccessToken());
- // Revert client template
+ // Revert client scope
ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove();
- realmResource().clientTemplates().get(clientTemplateId).remove();
+ realmResource().clientScopes().get(clientScopeId).remove();
}
@@ -527,8 +531,6 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
protocolMapper.setName("Hardcoded foo role");
protocolMapper.setProtocolMapper(HardcodedRole.PROVIDER_ID);
protocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- protocolMapper.setConsentRequired(false);
- protocolMapper.setConsentText(null);
protocolMapper.getConfig().put(HardcodedRole.ROLE_CONFIG, "foo-role");
return protocolMapper;
}
@@ -543,8 +545,6 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
protocolMapper.setName("Full name");
protocolMapper.setProtocolMapper(FullNameMapper.PROVIDER_ID);
protocolMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- protocolMapper.setConsentRequired(true);
- protocolMapper.setConsentText("Full name");
ClientRepresentation clientRep = createRep("test-app");
clientRep.setProtocolMappers(Collections.singletonList(protocolMapper));
@@ -575,33 +575,14 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
public void testProtocolMappersConsentRequired() throws Exception {
setTrustedHost("localhost");
- // Register client and assert it has builtin protocol mappers
+ // Register client and assert it doesn't have builtin protocol mappers
ClientRepresentation clientRep = createRep("test-app");
ClientRepresentation registeredClient = reg.create(clientRep);
- long usernamePropMappersCount = registeredClient.getProtocolMappers().stream().filter((ProtocolMapperRepresentation protocolMapper) -> {
- return protocolMapper.getProtocolMapper().equals(UserPropertyMapper.PROVIDER_ID);
- }).count();
- Assert.assertTrue(usernamePropMappersCount > 0);
-
- // Remove USernamePropertyMapper from the policy configuration
- ComponentRepresentation protocolMapperPolicyRep = findPolicyByProviderAndAuth(ProtocolMappersClientRegistrationPolicyFactory.PROVIDER_ID, getPolicyAnon());
- protocolMapperPolicyRep.getConfig().getList(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES).remove(UserPropertyMapper.PROVIDER_ID);
- realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep);
-
- // Register another client. Assert it doesn't have builtin mappers anymore
- clientRep = createRep("test-app-2");
- registeredClient = reg.create(clientRep);
-
- usernamePropMappersCount = registeredClient.getProtocolMappers().stream().filter((ProtocolMapperRepresentation protocolMapper) -> {
- return protocolMapper.getProtocolMapper().equals(UserPropertyMapper.PROVIDER_ID);
- }).count();
- Assert.assertEquals(0, usernamePropMappersCount);
+ Assert.assertNull(registeredClient.getProtocolMappers());
// Revert
ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove();
- protocolMapperPolicyRep.getConfig().getList(ProtocolMappersClientRegistrationPolicyFactory.ALLOWED_PROTOCOL_MAPPER_TYPES).add(UserPropertyMapper.PROVIDER_ID);
- realmResource().components().component(protocolMapperPolicyRep.getId()).update(protocolMapperPolicyRep);
}
@@ -622,8 +603,6 @@ public class ClientRegistrationPoliciesTest extends AbstractClientRegistrationTe
Assert.assertEquals(1, registeredClient.getProtocolMappers().size());
ProtocolMapperRepresentation hardcodedMapper = registeredClient.getProtocolMappers().get(0);
- Assert.assertTrue(hardcodedMapper.isConsentRequired());
- Assert.assertEquals("Hardcoded foo role", hardcodedMapper.getConsentText());
// Revert
ApiUtil.findClientResourceByClientId(realmResource(), "test-app").remove();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
index d1c7007..ec8455f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/exportimport/ExportImportUtil.java
@@ -18,10 +18,11 @@
package org.keycloak.testsuite.exportimport;
import org.junit.Assert;
+import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
-import org.keycloak.admin.client.resource.ClientTemplateResource;
+import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.common.constants.KerberosConstants;
@@ -29,12 +30,14 @@ import org.keycloak.models.Constants;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
+import org.keycloak.protocol.saml.SamlProtocolFactory;
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.ClientMappingsRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
@@ -63,6 +66,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
import org.keycloak.common.Profile;
/**
@@ -139,10 +144,6 @@ public class ExportImportUtil {
Assert.assertTrue(containsRole(allRoles, findClientRole(realmRsc, application.getId(), "app-admin")));
Assert.assertTrue(containsRole(allRoles, findClientRole(realmRsc, otherApp.getId(), "otherapp-admin")));
- Assert.assertTrue(findClientRole(realmRsc, application.getId(), "app-admin").isScopeParamRequired());
- Assert.assertFalse(findClientRole(realmRsc, otherApp.getId(), "otherapp-admin").isScopeParamRequired());
- Assert.assertFalse(findClientRole(realmRsc, otherApp.getId(), "otherapp-user").isScopeParamRequired());
-
UserRepresentation wburke = findByUsername(realmRsc, "wburke");
// user with creation timestamp in import
Assert.assertEquals(new Long(123654), wburke.getCreatedTimestamp());
@@ -307,11 +308,11 @@ public class ExportImportUtil {
Assert.assertNotNull(realmRsc.flows().getFlow(resetFlow.getId()));
Assert.assertTrue(realmRsc.flows().getExecutions(resetFlow.getAlias()).size() > 0);
- // Test protocol mappers. Default application has all the builtin protocol mappers. OtherApp just gss credential
+ // Test protocol mappers. Default application doesn't have any builtin protocol mappers. OtherApp just gss credential
List<ProtocolMapperRepresentation> applicationMappers = application.getProtocolMappers();
- Assert.assertNotNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));//application.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));
- Assert.assertNotNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "email"));
- Assert.assertNotNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "given name"));
+ Assert.assertNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));//application.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));
+ Assert.assertNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "email"));
+ Assert.assertNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, "given name"));
Assert.assertNull(findMapperByName(applicationMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME));
Assert.assertEquals(1, otherApp.getProtocolMappers().size());
@@ -320,51 +321,53 @@ public class ExportImportUtil {
ProtocolMapperRepresentation gssCredentialMapper = findMapperByName(otherAppMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
assertGssProtocolMapper(gssCredentialMapper);
- // Test clientTemplates
- List<ClientTemplateRepresentation> clientTemplates = realmRsc.clientTemplates().findAll();
- Assert.assertEquals(1, clientTemplates.size());
- ClientTemplateRepresentation clientTemplate = clientTemplates.get(0);
- Assert.assertEquals("foo-template", clientTemplate.getName());
- Assert.assertEquals("foo-template-desc", clientTemplate.getDescription());
- Assert.assertEquals(OIDCLoginProtocol.LOGIN_PROTOCOL, clientTemplate.getProtocol());
- Assert.assertEquals(1, clientTemplate.getProtocolMappers().size());
- List<ProtocolMapperRepresentation> clientTemplateMappers = clientTemplate.getProtocolMappers();
- ProtocolMapperRepresentation templateGssCredentialMapper = findMapperByName(clientTemplateMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
- assertGssProtocolMapper(templateGssCredentialMapper);
-
- // Test client template scopes
- Set<RoleRepresentation> allClientTemplateScopes = allScopeMappings(realmRsc.clientTemplates().get(clientTemplate.getId()));
- Assert.assertEquals(3, allClientTemplateScopes.size());
- Assert.assertTrue(containsRole(allClientTemplateScopes, findRealmRole(realmRsc, "admin")));//allClientTemplateScopes.contains(realm.getRole("admin")));
- Assert.assertTrue(containsRole(allClientTemplateScopes, findClientRole(realmRsc, application.getId(), "app-user")));//allClientTemplateScopes.contains(application.getRole("app-user")));
- Assert.assertTrue(containsRole(allClientTemplateScopes, findClientRole(realmRsc, application.getId(), "app-admin")));//allClientTemplateScopes.contains(application.getRole("app-admin")));
-
- List<RoleRepresentation> clientTemplateRealmScopes = realmScopeMappings(realmRsc.clientTemplates().get(clientTemplate.getId()));
- Assert.assertTrue(containsRole(clientTemplateRealmScopes, findRealmRole(realmRsc, "admin")));//clientTemplateRealmScopes.contains(realm.getRole("admin")));
-
- List<RoleRepresentation> clientTemplateAppScopes = clientScopeMappings(realmRsc.clientTemplates().get(clientTemplate.getId()));//application.getClientScopeMappings(oauthClient);
- Assert.assertTrue(containsRole(clientTemplateAppScopes, findClientRole(realmRsc, application.getId(), "app-user")));//clientTemplateAppScopes.contains(application.getRole("app-user")));
- Assert.assertTrue(containsRole(clientTemplateAppScopes, findClientRole(realmRsc, application.getId(), "app-admin")));//clientTemplateAppScopes.contains(application.getRole("app-admin")));
+ // Test clientScopes
+ List<ClientScopeRepresentation> clientScopes = realmRsc.clientScopes().findAll();
+ ClientScopeRepresentation clientScope = clientScopes.stream().filter((ClientScopeRepresentation clientScope1) -> {
- // Test user consents
- //admin = session.users().getUserByUsername("admin", realm);
+ return "foo_scope".equals(clientScope1.getName());
+
+ }).findFirst().get();
+ Assert.assertEquals("foo_scope", clientScope.getName());
+ Assert.assertEquals("foo scope-desc", clientScope.getDescription());
+ Assert.assertEquals(OIDCLoginProtocol.LOGIN_PROTOCOL, clientScope.getProtocol());
+ Assert.assertEquals(1, clientScope.getProtocolMappers().size());
+ List<ProtocolMapperRepresentation> clientScopeMappers = clientScope.getProtocolMappers();
+ ProtocolMapperRepresentation scopeGssCredentialMapper = findMapperByName(clientScopeMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
+ assertGssProtocolMapper(scopeGssCredentialMapper);
+
+ // Test client scope - scopes
+ Set<RoleRepresentation> allClientScopeScopes = allScopeMappings(realmRsc.clientScopes().get(clientScope.getId()));
+ Assert.assertEquals(3, allClientScopeScopes.size());
+ Assert.assertTrue(containsRole(allClientScopeScopes, findRealmRole(realmRsc, "admin")));
+ Assert.assertTrue(containsRole(allClientScopeScopes, findClientRole(realmRsc, application.getId(), "app-user")));
+ Assert.assertTrue(containsRole(allClientScopeScopes, findClientRole(realmRsc, application.getId(), "app-admin")));
+
+ List<RoleRepresentation> clientScopeRealmScopes = realmScopeMappings(realmRsc.clientScopes().get(clientScope.getId()));
+ Assert.assertTrue(containsRole(clientScopeRealmScopes, findRealmRole(realmRsc, "admin")));
+ List<RoleRepresentation> clientScopeAppScopes = clientScopeMappings(realmRsc.clientScopes().get(clientScope.getId()));
+ Assert.assertTrue(containsRole(clientScopeAppScopes, findClientRole(realmRsc, application.getId(), "app-user")));
+ Assert.assertTrue(containsRole(clientScopeAppScopes, findClientRole(realmRsc, application.getId(), "app-admin")));
+
+ // Test client scopes assignment
+ Assert.assertTrue(otherApp.getDefaultClientScopes().contains("foo_scope"));
+ Assert.assertFalse(application.getDefaultClientScopes().contains("foo_scope"));
+
+ // Test builtin client scopes
+ testRealmDefaultClientScopes(realmRsc);
+
+ // Test user consents
UserResource adminRsc = realmRsc.users().get(admin.getId());
List<Map<String, Object>> consents = adminRsc.getConsents();
Assert.assertEquals(2, consents.size());//.getConsents().size());
Map<String, Object> appAdminConsent = findConsentByClientId(consents, application.getClientId());
- Assert.assertEquals(2, calcNumberGrantedRoles(appAdminConsent));
- Assert.assertTrue(getGrantedProtocolMappers(appAdminConsent) == null || getGrantedProtocolMappers(appAdminConsent).isEmpty());
- Assert.assertTrue(isRealmRoleGranted(appAdminConsent, "admin"));//appAdminConsent.isRoleGranted(realm.getRole("admin")));
- Assert.assertTrue(isClientRoleGranted(appAdminConsent, application.getClientId(), "app-admin"));//appAdminConsent.isRoleGranted(application.getRole("app-admin")));
+ Assert.assertNotNull(appAdminConsent);
+ Assert.assertTrue(isClientScopeGranted(appAdminConsent, OAuth2Constants.OFFLINE_ACCESS));
Map<String, Object> otherAppAdminConsent = findConsentByClientId(consents, otherApp.getClientId());//admin.getConsentByClient(otherApp.getId());
- Assert.assertEquals(1, calcNumberGrantedRoles(otherAppAdminConsent));
- Assert.assertEquals(1, getGrantedProtocolMappers(otherAppAdminConsent).size());//otherAppAdminConsent.getGrantedProtocolMappers().size());
- Assert.assertTrue(isRealmRoleGranted(otherAppAdminConsent, "admin"));//otherAppAdminConsent.isRoleGranted(realm.getRole("admin")));
- Assert.assertFalse(isClientRoleGranted(otherAppAdminConsent, application.getClientId(), "app-admin"));//otherAppAdminConsent.isRoleGranted(application.getRole("app-admin")));
- Assert.assertTrue(isProtocolMapperGranted(otherAppAdminConsent, gssCredentialMapper));
+ Assert.assertFalse(isClientScopeGranted(otherAppAdminConsent, OAuth2Constants.OFFLINE_ACCESS));
Assert.assertTrue(application.isStandardFlowEnabled());
Assert.assertTrue(application.isImplicitFlowEnabled());
@@ -386,39 +389,12 @@ public class ExportImportUtil {
}
}
- private static boolean isProtocolMapperGranted(Map<String, Object> consent, ProtocolMapperRepresentation mapperRep) {
- Map<String, List> grantedMappers = (Map<String, List>)consent.get("grantedProtocolMappers");
- if (grantedMappers == null) return false;
- List<String> mappers = grantedMappers.get(mapperRep.getProtocol());
- if (mappers == null) return false;
- return mappers.contains(mapperRep.getName());
- }
- private static boolean isRealmRoleGranted(Map<String, Object> consent, String roleName) {
- if (consent.get("grantedRealmRoles") == null) return false;
- return ((List)consent.get("grantedRealmRoles")).contains(roleName);
+ private static boolean isClientScopeGranted(Map<String, Object> consent, String clientScopeName) {
+ if (consent.get("grantedClientScopes") == null) return false;
+ return ((List)consent.get("grantedClientScopes")).contains(clientScopeName);
}
- private static boolean isClientRoleGranted(Map<String, Object> consent, String clientId, String roleName) {
- if (consent.get("grantedClientRoles") == null) return false;
- Map<String, List> grantedClientRoles = (Map<String, List>)consent.get("grantedClientRoles");
- List rolesForClient = grantedClientRoles.get(clientId);
- if (rolesForClient == null) return false;
- return rolesForClient.contains(roleName);
- }
-
- private static Map<String, List<String>> getGrantedProtocolMappers(Map<String, Object> consent) {
- return (Map<String, List<String>>)consent.get("grantedProtocolMappers");
- }
-
- private static int calcNumberGrantedRoles(Map<String, Object> consent) {
- int numGranted = 0;
- List realmRoles = (List)consent.get("grantedRealmRoles");
- if (realmRoles != null) numGranted += realmRoles.size();
- Map clientRoles = (Map)consent.get("grantedClientRoles");
- if (clientRoles != null) numGranted += clientRoles.size();
- return numGranted;
- }
private static Map<String, Object> findConsentByClientId(List<Map<String, Object>> consents, String clientId) {
for (Map<String, Object> consent : consents) {
@@ -438,6 +414,10 @@ public class ExportImportUtil {
}
private static ProtocolMapperRepresentation findMapperByName(List<ProtocolMapperRepresentation> mappers, String type, String name) {
+ if (mappers == null) {
+ return null;
+ }
+
for (ProtocolMapperRepresentation mapper : mappers) {
if (mapper.getProtocol().equals(type) &&
mapper.getName().equals(name)) {
@@ -472,7 +452,7 @@ public class ExportImportUtil {
return allRoles;
}
- private static Set<RoleRepresentation> allScopeMappings(ClientTemplateResource client) {
+ private static Set<RoleRepresentation> allScopeMappings(ClientScopeResource client) {
Set<RoleRepresentation> allRoles = new HashSet<>();
List<RoleRepresentation> realmRoles = realmScopeMappings(client);
if (realmRoles != null) allRoles.addAll(realmRoles);
@@ -495,7 +475,7 @@ public class ExportImportUtil {
return clientScopeMappings;
}
- private static List<RoleRepresentation> clientScopeMappings(ClientTemplateResource client) {
+ private static List<RoleRepresentation> clientScopeMappings(ClientScopeResource client) {
List<RoleRepresentation> clientScopeMappings = new LinkedList<>();
Map<String, ClientMappingsRepresentation> clientRoles = client.getScopeMappings().getAll().getClientMappings();
if (clientRoles == null) return clientScopeMappings;
@@ -512,7 +492,7 @@ public class ExportImportUtil {
return client.getScopeMappings().realmLevel().listAll();
}
- private static List<RoleRepresentation> realmScopeMappings(ClientTemplateResource client) {
+ private static List<RoleRepresentation> realmScopeMappings(ClientScopeResource client) {
return client.getScopeMappings().realmLevel().listAll();
}
@@ -651,4 +631,41 @@ public class ExportImportUtil {
private static <D> void assertPredicate(List<D> source, List<Predicate<D>> predicate) {
Assert.assertTrue(!source.stream().filter(object -> !predicate.stream().filter(predicate1 -> predicate1.test(object)).findFirst().isPresent()).findAny().isPresent());
}
+
+
+ public static void testRealmDefaultClientScopes(RealmResource realm) {
+ // Assert built-in scopes were created in realm
+ List<ClientScopeRepresentation> clientScopes = realm.clientScopes().findAll();
+ Map<String, ClientScopeRepresentation> clientScopesMap = clientScopes
+ .stream().collect(Collectors.toMap(clientScope -> clientScope.getName(), clientScope -> clientScope));
+
+ org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_PROFILE));
+ org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_EMAIL));
+ org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_ADDRESS));
+ org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.SCOPE_PHONE));
+ org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(OAuth2Constants.OFFLINE_ACCESS));
+ org.keycloak.testsuite.Assert.assertTrue(clientScopesMap.containsKey(SamlProtocolFactory.SCOPE_ROLE_LIST));
+
+ // Check content of some client scopes
+ Map<String, ProtocolMapperRepresentation> protocolMappers = clientScopesMap.get(OAuth2Constants.SCOPE_EMAIL).getProtocolMappers()
+ .stream().collect(Collectors.toMap(protocolMapper -> protocolMapper.getName(), protocolMapper -> protocolMapper));
+ org.keycloak.testsuite.Assert.assertNames(protocolMappers.keySet(), OIDCLoginProtocolFactory.EMAIL, OIDCLoginProtocolFactory.EMAIL_VERIFIED);
+
+ ClientScopeRepresentation offlineScope = clientScopesMap.get(OAuth2Constants.OFFLINE_ACCESS);
+ org.keycloak.testsuite.Assert.assertTrue(offlineScope.getProtocolMappers()==null || offlineScope.getProtocolMappers().isEmpty());
+ List<RoleRepresentation> offlineRoleScopes = realm.clientScopes().get(offlineScope.getId()).getScopeMappings().realmLevel().listAll();
+ org.keycloak.testsuite.Assert.assertNames(offlineRoleScopes, OAuth2Constants.OFFLINE_ACCESS);
+
+ // Check default client scopes and optional client scopes expected
+ Map<String, ClientScopeRepresentation> defaultClientScopes = realm.getDefaultDefaultClientScopes()
+ .stream().collect(Collectors.toMap(clientScope -> clientScope.getName(), clientScope -> clientScope));
+ org.keycloak.testsuite.Assert.assertTrue(defaultClientScopes.containsKey(OAuth2Constants.SCOPE_PROFILE));
+ org.keycloak.testsuite.Assert.assertTrue(defaultClientScopes.containsKey(OAuth2Constants.SCOPE_EMAIL));
+
+ Map<String, ClientScopeRepresentation> optionalClientScopes = realm.getDefaultOptionalClientScopes()
+ .stream().collect(Collectors.toMap(clientScope -> clientScope.getName(), clientScope -> clientScope));
+ org.keycloak.testsuite.Assert.assertTrue(optionalClientScopes.containsKey(OAuth2Constants.SCOPE_ADDRESS));
+ org.keycloak.testsuite.Assert.assertTrue(optionalClientScopes.containsKey(OAuth2Constants.SCOPE_PHONE));
+ org.keycloak.testsuite.Assert.assertTrue(optionalClientScopes.containsKey(OAuth2Constants.OFFLINE_ACCESS));
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/kerberos/AbstractKerberosTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/kerberos/AbstractKerberosTest.java
index 8e6e42d..296ed70 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/kerberos/AbstractKerberosTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/federation/kerberos/AbstractKerberosTest.java
@@ -268,7 +268,6 @@ public abstract class AbstractKerberosTest extends AbstractAuthTest {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
- true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
true, false);
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java
index eaccb2a..4c95b7e 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ThemeSelectorTest.java
@@ -3,7 +3,7 @@ package org.keycloak.testsuite.forms;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Test;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.admin.ApiUtil;
@@ -38,31 +38,6 @@ public class ThemeSelectorTest extends AbstractTestRealmKeycloakTest {
testRealm().clients().get(rep.getId()).update(rep);
}
- @Test
- public void clientTemplateOverride() {
- ClientTemplateRepresentation templateRep = new ClientTemplateRepresentation();
- templateRep.setName("loginTheme");
- templateRep.setAttributes(new HashMap<>());
- templateRep.getAttributes().put("login_theme", "base");
-
- String templateId = ApiUtil.getCreatedId(testRealm().clientTemplates().create(templateRep));
-
- loginPage.open();
- assertEquals("keycloak", detectTheme());
-
- ClientRepresentation rep = testRealm().clients().findByClientId("test-app").get(0);
- rep.setClientTemplate("loginTheme");
- testRealm().clients().get(rep.getId()).update(rep);
-
- loginPage.open();
- assertEquals("base", detectTheme());
-
- rep.setClientTemplate("NONE");
- testRealm().clients().get(rep.getId()).update(rep);
-
- testRealm().clientTemplates().get(templateId).remove();
- }
-
private String detectTheme() {
if(driver.getPageSource().contains("/login/keycloak/css/login.css")) {
return "keycloak";
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
index 07a08ab..b2498d2 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/AbstractMigrationTest.java
@@ -16,6 +16,7 @@
*/
package org.keycloak.testsuite.migration;
+import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
@@ -28,11 +29,13 @@ import org.keycloak.models.Constants;
import org.keycloak.models.LDAPConstants;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
+import org.keycloak.protocol.saml.SamlProtocolFactory;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@@ -43,9 +46,12 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.exportimport.ExportImportUtil;
import org.keycloak.testsuite.runonserver.RunHelpers;
import org.keycloak.testsuite.util.OAuthClient;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -186,6 +192,12 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
testCliConsoleScopeSize(this.migrationRealm);
}
+ protected void testMigrationTo4_0_0() {
+ testRealmDefaultClientScopes(this.masterRealm);
+ testRealmDefaultClientScopes(this.migrationRealm);
+ testOfflineScopeAddedToClient();
+ }
+
private void testCliConsoleScopeSize(RealmResource realm) {
ClientRepresentation cli = realm.clients().findByClientId(Constants.ADMIN_CLI_CLIENT_ID).get(0);
ClientRepresentation console = realm.clients().findByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID).get(0);
@@ -324,7 +336,6 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
for (String roleName : Constants.AUTHZ_DEFAULT_AUTHORIZATION_ROLES) {
RoleResource role = realm.roles().get(roleName); //throws javax.ws.rs.NotFoundException if not found
- assertFalse("Role's scopeParamRequired should be false.", role.toRepresentation().isScopeParamRequired());
assertFalse("Role shouldn't be composite should be false.", role.toRepresentation().isComposite());
assertTrue("role should be added to default roles for new users", realm.toRepresentation().getDefaultRoles().contains(roleName));
@@ -380,21 +391,26 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
log.info("testing updated protocol mappers");
for (RealmResource realm : realms) {
for (ClientRepresentation client : realm.clients().findAll()) {
- for (ProtocolMapperRepresentation protocolMapper : client.getProtocolMappers()) {
- testUpdateProtocolMapper(protocolMapper);
+ if (client.getProtocolMappers() != null) {
+ for (ProtocolMapperRepresentation protocolMapper : client.getProtocolMappers()) {
+ testUpdateProtocolMapper(protocolMapper, client.getClientId());
+ }
}
}
- for (ClientTemplateRepresentation clientTemlate : realm.clientTemplates().findAll()) {
- for (ProtocolMapperRepresentation protocolMapper : clientTemlate.getProtocolMappers()) {
- testUpdateProtocolMapper(protocolMapper);
+ for (ClientScopeRepresentation clientScope : realm.clientScopes().findAll()) {
+ if (clientScope.getProtocolMappers() != null) {
+ for (ProtocolMapperRepresentation protocolMapper : clientScope.getProtocolMappers()) {
+ testUpdateProtocolMapper(protocolMapper, clientScope.getName());
+ }
}
}
}
}
- protected void testUpdateProtocolMapper(ProtocolMapperRepresentation protocolMapper) {
+ protected void testUpdateProtocolMapper(ProtocolMapperRepresentation protocolMapper, String clientId) {
if (protocolMapper.getConfig().get("id.token.claim") != null) {
- assertEquals("ProtocolMapper's config should contain key 'userinfo.token.claim'.",
+ assertEquals("ProtocolMapper's config should contain key 'userinfo.token.claim'. But it doesn't for protocolMapper '"
+ + protocolMapper.getName() + "' of client/clientScope '" + clientId + "'",
protocolMapper.getConfig().get("id.token.claim"), protocolMapper.getConfig().get("userinfo.token.claim"));
}
}
@@ -424,6 +440,28 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
}
}
+ private void testRealmDefaultClientScopes(RealmResource realm) {
+ log.info("Testing default client scopes created in realm: " + realm.toRepresentation().getRealm());
+ ExportImportUtil.testRealmDefaultClientScopes(realm);
+ }
+
+ private void testOfflineScopeAddedToClient() {
+ log.infof("Testing offline_access optional scope present in realm %s for client migration-test-client", migrationRealm.toRepresentation().getRealm());
+
+ List<ClientScopeRepresentation> optionalClientScopes = ApiUtil.findClientByClientId(this.migrationRealm, "migration-test-client").getOptionalClientScopes();
+
+ boolean found = optionalClientScopes.stream().filter((ClientScopeRepresentation clientScope) -> {
+
+ return "offline_access".equals(clientScope.getName());
+
+ }).findFirst().isPresent();
+
+ if (!found) {
+ Assert.fail("Offline_access not found as optional scope of client migration-test-client");
+ }
+
+ }
+
protected String getMigrationMode() {
return System.getProperty("migration.mode");
}
@@ -441,9 +479,14 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
testMigrationTo3_4_2();
}
+ protected void testMigrationTo4_x() {
+ testMigrationTo4_0_0();
+ }
+
protected void testMigrationTo3_x_and_higher() {
// NOTE: add future methods
testMigrationTo3_x();
+ testMigrationTo4_x();
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java
index 0423f70..11f6f70 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport198MigrationTest.java
@@ -69,7 +69,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
testMigrationTo2_2_0();
testMigrationTo2_3_0();
testMigrationTo2_5_0();
- //testMigrationTo2_5_1(); // todo do not know how to fix
+ //testMigrationTo2_5_1(); // Offline tokens migration is skipped for JSON
testMigrationTo3_x_and_higher();
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
index 127ca7b..a57fc85 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/JsonFileImport255MigrationTest.java
@@ -25,6 +25,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.arquillian.DeploymentTargetModifier;
import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
import org.keycloak.testsuite.util.IOUtil;
+import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 9949ee9..78936ca 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -30,7 +30,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.resource.ClientResource;
-import org.keycloak.admin.client.resource.ClientTemplateResource;
+import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.common.enums.SslRequired;
@@ -49,7 +49,7 @@ import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -58,6 +58,7 @@ import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.ActionURIUtils;
import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.ClientManager;
@@ -701,7 +702,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
}
@Test
- public void testClientTemplate() throws Exception {
+ public void testClientScope() throws Exception {
RealmResource realm = adminClient.realm("test");
RoleRepresentation realmRole = new RoleRepresentation();
realmRole.setName("realm-test-role");
@@ -722,33 +723,24 @@ public class AccessTokenTest extends AbstractKeycloakTest {
addRoles.add(realmRole2);
realm.users().get(user.getId()).roles().realmLevel().add(addRoles);
- ClientTemplateRepresentation rep = new ClientTemplateRepresentation();
- rep.setName("template");
- rep.setProtocol("oidc");
- Response response = realm.clientTemplates().create(rep);
+ ClientScopeRepresentation rep = new ClientScopeRepresentation();
+ rep.setName("scope");
+ rep.setProtocol("openid-connect");
+ Response response = realm.clientScopes().create(rep);
assertEquals(201, response.getStatus());
- URI templateUri = response.getLocation();
+ URI scopeUri = response.getLocation();
+ String clientScopeId = ApiUtil.getCreatedId(response);
response.close();
- ClientTemplateResource templateResource = adminClient.proxy(ClientTemplateResource.class, templateUri);
- ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", false, null, true, true);
+ ClientScopeResource clientScopeResource = adminClient.proxy(ClientScopeResource.class, scopeUri);
+ ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", true, true);
ProtocolMapperRepresentation mapper = ModelToRepresentation.toRepresentation(hard);
- response = templateResource.getProtocolMappers().createMapper(mapper);
+ response = clientScopeResource.getProtocolMappers().createMapper(mapper);
assertEquals(201, response.getStatus());
response.close();
- List<ClientRepresentation> clients = realm.clients().findAll();
- ClientRepresentation clientRep = null;
- for (ClientRepresentation c : clients) {
- if (c.getClientId().equals("test-app")) {
- clientRep = c;
- break;
- }
- }
- clientRep.setClientTemplate("template");
+ ClientRepresentation clientRep = ApiUtil.findClientByClientId(realm, "test-app").toRepresentation();
+ realm.clients().get(clientRep.getId()).addDefaultClientScope(clientScopeId);
clientRep.setFullScopeAllowed(false);
- clientRep.setUseTemplateMappers(true);
- clientRep.setUseTemplateScope(true);
- clientRep.setUseTemplateConfig(true);
realm.clients().get(clientRep.getId()).update(clientRep);
{
@@ -766,7 +758,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
AccessToken accessToken = getAccessToken(tokenResponse);
assertEquals("coded", accessToken.getOtherClaims().get("hard"));
- // check zero scope for template
+ // check zero scope for client scope
Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
@@ -778,7 +770,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
// test that scope is added
List<RoleRepresentation> addRole1 = new LinkedList<>();
addRole1.add(realmRole);
- templateResource.getScopeMappings().realmLevel().add(addRole1);
+ clientScopeResource.getScopeMappings().realmLevel().add(addRole1);
{
Client client = javax.ws.rs.client.ClientBuilder.newClient();
@@ -790,7 +782,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
assertEquals(200, response.getStatus());
org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
AccessToken accessToken = getAccessToken(tokenResponse);
- // check zero scope for template
+ // check single role in scope for client scope
assertNotNull(accessToken.getRealmAccess());
assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
@@ -817,7 +809,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
AccessToken accessToken = getAccessToken(tokenResponse);
- // check zero scope for template
+ // check zero scope for client scope
assertNotNull(accessToken.getRealmAccess());
assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
@@ -828,7 +820,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
}
// remove scopes and retest
- templateResource.getScopeMappings().realmLevel().remove(addRole1);
+ clientScopeResource.getScopeMappings().realmLevel().remove(addRole1);
realm.clients().get(clientRep.getId()).getScopeMappings().realmLevel().remove(addRole2);
{
@@ -850,35 +842,10 @@ public class AccessTokenTest extends AbstractKeycloakTest {
client.close();
}
- // test full scope on template
- rep.setFullScopeAllowed(true);
- templateResource.update(rep);
-
- {
- Client client = javax.ws.rs.client.ClientBuilder.newClient();
- UriBuilder builder = UriBuilder.fromUri(AUTH_SERVER_ROOT);
- URI grantUri = OIDCLoginProtocolService.tokenUrl(builder).build("test");
- WebTarget grantTarget = client.target(grantUri);
-
- response = executeGrantAccessTokenRequest(grantTarget);
- assertEquals(200, response.getStatus());
- org.keycloak.representations.AccessTokenResponse tokenResponse = response.readEntity(org.keycloak.representations.AccessTokenResponse.class);
-
- AccessToken accessToken = getAccessToken(tokenResponse);
-
- // check zero scope for template
- assertNotNull(accessToken.getRealmAccess());
- assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
- assertTrue(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
-
-
- response.close();
- client.close();
- }
-
- // test don't use template scope
- clientRep.setUseTemplateScope(false);
- realm.clients().get(clientRep.getId()).update(clientRep);
+ // test don't use client scope scope. Add roles back to the clientScope, but they won't be available
+ realm.clients().get(clientRep.getId()).removeDefaultClientScope(clientScopeId);
+ clientScopeResource.getScopeMappings().realmLevel().add(addRole1);
+ clientScopeResource.getScopeMappings().realmLevel().add(addRole2);
{
Client client = javax.ws.rs.client.ClientBuilder.newClient();
@@ -893,20 +860,20 @@ public class AccessTokenTest extends AbstractKeycloakTest {
AccessToken accessToken = getAccessToken(tokenResponse);
Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole.getName()));
Assert.assertFalse(accessToken.getRealmAccess().getRoles().contains(realmRole2.getName()));
+ assertNull(accessToken.getOtherClaims().get("hard"));
+ IDToken idToken = getIdToken(tokenResponse);
+ assertNull(idToken.getOtherClaims().get("hard"));
response.close();
client.close();
}
// undo mappers
- clientRep.setClientTemplate(ClientTemplateRepresentation.NONE);
- clientRep.setFullScopeAllowed(true);
- realm.clients().get(clientRep.getId()).update(clientRep);
realm.users().get(user.getId()).roles().realmLevel().remove(addRoles);
realm.roles().get(realmRole.getName()).remove();
realm.roles().get(realmRole2.getName()).remove();
- templateResource.remove();
+ clientScopeResource.remove();
{
Client client = javax.ws.rs.client.ClientBuilder.newClient();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
index d0e0517..c90c748 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
@@ -31,7 +31,7 @@ import org.keycloak.events.EventType;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
-import org.keycloak.representations.idm.ClientTemplateRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -49,6 +49,7 @@ import org.keycloak.testsuite.util.ProtocolMapperUtil;
import org.keycloak.testsuite.util.RoleBuilder;
import org.openqa.selenium.By;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -96,8 +97,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.assertCurrent();
- Assert.assertTrue(driver.getPageSource().contains(ROLE_USER));
- Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER));
+ grantPage.assertGrants(OAuthGrantPage.PROFILE_CONSENT_TEXT, OAuthGrantPage.EMAIL_CONSENT_TEXT);
grantPage.accept();
@@ -146,8 +146,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.assertCurrent();
- Assert.assertTrue(driver.getPageSource().contains(ROLE_USER));
- Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER));
+ grantPage.assertGrants(OAuthGrantPage.PROFILE_CONSENT_TEXT, OAuthGrantPage.EMAIL_CONSENT_TEXT);
grantPage.cancel();
@@ -179,10 +178,8 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
// Assert permissions granted on Account mgmt. applications page
accountAppsPage.open();
AccountApplicationsPage.AppEntry thirdPartyEntry = accountAppsPage.getApplications().get(THIRD_PARTY_APP);
- Assert.assertTrue(thirdPartyEntry.getRolesGranted().contains(ROLE_USER));
- Assert.assertTrue(thirdPartyEntry.getRolesGranted().contains("Have Customer User privileges in test-app"));
- Assert.assertTrue(thirdPartyEntry.getProtocolMappersGranted().contains("Full name"));
- Assert.assertTrue(thirdPartyEntry.getProtocolMappersGranted().contains("Email"));
+ thirdPartyEntry.getClientScopesGranted().contains(OAuthGrantPage.PROFILE_CONSENT_TEXT);
+ thirdPartyEntry.getClientScopesGranted().contains(OAuthGrantPage.EMAIL_CONSENT_TEXT);
// Open login form and assert grantPage not shown
oauth.openLoginForm();
@@ -203,36 +200,29 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
// Open login form again and assert grant Page is shown
oauth.openLoginForm();
grantPage.assertCurrent();
- Assert.assertTrue(driver.getPageSource().contains(ROLE_USER));
- Assert.assertTrue(driver.getPageSource().contains(ROLE_CUSTOMER));
+ grantPage.assertGrants(OAuthGrantPage.PROFILE_CONSENT_TEXT, OAuthGrantPage.EMAIL_CONSENT_TEXT);
}
@Test
- public void oauthGrantAddAnotherRoleAndMapper() {
+ public void oauthGrantAddAnotherScope() {
// Grant permissions on grant screen
oauth.clientId(THIRD_PARTY_APP);
oauth.doLoginGrant("test-user@localhost", "password");
oauth.scope(OAuth2Constants.GRANT_TYPE);
- // Add new protocolMapper and role before showing grant page
- ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createClaimMapper(
- KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
- KerberosConstants.GSS_DELEGATION_CREDENTIAL,
- KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
- true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
- true, false);
-
-
+ // Create new clientScope and add to client
RealmResource appRealm = adminClient.realm(REALM_NAME);
- appRealm.roles().create(RoleBuilder.create().name("new-role").build());
- RoleRepresentation newRole = appRealm.roles().get("new-role").toRepresentation();
-
- ClientManager.realm(adminClient.realm(REALM_NAME)).clientId(THIRD_PARTY_APP)
- .addProtocolMapper(protocolMapper)
- .addScopeMapping(newRole);
+ ClientScopeRepresentation scope1 = new ClientScopeRepresentation();
+ scope1.setName("foo-scope");
+ scope1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ Response response = appRealm.clientScopes().create(scope1);
+ String fooScopeId = ApiUtil.getCreatedId(response);
+ response.close();
+ getCleanup().addClientScopeId(fooScopeId);
- UserResource userResource = findUserByUsernameId(appRealm, "test-user@localhost");
- userResource.roles().realmLevel().add(Collections.singletonList(newRole));
+ // Add clientScope to client
+ ClientResource thirdParty = findClientByClientId(appRealm, THIRD_PARTY_APP);
+ thirdParty.addDefaultClientScope(fooScopeId);
// Confirm grant page
grantPage.assertCurrent();
@@ -242,20 +232,16 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
.detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
.assertEvent();
- // Assert new role and protocol mapper not in account mgmt.
+ // Assert new clientScope not yet in account mgmt
accountAppsPage.open();
AccountApplicationsPage.AppEntry appEntry = accountAppsPage.getApplications().get(THIRD_PARTY_APP);
- Assert.assertFalse(appEntry.getRolesGranted().contains("new-role"));
- Assert.assertFalse(appEntry.getProtocolMappersGranted().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME));
+ Assert.assertFalse(appEntry.getClientScopesGranted().contains("foo-scope"));
- // Show grant page another time. Just new role and protocol mapper are on the page
+ // Show grant page another time. Just new clientScope is on the page
oauth.openLoginForm();
grantPage.assertCurrent();
+ grantPage.assertGrants("foo-scope");
- Assert.assertFalse(driver.getPageSource().contains(ROLE_USER));
- Assert.assertFalse(driver.getPageSource().contains("Full name"));
- Assert.assertTrue(driver.getPageSource().contains("new-role"));
- Assert.assertTrue(driver.getPageSource().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME));
grantPage.accept();
events.expectLogin()
.client(THIRD_PARTY_APP)
@@ -265,8 +251,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
// Go to account mgmt. Everything is granted now
accountAppsPage.open();
appEntry = accountAppsPage.getApplications().get(THIRD_PARTY_APP);
- Assert.assertTrue(appEntry.getRolesGranted().contains("new-role"));
- Assert.assertTrue(appEntry.getProtocolMappersGranted().contains(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME));
+ Assert.assertTrue(appEntry.getClientScopesGranted().contains("foo-scope"));
// Revoke
accountAppsPage.revokeGrant(THIRD_PARTY_APP);
@@ -274,39 +259,32 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
.client("account").detail(Details.REVOKED_CLIENT, THIRD_PARTY_APP).assertEvent();
// Cleanup
- ClientManager.realm(adminClient.realm(REALM_NAME)).clientId(THIRD_PARTY_APP)
- .removeProtocolMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME)
- .removeScopeMapping(newRole);
-
- appRealm.roles().deleteRole("new-role");
-
+ thirdParty.removeDefaultClientScope(fooScopeId);
}
@Test
public void oauthGrantScopeParamRequired() throws Exception {
-
RealmResource appRealm = adminClient.realm(REALM_NAME);
-
ClientResource thirdParty = findClientByClientId(appRealm, THIRD_PARTY_APP);
- thirdParty.roles().create(RoleBuilder.create().id("bar-role").name("bar-role").scopeParamRequired(true).build());
- RoleRepresentation barAppRole = thirdParty.roles().get("bar-role").toRepresentation();
-
- appRealm.roles().create(RoleBuilder.create().id("foo-role").name("foo-role").scopeParamRequired(true).build());
- RoleRepresentation fooRole = appRealm.roles().get("foo-role").toRepresentation();
- ClientManager.realm(appRealm).clientId(THIRD_PARTY_APP).addScopeMapping(fooRole);
- UserResource testUser = findUserByUsernameId(appRealm, "test-user@localhost");
-
- testUser.roles().clientLevel(thirdParty.toRepresentation().getId()).add(Collections.singletonList(barAppRole));
- testUser.roles().realmLevel().add(Collections.singletonList(fooRole));
+ // Create clientScope
+ ClientScopeRepresentation scope1 = new ClientScopeRepresentation();
+ scope1.setName("foo-scope");
+ scope1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ Response response = appRealm.clientScopes().create(scope1);
+ String fooScopeId = ApiUtil.getCreatedId(response);
+ response.close();
+ getCleanup().addClientScopeId(fooScopeId);
- // Assert roles not on grant screen when not requested
+ // Add clientScope as optional to client
+ thirdParty.addOptionalClientScope(fooScopeId);
+ // Assert clientScope not on grant screen when not requested
oauth.clientId(THIRD_PARTY_APP);
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.assertCurrent();
- Assert.assertFalse(driver.getPageSource().contains("foo-role"));
- Assert.assertFalse(driver.getPageSource().contains("bar-role"));
+ List<String> grants = grantPage.getDisplayedGrants();
+ Assert.assertFalse(grants.contains("foo-scope"));
grantPage.cancel();
events.expectLogin()
@@ -316,11 +294,11 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
.session(Matchers.nullValue(String.class))
.assertEvent();
- oauth.scope("foo-role third-party/bar-role");
+ oauth.scope("foo-scope");
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.assertCurrent();
- Assert.assertTrue(driver.getPageSource().contains("foo-role"));
- Assert.assertTrue(driver.getPageSource().contains("bar-role"));
+ grants = grantPage.getDisplayedGrants();
+ Assert.assertTrue(grants.contains("foo-scope"));
grantPage.accept();
events.expectLogin()
@@ -333,44 +311,40 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
accountAppsPage.revokeGrant(THIRD_PARTY_APP);
events.expect(EventType.REVOKE_GRANT)
.client("account").detail(Details.REVOKED_CLIENT, THIRD_PARTY_APP).assertEvent();
- // cleanup
- appRealm.roles().deleteRole(fooRole.getName());
- thirdParty.roles().deleteRole(barAppRole.getName());
+ // cleanup
+ oauth.scope(null);
+ thirdParty.removeOptionalClientScope(fooScopeId);
}
// KEYCLOAK-4326
@Test
- public void oauthGrantClientTemplateMappers() throws Exception {
- // Add client template with some protocol mapper
+ public void oauthGrantClientScopeMappers() throws Exception {
+ // Add client scope with some protocol mapper
RealmResource appRealm = adminClient.realm(REALM_NAME);
- ClientTemplateRepresentation template1 = new ClientTemplateRepresentation();
- template1.setName("foo");
- template1.setFullScopeAllowed(false);
- template1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- Response response = appRealm.clientTemplates().create(template1);
- String templateId = ApiUtil.getCreatedId(response);
+ ClientScopeRepresentation scope1 = new ClientScopeRepresentation();
+ scope1.setName("foo-addr");
+ scope1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ Response response = appRealm.clientScopes().create(scope1);
+ String fooScopeId = ApiUtil.getCreatedId(response);
response.close();
- ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true);
- response = appRealm.clientTemplates().get(templateId).getProtocolMappers().createMapper(protocolMapper);
+ ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true, true);
+ response = appRealm.clientScopes().get(fooScopeId).getProtocolMappers().createMapper(protocolMapper);
response.close();
- // Add template to client
+ // Add clientScope to client
ClientResource thirdParty = findClientByClientId(appRealm, THIRD_PARTY_APP);
- ClientRepresentation thirdPartyRep = thirdParty.toRepresentation();
- thirdPartyRep.setClientTemplate("foo");
- thirdPartyRep.setUseTemplateMappers(true);
- thirdParty.update(thirdPartyRep);
+ thirdParty.addDefaultClientScope(fooScopeId);
+ getCleanup().addClientScopeId(fooScopeId);
// Login
oauth.clientId(THIRD_PARTY_APP);
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.assertCurrent();
- Assert.assertTrue(driver.getPageSource().contains("Email"));
- Assert.assertTrue(driver.getPageSource().contains("Address"));
+ grantPage.assertGrants(OAuthGrantPage.EMAIL_CONSENT_TEXT, OAuthGrantPage.PROFILE_CONSENT_TEXT, "foo-addr");
grantPage.accept();
events.expectLogin()
@@ -383,7 +357,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
Assert.assertTrue(accountAppsPage.isCurrent());
Map<String, AccountApplicationsPage.AppEntry> apps = accountAppsPage.getApplications();
Assert.assertTrue(apps.containsKey("third-party"));
- Assert.assertTrue(apps.get("third-party").getProtocolMappersGranted().contains("Address"));
+ Assert.assertTrue(apps.get("third-party").getClientScopesGranted().contains("foo-addr"));
// Login as admin and see the consent screen of particular user
UserResource user = ApiUtil.findUserByUsernameId(appRealm, "test-user@localhost");
@@ -405,7 +379,8 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
events.expect(EventType.REVOKE_GRANT)
.client("account").detail(Details.REVOKED_CLIENT, THIRD_PARTY_APP).assertEvent();
-
+ // Cleanup
+ thirdParty.removeDefaultClientScope(fooScopeId);
}
@Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthProofKeyForCodeExchangeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthProofKeyForCodeExchangeTest.java
index 6d02dcd..077bec7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthProofKeyForCodeExchangeTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthProofKeyForCodeExchangeTest.java
@@ -435,7 +435,7 @@ public class OAuthProofKeyForCodeExchangeTest extends AbstractKeycloakTest {
assertEquals(findUserByUsername(adminClient.realm("test"), "test-user@localhost").getId(), token.getSubject());
Assert.assertNotEquals("test-user@localhost", token.getSubject());
assertEquals(sessionId, token.getSessionState());
- assertEquals(1, token.getRealmAccess().getRoles().size());
+ assertEquals(2, token.getRealmAccess().getRoles().size());
assertTrue(token.getRealmAccess().isUserInRole("user"));
assertEquals(1, token.getResourceAccess(oauth.getClientId()).getRoles().size());
assertTrue(token.getResourceAccess(oauth.getClientId()).isUserInRole("customer-user"));
@@ -482,7 +482,7 @@ public class OAuthProofKeyForCodeExchangeTest extends AbstractKeycloakTest {
assertEquals(findUserByUsername(adminClient.realm("test"), "test-user@localhost").getId(), refreshedToken.getSubject());
Assert.assertNotEquals("test-user@localhost", refreshedToken.getSubject());
- assertEquals(1, refreshedToken.getRealmAccess().getRoles().size());
+ assertEquals(2, refreshedToken.getRealmAccess().getRoles().size());
Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
assertEquals(1, refreshedToken.getResourceAccess(oauth.getClientId()).getRoles().size());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthScopeInTokenResponseTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthScopeInTokenResponseTest.java
index e6c8253..39d6ff0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthScopeInTokenResponseTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OAuthScopeInTokenResponseTest.java
@@ -7,11 +7,16 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import javax.ws.rs.core.Response;
+
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
+import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.util.OAuthClient;
//OIDC Financial API Read Only Profile : scope MUST be returned in the response from Token Endpoint
@@ -34,7 +39,7 @@ public class OAuthScopeInTokenResponseTest extends AbstractKeycloakTest {
String loginPassword = "password";
String clientSecret = "password";
- String expectedScope = "";
+ String expectedScope = "openid profile email";
oauth.doLogin(loginUser, loginPassword);
@@ -44,13 +49,13 @@ public class OAuthScopeInTokenResponseTest extends AbstractKeycloakTest {
}
@Test
- public void specifySingleScopeAsRealmRoleTest() throws Exception {
+ public void specifySingleNotExistingScopeTest() throws Exception {
String loginUser = "john-doh@localhost";
String loginPassword = "password";
String clientSecret = "password";
String requestedScope = "user";
- String expectedScope = requestedScope;
+ String expectedScope = "openid profile email";
oauth.scope(requestedScope);
oauth.doLogin(loginUser, loginPassword);
@@ -61,13 +66,13 @@ public class OAuthScopeInTokenResponseTest extends AbstractKeycloakTest {
}
@Test
- public void specifyMultipleScopeAsRealmRoleTest() throws Exception {
+ public void specifyMultipleScopeTest() throws Exception {
String loginUser = "rich.roles@redhat.com";
String loginPassword = "password";
String clientSecret = "password";
- String requestedScope = "user realm-composite-role";
- String expectedScope = requestedScope;
+ String requestedScope = "user address";
+ String expectedScope = "openid profile email address";
oauth.scope(requestedScope);
oauth.doLogin(loginUser, loginPassword);
@@ -78,47 +83,25 @@ public class OAuthScopeInTokenResponseTest extends AbstractKeycloakTest {
}
@Test
- public void specifyNotAssignedScopeAsRealmRoleTest() throws Exception {
- String loginUser = "john-doh@localhost";
- String loginPassword = "password";
- String clientSecret = "password";
-
- String requestedScope = "user realm-composite-role";
- String expectedScope = "user";
-
- oauth.scope(requestedScope);
- oauth.doLogin(loginUser, loginPassword);
+ public void specifyMultipleExistingScopesTest() throws Exception {
+ // Create client scope and add it as optional scope
+ ClientScopeRepresentation userScope = new ClientScopeRepresentation();
+ userScope.setName("user");
+ userScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ Response response = realmsResouce().realm("test").clientScopes().create(userScope);
+ String userScopeId = ApiUtil.getCreatedId(response);
+ getCleanup().addClientScopeId(userScopeId);
+
+ ApiUtil.findClientResourceByClientId(realmsResouce().realm("test"), "test-app").addOptionalClientScope(userScopeId);
- String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-
- expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
- }
- @Test
- public void specifySingleScopeAsClientRoleTest() throws Exception {
String loginUser = "john-doh@localhost";
String loginPassword = "password";
String clientSecret = "password";
-
- String requestedScope = "test-app/customer-user";
- String expectedScope = requestedScope;
-
- oauth.scope(requestedScope);
- oauth.doLogin(loginUser, loginPassword);
- String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-
- expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
- }
-
- @Test
- public void specifyMultipleScopeAsClientRoleTest() throws Exception {
- String loginUser = "rich.roles@redhat.com";
- String loginPassword = "password";
- String clientSecret = "password";
-
- String requestedScope = "test-app-scope/test-app-disallowed-by-scope test-app-scope/test-app-allowed-by-scope";
- String expectedScope = requestedScope;
+ // Login without 'user' scope
+ String requestedScope = "address phone";
+ String expectedScope = "openid profile email address phone";
oauth.scope(requestedScope);
oauth.doLogin(loginUser, loginPassword);
@@ -126,74 +109,20 @@ public class OAuthScopeInTokenResponseTest extends AbstractKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
- }
- @Test
- public void specifyNotAssignedScopeAsClientRoleTest() throws Exception {
- String loginUser = "rich.roles@redhat.com";
- String loginPassword = "password";
- String clientSecret = "password";
-
- String requestedScope = "test-app-scope/test-app-unspecified-by-scope test-app-scope/test-app-allowed-by-scope";
- String expectedScope = "test-app-scope/test-app-allowed-by-scope";
-
- oauth.scope(requestedScope);
- oauth.doLogin(loginUser, loginPassword);
+ // Login with 'user' scope
+ requestedScope = "user address phone";
+ expectedScope = "openid profile email user address phone";
- String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-
- expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
- }
-
- @Test
- public void specifyMultipleScopeAsRealmAndClientRoleTest() throws Exception {
- String loginUser = "rich.roles@redhat.com";
- String loginPassword = "password";
- String clientSecret = "password";
-
- String requestedScope = "test-app-scope/test-app-disallowed-by-scope admin test-app/customer-user test-app-scope/test-app-allowed-by-scope";
- String expectedScope = requestedScope;
-
- oauth.scope(requestedScope);
+ oauth.scope(requestedScope);
oauth.doLogin(loginUser, loginPassword);
- String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-
- expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
- }
-
- @Test
- public void specifyNotAssignedScopeAsRealmAndClientRoleTest() throws Exception {
- String loginUser = "john-doh@localhost";
- String loginPassword = "password";
- String clientSecret = "password";
-
- String requestedScope = "test-app/customer-user test-app-scope/test-app-disallowed-by-scope admin test-app/customer-user user test-app-scope/test-app-allowed-by-scope";
- String expectedScope = "user test-app/customer-user";
-
- oauth.scope(requestedScope);
- oauth.doLogin(loginUser, loginPassword);
+ code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
- String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-
expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
- }
-
- @Test
- public void specifyDuplicatedScopeAsRealmAndClientRoleTest() throws Exception {
- String loginUser = "john-doh@localhost";
- String loginPassword = "password";
- String clientSecret = "password";
-
- String requestedScope = "test-app/customer-user user user test-app/customer-user";
- String expectedScope = "user test-app/customer-user";
-
- oauth.scope(requestedScope);
- oauth.doLogin(loginUser, loginPassword);
- String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
-
- expectSuccessfulResponseFromTokenEndpoint(code, expectedScope, clientSecret);
+ // Cleanup
+ ApiUtil.findClientResourceByClientId(realmsResouce().realm("test"), "test-app").removeOptionalClientScope(userScopeId);
}
private void expectSuccessfulResponseFromTokenEndpoint(String code, String expectedScope, String clientSecret) throws Exception {
@@ -204,5 +133,7 @@ public class OAuthScopeInTokenResponseTest extends AbstractKeycloakTest {
Collection<String> expectedScopes = Arrays.asList(expectedScope.split(" "));
Collection<String> receivedScopes = Arrays.asList(response.getScope().split(" "));
Assert.assertTrue(expectedScopes.containsAll(receivedScopes) && receivedScopes.containsAll(expectedScopes));
+
+ oauth.doLogout(response.getRefreshToken(), clientSecret);
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
index 8c0f9ef..0b91453 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
@@ -35,9 +35,11 @@ import org.keycloak.events.Errors;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.Constants;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
@@ -66,6 +68,8 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.admin.ApiUtil.findRealmRoleByName;
import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsername;
@@ -139,7 +143,17 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
@Test
public void offlineTokenDisabledForClient() throws Exception {
- ClientManager.realm(adminClient.realm("test")).clientId("offline-client").fullScopeAllowed(false);
+ // Remove offline-access scope from client
+ ClientScopeRepresentation offlineScope = adminClient.realm("test").clientScopes().findAll().stream().filter((ClientScopeRepresentation clientScope) -> {
+
+ return OAuth2Constants.OFFLINE_ACCESS.equals(clientScope.getName());
+
+ }).findFirst().get();
+
+ ClientManager.realm(adminClient.realm("test")).clientId("offline-client")
+ .fullScopeAllowed(false)
+ .removeClientScope(offlineScope.getId(), false);
+
oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
oauth.clientId("offline-client");
@@ -157,17 +171,22 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "secret1");
+ String offlineTokenString = tokenResponse.getRefreshToken();
+ RefreshToken refreshToken = oauth.verifyRefreshToken(offlineTokenString);
- assertEquals(400, tokenResponse.getStatusCode());
- assertEquals("not_allowed", tokenResponse.getError());
-
+ // Token is refreshed, but it's not offline token
events.expectCodeToToken(codeId, sessionId)
.client("offline-client")
- .error("not_allowed")
- .clearDetails()
+ .detail(Details.REFRESH_TOKEN_TYPE, TokenUtil.TOKEN_TYPE_REFRESH)
.assertEvent();
- ClientManager.realm(adminClient.realm("test")).clientId("offline-client").fullScopeAllowed(true);
+ assertEquals(TokenUtil.TOKEN_TYPE_REFRESH, refreshToken.getType());
+ assertFalse(tokenResponse.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
+
+ // Revert changes
+ ClientManager.realm(adminClient.realm("test")).clientId("offline-client")
+ .fullScopeAllowed(true)
+ .addClientScope(offlineScope.getId(), false);
}
@@ -234,6 +253,8 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
assertEquals(TokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
assertEquals(0, offlineToken.getExpiration());
+ assertTrue(tokenResponse.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
+
String newRefreshTokenString = testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, sessionId, userId);
// Change offset to very big value to ensure offline session expires
@@ -258,8 +279,8 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
final String sessionId, String userId) {
// Change offset to big value to ensure userSession expired
setTimeOffset(99999);
- Assert.assertFalse(oldToken.isActive());
- Assert.assertTrue(offlineToken.isActive());
+ assertFalse(oldToken.isActive());
+ assertTrue(offlineToken.isActive());
// Assert userSession expired
testingClient.testing().removeExpired("test");
@@ -279,13 +300,16 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
Assert.assertNotNull(newRefreshToken);
Assert.assertNotEquals(oldToken.getId(), refreshedToken.getId());
+ // Assert scope parameter contains "offline_access"
+ assertTrue(response.getScope().contains(OAuth2Constants.OFFLINE_ACCESS));
+
Assert.assertEquals(userId, refreshedToken.getSubject());
- Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
- Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole(Constants.OFFLINE_ACCESS_ROLE));
+ assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
+ assertTrue(refreshedToken.getRealmAccess().isUserInRole(Constants.OFFLINE_ACCESS_ROLE));
Assert.assertEquals(1, refreshedToken.getResourceAccess("test-app").getRoles().size());
- Assert.assertTrue(refreshedToken.getResourceAccess("test-app").isUserInRole("customer-user"));
+ assertTrue(refreshedToken.getResourceAccess("test-app").isUserInRole("customer-user"));
EventRepresentation refreshEvent = events.expectRefresh(offlineToken.getId(), sessionId)
.client("offline-client")
@@ -532,9 +556,9 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
applicationsPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, AccountFormServiceTest.ACCOUNT_REDIRECT + "?path=applications").assertEvent();
- Assert.assertTrue(applicationsPage.isCurrent());
+ assertTrue(applicationsPage.isCurrent());
Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
- Assert.assertTrue(apps.containsKey("offline-client-2"));
+ assertTrue(apps.containsKey("offline-client-2"));
Assert.assertEquals("Offline Token", apps.get("offline-client-2").getAdditionalGrants().get(0));
// Now remove the client
@@ -544,12 +568,12 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
// Go to applications page and see offline-client not anymore
applicationsPage.open();
apps = applicationsPage.getApplications();
- Assert.assertFalse(apps.containsKey("offline-client-2"));
+ assertFalse(apps.containsKey("offline-client-2"));
// Login as admin and see consents of user
UserResource user = ApiUtil.findUserByUsernameId(appRealm, "test-user@localhost");
List<Map<String, Object>> consents = user.getConsents();
- Assert.assertTrue(consents.isEmpty());
+ assertTrue(consents.isEmpty());
}
@Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
index 0e3f7b2..f3e8ade 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OIDCProtocolMappersTest.java
@@ -132,19 +132,19 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
- ProtocolMapperRepresentation mapper = createAddressMapper(true, true);
+ ProtocolMapperRepresentation mapper = createAddressMapper(true, true, true);
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.REGION), "region_some");
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.COUNTRY), "country_some");
mapper.getConfig().remove(AddressMapper.getModelPropertyName(AddressClaimSet.POSTAL_CODE)); // Even if we remove protocolMapper config property, it should still default to postal_code
app.getProtocolMappers().createMapper(mapper).close();
- ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", false, null, true, true);
+ ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", true, true);
app.getProtocolMappers().createMapper(hard).close();
- app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", false, null, true, true)).close();
- app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, "", true, true, true)).close();
- app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, "", true, true, true)).close();
- app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, "", true, true, true)).close();
- app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, "", true, true, false)).close();
+ app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", true, true)).close();
+ app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, true, true)).close();
+ app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, true, true)).close();
+ app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, true, true)).close();
+ app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, true, false)).close();
app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")).close();
app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")).close();
app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")).close();
@@ -264,8 +264,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.update(user);
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
- app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, "", true, true, false)).close();
- app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, "", true, true, false)).close();
+ app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, true, false)).close();
+ app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, true, false)).close();
}
{
@@ -542,8 +542,6 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
rep.setName(name);
rep.setProtocolMapper(mapperType);
rep.setConfig(config);
- rep.setConsentRequired(true);
- rep.setConsentText("Test Consent Text");
return rep;
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
index ae0b447..681e67b 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java
@@ -181,7 +181,6 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
assertEquals(findUserByUsername(adminClient.realm("test"), "test-user@localhost").getId(), refreshedToken.getSubject());
Assert.assertNotEquals("test-user@localhost", refreshedToken.getSubject());
- assertEquals(1, refreshedToken.getRealmAccess().getRoles().size());
Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
assertEquals(1, refreshedToken.getResourceAccess(oauth.getClientId()).getRoles().size());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
index 114b70e..7a53e72 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
@@ -32,6 +32,7 @@ import org.keycloak.representations.oidc.TokenMetadataRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.oidc.OIDCScopeTest;
import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.keycloak.testsuite.util.OAuthClient.AccessTokenResponse;
import org.keycloak.util.JsonSerialization;
@@ -219,6 +220,9 @@ public class TokenIntrospectionTest extends AbstractTestRealmKeycloakTest {
assertEquals("test-user@localhost", rep.getUserName());
assertEquals("test-app", rep.getClientId());
assertEquals(loginEvent.getUserId(), rep.getSubject());
+
+ // Assert expected scope
+ OIDCScopeTest.assertScopes("openid email profile", rep.getScope());
}
@Test
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCScopeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCScopeTest.java
new file mode 100644
index 0000000..ed903cc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCScopeTest.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.oidc;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.Response;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ClientScopeResource;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.events.Details;
+import org.keycloak.events.EventType;
+import org.keycloak.models.ClientScopeModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.AddressClaimSet;
+import org.keycloak.representations.IDToken;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
+import org.keycloak.representations.idm.EventRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
+import org.keycloak.testsuite.Assert;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.pages.AccountApplicationsPage;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.ErrorPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.OAuthGrantPage;
+import org.keycloak.testsuite.runonserver.RunOnServerDeployment;
+import org.keycloak.testsuite.util.ClientManager;
+import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.UserBuilder;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for OAuth2 'scope' parameter and for some other aspects of client scopes
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OIDCScopeTest extends AbstractTestRealmKeycloakTest {
+
+ @Rule
+ public AssertEvents events = new AssertEvents(this);
+
+ @Page
+ protected AppPage appPage;
+
+ @Page
+ protected LoginPage loginPage;
+
+ @Page
+ protected AccountUpdateProfilePage profilePage;
+
+ @Page
+ protected OAuthGrantPage grantPage;
+
+ @Page
+ protected AccountApplicationsPage accountAppsPage;
+
+ @Page
+ protected ErrorPage errorPage;
+
+ @Deployment
+ public static WebArchive deploy() {
+ return RunOnServerDeployment.create(OIDCAdvancedRequestParamsTest.class, AbstractTestRealmKeycloakTest.class);
+ }
+
+ private static String userId = KeycloakModelUtils.generateId();
+
+ @Override
+ public void configureTestRealm(RealmRepresentation testRealm) {
+ UserRepresentation user = UserBuilder.create()
+ .id(userId)
+ .username("john")
+ .enabled(true)
+ .email("john@email.cz")
+ .firstName("John")
+ .lastName("Doe")
+ .password("password")
+ .role("account", "manage-account")
+ .role("account", "view-profile")
+ .addRoles("role-1", "role-2")
+ .build();
+
+ user.setEmailVerified(true);
+ MultivaluedHashMap<String, String> attrs = new MultivaluedHashMap<>();
+ attrs.add("street", "Elm 5");
+ attrs.add("phoneNumber", "111-222-333");
+ attrs.add("phoneNumberVerified", "true");
+ user.setAttributes(attrs);
+
+ testRealm.getUsers().add(user);
+
+
+ // Add sample realm roles
+ RoleRepresentation role1 = new RoleRepresentation();
+ role1.setName("role-1");
+ testRealm.getRoles().getRealm().add(role1);
+ RoleRepresentation role2 = new RoleRepresentation();
+ role2.setName("role-2");
+ testRealm.getRoles().getRealm().add(role2);
+ }
+
+ @Before
+ public void clientConfiguration() {
+ ClientManager.realm(adminClient.realm("test")).clientId("test-app").directAccessGrant(true);
+ oauth.clientId("test-app");
+ oauth.scope(null);
+ oauth.maxAge(null);
+ }
+
+ @After
+ public void removePersistentConsentFromUser() {
+ try {
+ adminClient.realm("test").users().get(userId).revokeConsent("third-party");
+ } catch (NotFoundException nfe) {
+ // Ignore if consent not present
+ }
+ }
+
+
+ @Test
+ public void testBuiltinOptionalScopes() throws Exception {
+ // Login. Assert that just 'profile' and 'email' data are there. 'Address' and 'phone' not
+ oauth.doLogin("john", "password");
+ EventRepresentation loginEvent = events.expectLogin()
+ .user(userId)
+ .assertEvent();
+
+ Tokens tokens = sendTokenRequest(loginEvent, "openid email profile", "test-app");
+ IDToken idToken = tokens.idToken;
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, true);
+ assertAddress(idToken, false);
+ assertPhone(idToken, false);
+
+ // Logout
+ oauth.doLogout(tokens.refreshToken, "password");
+ events.expectLogout(idToken.getSessionState())
+ .client("test-app")
+ .user(userId)
+ .removeDetail(Details.REDIRECT_URI).assertEvent();
+
+ // Login with optional scopes. Assert that everything is there
+ oauth.scope("openid address phone");
+ oauth.doLogin("john", "password");
+ loginEvent = events.expectLogin()
+ .user(userId)
+ .assertEvent();
+ tokens = sendTokenRequest(loginEvent, "openid email profile address phone", "test-app");
+ idToken = tokens.idToken;
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, true);
+ assertAddress(idToken, true);
+ assertPhone(idToken, true);
+ }
+
+
+ private void assertProfile(IDToken idToken, boolean claimsIn) {
+ if (claimsIn) {
+ Assert.assertEquals("john", idToken.getPreferredUsername());
+ Assert.assertEquals("John", idToken.getGivenName());
+ Assert.assertEquals("Doe", idToken.getFamilyName());
+ Assert.assertEquals("John Doe", idToken.getName());
+ } else {
+ Assert.assertNull(idToken.getPreferredUsername());
+ Assert.assertNull(idToken.getGivenName());
+ Assert.assertNull(idToken.getFamilyName());
+ Assert.assertNull(idToken.getName());
+ }
+ }
+
+
+ private void assertEmail(IDToken idToken, boolean claimsIn) {
+ if (claimsIn) {
+ Assert.assertEquals("john@email.cz", idToken.getEmail());
+ Assert.assertEquals(true, idToken.getEmailVerified());
+ } else {
+ Assert.assertNull(idToken.getEmail());
+ Assert.assertNull(idToken.getEmailVerified());
+ }
+ }
+
+
+ private void assertAddress(IDToken idToken, boolean claimsIn) {
+ AddressClaimSet address = idToken.getAddress();
+ if (claimsIn) {
+ Assert.assertNotNull(address);
+ Assert.assertEquals("Elm 5", address.getStreetAddress());
+ } else {
+ Assert.assertNull(address);
+ }
+ }
+
+
+ private void assertPhone(IDToken idToken, boolean claimsIn) {
+ if (claimsIn) {
+ Assert.assertEquals("111-222-333", idToken.getPhoneNumber());
+ Assert.assertEquals(true, idToken.getPhoneNumberVerified());
+ } else {
+ Assert.assertNull(idToken.getPhoneNumber());
+ Assert.assertNull(idToken.getPhoneNumberVerified());
+ }
+ }
+
+
+ @Test
+ public void testRemoveScopes() throws Exception {
+ // Add 'profile' as optional scope. Remove 'email' scope entirely
+ String profileScopeId = ApiUtil.findClientScopeByName(testRealm(), "profile").toRepresentation().getId();
+ String emailScopeId = ApiUtil.findClientScopeByName(testRealm(), "email").toRepresentation().getId();
+
+ ClientResource testApp = ApiUtil.findClientByClientId(testRealm(), "test-app");
+ testApp.removeDefaultClientScope(profileScopeId);
+ testApp.removeDefaultClientScope(emailScopeId);
+ testApp.addOptionalClientScope(profileScopeId);
+
+ // Login without scope parameter. Assert 'profile' and 'email' info not there
+ oauth.doLogin("john", "password");
+ EventRepresentation loginEvent = events.expectLogin()
+ .user(userId)
+ .assertEvent();
+
+ Tokens tokens = sendTokenRequest(loginEvent, "openid", "test-app");
+ IDToken idToken = tokens.idToken;
+
+ assertProfile(idToken, false);
+ assertEmail(idToken, false);
+ assertAddress(idToken, false);
+ assertPhone(idToken, false);
+
+ // Logout
+ oauth.doLogout(tokens.refreshToken, "password");
+ events.expectLogout(idToken.getSessionState())
+ .client("test-app")
+ .user(userId)
+ .removeDetail(Details.REDIRECT_URI).assertEvent();
+
+ // Login with scope parameter. Just 'profile' is there
+ oauth.scope("openid profile email");
+ oauth.doLogin("john", "password");
+ loginEvent = events.expectLogin()
+ .user(userId)
+ .assertEvent();
+ tokens = sendTokenRequest(loginEvent, "openid profile", "test-app");
+ idToken = tokens.idToken;
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, false);
+ assertAddress(idToken, false);
+ assertPhone(idToken, false);
+
+ // Revert
+ testApp.removeOptionalClientScope(profileScopeId);
+ testApp.addDefaultClientScope(profileScopeId);
+ testApp.addDefaultClientScope(emailScopeId);
+ }
+
+
+ @Test
+ public void testOptionalScopesWithConsentRequired() throws Exception {
+ // Remove "displayOnConsentScreen" from address
+ ClientScopeResource addressScope = ApiUtil.findClientScopeByName(testRealm(), "address");
+ ClientScopeRepresentation addressScopeRep = addressScope.toRepresentation();
+ addressScopeRep.getAttributes().put(ClientScopeModel.DISPLAY_ON_CONSENT_SCREEN, "false");
+ addressScope.update(addressScopeRep);
+
+ oauth.clientId("third-party");
+ oauth.doLoginGrant("john", "password");
+
+ grantPage.assertCurrent();
+ grantPage.assertGrants(OAuthGrantPage.PROFILE_CONSENT_TEXT, OAuthGrantPage.EMAIL_CONSENT_TEXT);
+ grantPage.accept();
+
+ EventRepresentation loginEvent = events.expectLogin()
+ .user(userId)
+ .client("third-party")
+ .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+ .assertEvent();
+
+ Tokens tokens = sendTokenRequest(loginEvent, "openid email profile", "third-party");
+ IDToken idToken = tokens.idToken;
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, true);
+ assertAddress(idToken, false);
+ assertPhone(idToken, false);
+
+ // Logout
+ oauth.doLogout(tokens.refreshToken, "password");
+ events.expectLogout(idToken.getSessionState())
+ .client("third-party")
+ .user(userId)
+ .removeDetail(Details.REDIRECT_URI).assertEvent();
+
+ // Login with optional scopes. Grant screen should have just "phone"
+ oauth.scope("openid address phone");
+ oauth.doLoginGrant("john", "password");
+
+ grantPage.assertCurrent();
+ grantPage.assertGrants(OAuthGrantPage.PHONE_CONSENT_TEXT);
+ grantPage.accept();
+
+ loginEvent = events.expectLogin()
+ .client("third-party")
+ .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+ .user(userId)
+ .assertEvent();
+ tokens = sendTokenRequest(loginEvent, "openid email profile address phone", "third-party");
+ idToken = tokens.idToken;
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, true);
+ assertAddress(idToken, true);
+ assertPhone(idToken, true);
+
+ // Revert
+ addressScopeRep.getAttributes().put(ClientScopeModel.DISPLAY_ON_CONSENT_SCREEN, "true");
+ addressScope.update(addressScopeRep);
+ }
+
+
+ @Test
+ public void testClientDisplayedOnConsentScreen() throws Exception {
+ // Add "displayOnConsentScreen" to client
+ ClientResource thirdParty = ApiUtil.findClientByClientId(testRealm(), "third-party");
+ ClientRepresentation thirdPartyRep = thirdParty.toRepresentation();
+ thirdPartyRep.getAttributes().put(ClientScopeModel.DISPLAY_ON_CONSENT_SCREEN, "true");
+ thirdPartyRep.getAttributes().put(ClientScopeModel.CONSENT_SCREEN_TEXT, "ThirdParty permissions");
+ thirdParty.update(thirdPartyRep);
+
+ // Login. Client should be displayed on consent screen
+ oauth.clientId("third-party");
+ oauth.doLoginGrant("john", "password");
+
+ grantPage.assertCurrent();
+ grantPage.assertGrants(OAuthGrantPage.PROFILE_CONSENT_TEXT, OAuthGrantPage.EMAIL_CONSENT_TEXT, "ThirdParty permissions");
+ grantPage.accept();
+
+ EventRepresentation loginEvent = events.expectLogin()
+ .user(userId)
+ .client("third-party")
+ .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+ .assertEvent();
+
+ Tokens tokens = sendTokenRequest(loginEvent, "openid email profile", "third-party");
+ IDToken idToken = tokens.idToken;
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, true);
+ assertAddress(idToken, false);
+ assertPhone(idToken, false);
+
+ // Revert
+ thirdPartyRep.getAttributes().put(ClientScopeModel.DISPLAY_ON_CONSENT_SCREEN, "true");
+ thirdParty.update(thirdPartyRep);
+ }
+
+
+ @Test
+ public void testRefreshTokenWithConsentRequired() {
+ // Login with consentRequired
+ oauth.clientId("third-party");
+ oauth.doLoginGrant("john", "password");
+
+ grantPage.assertCurrent();
+ grantPage.assertGrants(OAuthGrantPage.PROFILE_CONSENT_TEXT, OAuthGrantPage.EMAIL_CONSENT_TEXT);
+ grantPage.accept();
+
+ EventRepresentation loginEvent = events.expectLogin()
+ .user(userId)
+ .client("third-party")
+ .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+ .assertEvent();
+
+ Tokens tokens = sendTokenRequest(loginEvent, "openid email profile", "third-party");
+ IDToken idToken = tokens.idToken;
+ RefreshToken refreshToken1 = oauth.verifyRefreshToken(tokens.refreshToken);
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, true);
+ assertAddress(idToken, false);
+ assertPhone(idToken, false);
+
+ // Ensure that I can refresh token
+ OAuthClient.AccessTokenResponse refreshResponse = oauth.doRefreshTokenRequest(tokens.refreshToken, "password");
+ Assert.assertEquals(200, refreshResponse.getStatusCode());
+ idToken = oauth.verifyIDToken(refreshResponse.getIdToken());
+
+ assertProfile(idToken, true);
+ assertEmail(idToken, true);
+ assertAddress(idToken, false);
+ assertPhone(idToken, false);
+
+ events.expectRefresh(refreshToken1.getId(), idToken.getSessionState())
+ .user(userId)
+ .client("third-party")
+ .assertEvent();
+
+ // Go to applications in account mgmt and revoke consent
+ accountAppsPage.open();
+ events.clear();
+ accountAppsPage.revokeGrant("third-party");
+ events.expect(EventType.REVOKE_GRANT)
+ .client("account")
+ .user(userId)
+ .detail(Details.REVOKED_CLIENT, "third-party")
+ .assertEvent();
+
+ // Ensure I can't refresh anymore
+ refreshResponse = oauth.doRefreshTokenRequest(refreshResponse.getRefreshToken(), "password");
+ assertEquals(400, refreshResponse.getStatusCode());
+ events.expectRefresh(refreshToken1.getId(), idToken.getSessionState())
+ .client("third-party")
+ .user(userId)
+ .removeDetail(Details.TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_ID)
+ .removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
+ .error("invalid_token").assertEvent();
+ }
+
+
+ // KEYCLOAK-6170
+ @Test
+ public void testTwoRefreshTokensWithDifferentScopes() {
+ // Add 2 client scopes. Each with scope to 1 realm role
+ ClientScopeRepresentation clientScope1 = new ClientScopeRepresentation();
+ clientScope1.setName("scope-role-1");
+ clientScope1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ Response response = testRealm().clientScopes().create(clientScope1);
+ String scope1Id = ApiUtil.getCreatedId(response);
+ getCleanup().addClientScopeId(scope1Id);
+ response.close();
+
+ ClientScopeRepresentation clientScope2 = new ClientScopeRepresentation();
+ clientScope2.setName("scope-role-2");
+ clientScope2.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ response = testRealm().clientScopes().create(clientScope2);
+ String scope2Id = ApiUtil.getCreatedId(response);
+ getCleanup().addClientScopeId(scope2Id);
+ response.close();
+
+ RoleRepresentation role1 = testRealm().roles().get("role-1").toRepresentation();
+ testRealm().clientScopes().get(scope1Id).getScopeMappings().realmLevel().add(Arrays.asList(role1));
+
+ RoleRepresentation role2 = testRealm().roles().get("role-2").toRepresentation();
+ testRealm().clientScopes().get(scope2Id).getScopeMappings().realmLevel().add(Arrays.asList(role2));
+
+ // Add client scopes to our client. Disable fullScopeAllowed
+ ClientResource testApp = ApiUtil.findClientByClientId(testRealm(), "test-app");
+ ClientRepresentation testAppRep = testApp.toRepresentation();
+ testAppRep.setFullScopeAllowed(false);
+ testApp.update(testAppRep);
+ testApp.addOptionalClientScope(scope1Id);
+ testApp.addOptionalClientScope(scope2Id);
+
+ // Login with scope-role-1. Save refresh token
+ oauth.scope("scope-role-1");
+ oauth.doLogin("john", "password");
+ EventRepresentation loginEvent = events.expectLogin()
+ .user(userId)
+ .assertEvent();
+
+ Tokens tokens1 = sendTokenRequest(loginEvent, "openid email profile scope-role-1", "test-app");
+ Assert.assertTrue(tokens1.accessToken.getRealmAccess().isUserInRole("role-1"));
+ Assert.assertFalse(tokens1.accessToken.getRealmAccess().isUserInRole("role-2"));
+
+ //SSO login with scope-role-2. Save refresh token
+ oauth.scope("scope-role-2");
+ oauth.openLoginForm();
+ loginEvent = events.expectLogin().user(userId).removeDetail(Details.USERNAME).client("test-app").assertEvent();
+ Tokens tokens2 = sendTokenRequest(loginEvent, "openid email profile scope-role-2", "test-app");
+ Assert.assertFalse(tokens2.accessToken.getRealmAccess().isUserInRole("role-1"));
+ Assert.assertTrue(tokens2.accessToken.getRealmAccess().isUserInRole("role-2"));
+
+ // Ensure I can refresh refreshToken1. Just role1 is present
+ OAuthClient.AccessTokenResponse refreshResponse1 = oauth.doRefreshTokenRequest(tokens1.refreshToken, "password");
+ Assert.assertEquals(200, refreshResponse1.getStatusCode());
+ AccessToken accessToken1 = oauth.verifyToken(refreshResponse1.getAccessToken());
+ Assert.assertTrue(accessToken1.getRealmAccess().isUserInRole("role-1"));
+ Assert.assertFalse(accessToken1.getRealmAccess().isUserInRole("role-2"));
+
+ // Ensure I can refresh refreshToken2. Just role2 is present
+ OAuthClient.AccessTokenResponse refreshResponse2 = oauth.doRefreshTokenRequest(tokens2.refreshToken, "password");
+ Assert.assertEquals(200, refreshResponse2.getStatusCode());
+ AccessToken accessToken2 = oauth.verifyToken(refreshResponse2.getAccessToken());
+ Assert.assertFalse(accessToken2.getRealmAccess().isUserInRole("role-1"));
+ Assert.assertTrue(accessToken2.getRealmAccess().isUserInRole("role-2"));
+
+ // Revert
+ testAppRep.setFullScopeAllowed(true);
+ testApp.update(testAppRep);
+ testApp.removeOptionalClientScope(scope1Id);
+ testApp.removeOptionalClientScope(scope2Id);
+ }
+
+
+ protected Tokens sendTokenRequest(EventRepresentation loginEvent, String expectedScope, String clientId) {
+ String sessionId = loginEvent.getSessionId();
+ String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+ String code = new OAuthClient.AuthorizationEndpointResponse(oauth).getCode();
+ OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+ Assert.assertEquals(200, response.getStatusCode());
+
+ // Test scopes
+ log.info("expectedScopes = " + expectedScope);
+ log.info("responseScopes = " + response.getScope());
+ assertScopes(expectedScope, response.getScope());
+
+ IDToken idToken = oauth.verifyIDToken(response.getIdToken());
+ AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
+
+ // Test scope in the access token
+ assertScopes(expectedScope, accessToken.getScope());
+
+ EventRepresentation codeToTokenEvent = events.expectCodeToToken(codeId, sessionId)
+ .user(userId)
+ .client(clientId)
+ .assertEvent();
+
+ // Test scope in the event
+ assertScopes(expectedScope, codeToTokenEvent.getDetails().get(Details.SCOPE));
+
+ return new Tokens(idToken, accessToken, response.getRefreshToken());
+ }
+
+ public static void assertScopes(String expectedScope, String receivedScope) {
+ Collection<String> expectedScopes = Arrays.asList(expectedScope.split(" "));
+ Collection<String> receivedScopes = Arrays.asList(receivedScope.split(" "));
+ Assert.assertTrue("Not matched. expectedScope: " + expectedScope + ", receivedScope: " + receivedScope,
+ expectedScopes.containsAll(receivedScopes) && receivedScopes.containsAll(expectedScopes));
+ }
+
+
+ private static class Tokens {
+ private final IDToken idToken;
+ private final AccessToken accessToken;
+ private final String refreshToken;
+
+ private Tokens(IDToken idToken, AccessToken accessToken, String refreshToken) {
+ this.idToken = idToken;
+ this.accessToken = accessToken;
+ this.refreshToken = refreshToken;
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
index 30e6d8a..e588281 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oidc/OIDCWellKnownProviderTest.java
@@ -21,6 +21,7 @@ import org.junit.Before;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.jose.jws.Algorithm;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory;
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
@@ -114,7 +115,8 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
Assert.assertFalse(oidcConfig.getClaimsParameterSupported());
// Scopes supported
- Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS);
+ Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS,
+ OAuth2Constants.SCOPE_PROFILE, OAuth2Constants.SCOPE_EMAIL, OAuth2Constants.SCOPE_PHONE, OAuth2Constants.SCOPE_ADDRESS);
// Request and Request_Uri
Assert.assertTrue(oidcConfig.getRequestParameterSupported());
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
index db16f02..0e72780 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AdminEventPaths.java
@@ -22,8 +22,8 @@ import org.keycloak.admin.client.resource.AuthenticationManagementResource;
import org.keycloak.admin.client.resource.ClientAttributeCertificateResource;
import org.keycloak.admin.client.resource.ClientInitialAccessResource;
import org.keycloak.admin.client.resource.ClientResource;
-import org.keycloak.admin.client.resource.ClientTemplateResource;
-import org.keycloak.admin.client.resource.ClientTemplatesResource;
+import org.keycloak.admin.client.resource.ClientScopeResource;
+import org.keycloak.admin.client.resource.ClientScopesResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.ComponentsResource;
import org.keycloak.admin.client.resource.GroupResource;
@@ -59,6 +59,16 @@ public class AdminEventPaths {
return uri.toString();
}
+ public static String defaultDefaultClientScopePath(String clientScopeId) {
+ URI uri = UriBuilder.fromUri("").path(RealmResource.class, "addDefaultDefaultClientScope").build(clientScopeId);
+ return uri.toString();
+ }
+
+ public static String defaultOptionalClientScopePath(String clientScopeId) {
+ URI uri = UriBuilder.fromUri("").path(RealmResource.class, "addDefaultOptionalClientScope").build(clientScopeId);
+ return uri.toString();
+ }
+
// CLIENT RESOURCE
@@ -153,37 +163,37 @@ public class AdminEventPaths {
- // CLIENT TEMPLATES
+ // CLIENT SCOPES
- public static String clientTemplateResourcePath(String clientTemplateId) {
- URI uri = UriBuilder.fromUri("").path(RealmResource.class, "clientTemplates").path(ClientTemplatesResource.class, "get").build(clientTemplateId);
+ public static String clientScopeResourcePath(String clientScopeId) {
+ URI uri = UriBuilder.fromUri("").path(RealmResource.class, "clientScopes").path(ClientScopesResource.class, "get").build(clientScopeId);
return uri.toString();
}
- public static String clientTemplateScopeMappingsRealmLevelPath(String clientTemplateDbId) {
- URI uri = UriBuilder.fromUri(clientTemplateResourcePath(clientTemplateDbId)).path(ClientTemplateResource.class, "getScopeMappings")
+ public static String clientScopeRoleMappingsRealmLevelPath(String clientScopeDbId) {
+ URI uri = UriBuilder.fromUri(clientScopeResourcePath(clientScopeDbId)).path(ClientScopeResource.class, "getScopeMappings")
.path(RoleMappingResource.class, "realmLevel")
.build();
return uri.toString();
}
- public static String clientTemplateScopeMappingsClientLevelPath(String clientTemplateDbId, String clientOwningRoleId) {
- URI uri = UriBuilder.fromUri(clientTemplateResourcePath(clientTemplateDbId)).path(ClientTemplateResource.class, "getScopeMappings")
+ public static String clientScopeRoleMappingsClientLevelPath(String clientScopeDbId, String clientOwningRoleId) {
+ URI uri = UriBuilder.fromUri(clientScopeResourcePath(clientScopeDbId)).path(ClientScopeResource.class, "getScopeMappings")
.path(RoleMappingResource.class, "clientLevel")
.build(clientOwningRoleId);
return uri.toString();
}
- public static String clientTemplateProtocolMappersPath(String clientTemplateDbId) {
- URI uri = UriBuilder.fromUri(clientTemplateResourcePath(clientTemplateDbId))
- .path(ClientTemplateResource.class, "getProtocolMappers")
+ public static String clientScopeProtocolMappersPath(String clientScopeDbId) {
+ URI uri = UriBuilder.fromUri(clientScopeResourcePath(clientScopeDbId))
+ .path(ClientScopeResource.class, "getProtocolMappers")
.build();
return uri.toString();
}
- public static String clientTemplateProtocolMapperPath(String clientTemplateDbId, String protocolMapperId) {
- URI uri = UriBuilder.fromUri(clientTemplateProtocolMappersPath(clientTemplateDbId))
+ public static String clientScopeProtocolMapperPath(String clientScopeDbId, String protocolMapperId) {
+ URI uri = UriBuilder.fromUri(clientScopeProtocolMappersPath(clientScopeDbId))
.path(ProtocolMappersResource.class, "getMapperById")
.build(protocolMapperId);
return uri.toString();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientManager.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientManager.java
index eb50949..4a3520f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientManager.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientManager.java
@@ -88,10 +88,27 @@ public class ClientManager {
return this;
}
- public void fullScopeAllowed(boolean enable) {
+ public ClientManagerBuilder fullScopeAllowed(boolean enable) {
ClientRepresentation app = clientResource.toRepresentation();
app.setFullScopeAllowed(enable);
clientResource.update(app);
+ return this;
+ }
+
+ public void addClientScope(String clientScopeId, boolean defaultScope) {
+ if (defaultScope) {
+ clientResource.addDefaultClientScope(clientScopeId);
+ } else {
+ clientResource.addOptionalClientScope(clientScopeId);
+ }
+ }
+
+ public void removeClientScope(String clientScopeId, boolean defaultScope) {
+ if (defaultScope) {
+ clientResource.removeDefaultClientScope(clientScopeId);
+ } else {
+ clientResource.removeOptionalClientScope(clientScopeId);
+ }
}
public void consentRequired(boolean enable) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
index 18b7872..4a493c0 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ProtocolMapperUtil.java
@@ -46,8 +46,8 @@ public class ProtocolMapperUtil {
* @param accessToken
* @return
*/
- public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken) {
- return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken));
+ public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) {
+ return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken, userInfo));
}
/**
@@ -57,8 +57,6 @@ public class ProtocolMapperUtil {
* @param hardcodedName
* @param hardcodedValue
* @param claimType
- * @param consentRequired
- * @param consentText
* @param accessToken
* @param idToken
* @return
@@ -66,10 +64,9 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createHardcodedClaim(String name,
String hardcodedName,
String hardcodedValue, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken) {
return ModelToRepresentation.toRepresentation(HardcodedClaim.create(name, hardcodedName, hardcodedValue,
- claimType, consentRequired, consentText, accessToken, idToken));
+ claimType, accessToken, idToken));
}
/**
@@ -79,8 +76,6 @@ public class ProtocolMapperUtil {
* @param userAttribute
* @param tokenClaimName
* @param claimType
- * @param consentRequired
- * @param consentText
* @param accessToken
* @param idToken
* @param multivalued
@@ -89,23 +84,20 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createClaimMapper(String name,
String userAttribute,
String tokenClaimName, String claimType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken, boolean multivalued) {
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
- claimType, consentRequired, consentText, accessToken, idToken, multivalued));
+ claimType, accessToken, idToken, multivalued));
}
public static ProtocolMapperRepresentation createClaimMapper(String name,
String userSessionNote,
String tokenClaimName, String jsonType,
- boolean consentRequired, String consentText,
boolean accessToken, boolean idToken) {
return ModelToRepresentation.toRepresentation(UserSessionNoteMapper.createClaimMapper(name,
userSessionNote,
tokenClaimName, jsonType,
- consentRequired, consentText,
accessToken, idToken));
}
@@ -161,7 +153,7 @@ public class ProtocolMapperUtil {
boolean multiValued) {
return ModelToRepresentation.toRepresentation(
- ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, false, null, accessToken, idToken, script, multiValued)
+ ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, script, multiValued)
);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java
index ceadc0c..8338bcb 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/RoleBuilder.java
@@ -54,11 +54,6 @@ public class RoleBuilder {
return this;
}
- public RoleBuilder scopeParamRequired(Boolean required) {
- rep.setScopeParamRequired(required);
- return this;
- }
-
public RoleBuilder composite() {
rep.setComposite(true);
return this;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
index a0c5b30..a15128c 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/model/testrealm.json
@@ -172,7 +172,7 @@
"clientConsents": [
{
"clientId": "Application",
- "grantedRealmRoles": [ "admin" ],
+ "grantedRealmRoles": [ "offline_access" ],
"grantedClientRoles": {
"Application": [ "app-admin" ]
}
@@ -230,6 +230,7 @@
"clientId": "OtherApp",
"name": "Other Application",
"enabled": true,
+ "clientTemplate": "foo scope",
"standardFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": true,
@@ -455,8 +456,8 @@
],
"clientTemplates" : [
{
- "name" : "foo-template",
- "description" : "foo-template-desc",
+ "name" : "foo scope",
+ "description" : "foo scope-desc",
"protocol" : "openid-connect",
"protocolMappers" : [
{
@@ -491,8 +492,7 @@
"application" : {
"Application" : [
{
- "name": "app-admin",
- "scopeParamRequired": true
+ "name": "app-admin"
},
{
"name": "app-user"
@@ -500,8 +500,7 @@
],
"OtherApp" : [
{
- "name": "otherapp-admin",
- "scopeParamRequired": false
+ "name": "otherapp-admin"
},
{
"name": "otherapp-user"
@@ -515,7 +514,7 @@
"roles": ["admin"]
},
{
- "clientTemplate": "foo-template",
+ "clientTemplate": "foo scope",
"roles": ["admin"]
}
],
@@ -526,7 +525,7 @@
"roles": ["app-user"]
},
{
- "clientTemplate": "foo-template",
+ "clientTemplate": "foo scope",
"roles": ["app-user", "app-admin" ]
}
]
diff --git a/testsuite/integration-arquillian/tests/other/springboot-tests/src/test/java/org/keycloak/testsuite/springboot/OfflineTokenSpringBootTest.java b/testsuite/integration-arquillian/tests/other/springboot-tests/src/test/java/org/keycloak/testsuite/springboot/OfflineTokenSpringBootTest.java
index 9fdc0f7..ff88071 100644
--- a/testsuite/integration-arquillian/tests/other/springboot-tests/src/test/java/org/keycloak/testsuite/springboot/OfflineTokenSpringBootTest.java
+++ b/testsuite/integration-arquillian/tests/other/springboot-tests/src/test/java/org/keycloak/testsuite/springboot/OfflineTokenSpringBootTest.java
@@ -132,7 +132,7 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
loginPage.login(USER_LOGIN, USER_PASSWORD);
oauthGrantPage.assertCurrent();
- WaitUtils.waitUntilElement(By.xpath("//body")).text().contains("Offline access");
+ WaitUtils.waitUntilElement(By.xpath("//body")).text().contains(OAuthGrantPage.OFFLINE_ACCESS_CONSENT_TEXT);
oauthGrantPage.accept();
@@ -143,7 +143,7 @@ public class OfflineTokenSpringBootTest extends AbstractSpringBootTest {
Urls.accountApplicationsPage(getAuthServerRoot(), REALM_NAME).toString();
driver.navigate().to(accountAppPageUrl);
AccountApplicationsPage.AppEntry offlineClient = accountAppPage.getApplications().get(CLIENT_ID);
- Assert.assertTrue(offlineClient.getRolesGranted().contains("Offline access"));
+ Assert.assertTrue(offlineClient.getClientScopesGranted().contains(OAuthGrantPage.OFFLINE_ACCESS_CONSENT_TEXT));
Assert.assertTrue(offlineClient.getAdditionalGrants().contains("Offline Token"));
//This was necessary to be introduced, otherwise other testcases will fail
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPMultipleAttributesTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPMultipleAttributesTest.java
index b0951f4..3f8618a 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPMultipleAttributesTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPMultipleAttributesTest.java
@@ -34,6 +34,7 @@ import org.keycloak.models.LDAPConstants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
import org.keycloak.services.managers.RealmManager;
@@ -114,11 +115,12 @@ public class LDAPMultipleAttributesTest {
// Create ldap-portal client
ClientModel ldapClient = KeycloakModelUtils.createClient(appRealm, "ldap-portal");
+ ldapClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
ldapClient.addRedirectUri("/ldap-portal");
ldapClient.addRedirectUri("/ldap-portal/*");
ldapClient.setManagementUrl("/ldap-portal");
- ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, "", true, true, true));
- ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, "", true, true, false));
+ ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, true, true));
+ ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, true, false));
ldapClient.addScopeMapping(appRealm.getRole("user"));
ldapClient.setSecret("password");
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java
index daeb309..fc76d40 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java
@@ -40,6 +40,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.infinispan.UserAdapter;
import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.representations.idm.ClientRepresentation;
@@ -89,6 +90,7 @@ public class UserStorageFailureTest {
realmName = appRealm.getName();
ClientModel offlineClient = appRealm.addClient("offline-client");
+ offlineClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
offlineClient.setEnabled(true);
offlineClient.setDirectAccessGrantsEnabled(true);
offlineClient.setSecret("secret");
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/helper/adapter/SamlAdapterTestStrategy.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/helper/adapter/SamlAdapterTestStrategy.java
index f25156a..b81b6fc 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/helper/adapter/SamlAdapterTestStrategy.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/helper/adapter/SamlAdapterTestStrategy.java
@@ -412,8 +412,8 @@ public class SamlAdapterTestStrategy extends ExternalResource {
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
ClientModel app = appRealm.getClientByClientId(APP_SERVER_BASE_URL + "/employee2/");
app.addProtocolMapper(GroupMembershipMapper.create("groups", "group", null, null, true));
- app.addProtocolMapper(UserAttributeStatementMapper.createAttributeMapper("topAttribute", "topAttribute", "topAttribute", "Basic", null, false, null));
- app.addProtocolMapper(UserAttributeStatementMapper.createAttributeMapper("level2Attribute", "level2Attribute", "level2Attribute", "Basic", null, false, null));
+ app.addProtocolMapper(UserAttributeStatementMapper.createAttributeMapper("topAttribute", "topAttribute", "topAttribute", "Basic", null));
+ app.addProtocolMapper(UserAttributeStatementMapper.createAttributeMapper("level2Attribute", "level2Attribute", "level2Attribute", "Basic", null));
}
}, "demo");
{
@@ -477,7 +477,7 @@ public class SamlAdapterTestStrategy extends ExternalResource {
app.addProtocolMapper(mapper);
}
}
- app.addProtocolMapper(HardcodedAttributeMapper.create("hardcoded-attribute", "hardcoded-attribute", "Basic", null, "hard", false, null));
+ app.addProtocolMapper(HardcodedAttributeMapper.create("hardcoded-attribute", "hardcoded-attribute", "Basic", null, "hard"));
app.addProtocolMapper(HardcodedRole.create("hardcoded-role", "hardcoded-role"));
app.addProtocolMapper(RoleNameMapper.create("renamed-role", "manager", "el-jefe"));
app.addProtocolMapper(RoleNameMapper.create("renamed-employee-role", APP_SERVER_BASE_URL + "/employee/.employee", "pee-on"));
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
index f1409f4..c126124 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
@@ -134,7 +134,6 @@ public class AbstractModelTest {
Assert.assertEquals(expected.getId(), actual.getId());
Assert.assertEquals(expected.getName(), actual.getName());
Assert.assertEquals(expected.getDescription(), actual.getDescription());
- Assert.assertEquals(expected.isScopeParamRequired(), actual.isScopeParamRequired());
Assert.assertEquals(expected.getContainer(), actual.getContainer());
Assert.assertEquals(expected.getComposites().size(), actual.getComposites().size());
}
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
index 90b1280..959c3f5 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClientModelTest.java
@@ -21,17 +21,22 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
+import org.keycloak.protocol.oidc.mappers.AddressMapper;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
+import org.keycloak.services.managers.ClientManager;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -53,6 +58,7 @@ public class ClientModelTest extends AbstractModelTest {
client.setBaseUrl("http://base");
client.setManagementUrl("http://management");
client.setClientId("app-name");
+ client.setProtocol("openid-connect");
client.addRole("role-1");
client.addRole("role-2");
client.addRole("role-3");
@@ -68,6 +74,8 @@ public class ClientModelTest extends AbstractModelTest {
client.registerNode("node1", 10);
client.registerNode("10.20.30.40", 50);
+ client.addProtocolMapper(AddressMapper.createAddressMapper());
+
client.updateClient();
}
@@ -192,23 +200,156 @@ public class ClientModelTest extends AbstractModelTest {
}
@Test
- public void testCannotRemoveBoundClientTemplate() {
+ public void testClientScopesBinding() {
ClientModel client = realm.addClient("templatized");
- ClientTemplateModel template = realm.addClientTemplate("template");
- client.setClientTemplate(template);
+ client.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ ClientScopeModel scope1 = realm.addClientScope("scope1");
+ scope1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ ClientScopeModel scope2 = realm.addClientScope("scope2");
+ scope2.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ ClientScopeModel scope3 = realm.addClientScope("scope3");
+ scope3.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+
commit();
+
+ // Add some clientScope bindings
+ realm = realmManager.getRealmByName("original");
+ client = realm.getClientByClientId("templatized");
+ scope1 = realm.getClientScopeById(scope1.getId());
+ scope2 = realm.getClientScopeById(scope2.getId());
+ scope3 = realm.getClientScopeById(scope3.getId());
+
+ client.addClientScope(scope1, true);
+ client.addClientScope(scope2, false);
+ client.addClientScope(scope3, false);
+ commit();
+
+ // Test that clientScope bindings are available
+ realm = realmManager.getRealmByName("original");
+ client = realm.getClientByClientId("templatized");
+
+ Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true, true);
+ Assert.assertTrue(clientScopes1.containsKey("scope1"));
+ Assert.assertFalse(clientScopes1.containsKey("scope2"));
+ Assert.assertFalse(clientScopes1.containsKey("scope3"));
+
+ Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false, true);
+ Assert.assertFalse(clientScopes2.containsKey("scope1"));
+ Assert.assertTrue(clientScopes2.containsKey("scope2"));
+ Assert.assertTrue(clientScopes2.containsKey("scope3"));
+
+ // Remove some binding and check it was removed
+ client.removeClientScope(scope1);
+ client.removeClientScope(scope2);
+ commit();
+
realm = realmManager.getRealmByName("original");
+ client = realm.getClientByClientId("templatized");
+
+ clientScopes1 = client.getClientScopes(true, true);
+ Assert.assertFalse(clientScopes1.containsKey("scope1"));
+ Assert.assertFalse(clientScopes1.containsKey("scope2"));
+ Assert.assertFalse(clientScopes1.containsKey("scope3"));
+
+ clientScopes2 = client.getClientScopes(false, true);
+ Assert.assertFalse(clientScopes2.containsKey("scope1"));
+ Assert.assertFalse(clientScopes2.containsKey("scope2"));
+ Assert.assertTrue(clientScopes2.containsKey("scope3"));
+
+ // Check can't add when already added
try {
- realm.removeClientTemplate(template.getId());
+ client.addClientScope(scope3, true);
Assert.fail();
} catch (ModelException e) {
+ // Expected
+ }
+ }
+ @Test
+ public void testCannotRemoveBoundClientTemplate() {
+ ClientModel client = realm.addClient("templatized");
+ ClientScopeModel scope1 = realm.addClientScope("template");
+ client.addClientScope(scope1, true);
+ commit();
+ realm = realmManager.getRealmByName("original");
+ try {
+ realm.removeClientScope(scope1.getId());
+ Assert.fail();
+ } catch (ModelException e) {
+ // Expected
}
realm.removeClient(client.getId());
- realm.removeClientTemplate(template.getId());
+ realm.removeClientScope(scope1.getId());
commit();
}
+ @Test
+ public void testDefaultDefaultClientScopes() {
+ ClientScopeModel scope1 = realm.addClientScope("scope1");
+ scope1.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ ClientScopeModel scope2 = realm.addClientScope("scope2");
+ scope2.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ ClientScopeModel scope3 = realm.addClientScope("scope3");
+ scope3.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+
+ commit();
+
+ // Add some scopes to realm defaultDefaultClientScopes
+ realm = realmManager.getRealmByName("original");
+ scope1 = realm.getClientScopeById(scope1.getId());
+ scope2 = realm.getClientScopeById(scope2.getId());
+ scope3 = realm.getClientScopeById(scope3.getId());
+
+ realm.addDefaultClientScope(scope1, true);
+ realm.addDefaultClientScope(scope2, false);
+ realm.addDefaultClientScope(scope3, false);
+ commit();
+
+ // Add client
+ realm = realmManager.getRealmByName("original");
+ ClientModel client = realm.addClient("foo");
+ client.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ commit();
+
+ // Ensure that client has scopes attached
+ realm = realmManager.getRealmByName("original");
+ client = realm.getClientByClientId("foo");
+
+ Map<String, ClientScopeModel> clientScopes1 = client.getClientScopes(true, true);
+ Assert.assertTrue(clientScopes1.containsKey("scope1"));
+ Assert.assertFalse(clientScopes1.containsKey("scope2"));
+ Assert.assertFalse(clientScopes1.containsKey("scope3"));
+
+ Map<String, ClientScopeModel> clientScopes2 = client.getClientScopes(false, true);
+ Assert.assertFalse(clientScopes2.containsKey("scope1"));
+ Assert.assertTrue(clientScopes2.containsKey("scope2"));
+ Assert.assertTrue(clientScopes2.containsKey("scope3"));
+
+ // Remove some realm default client scopes
+ realm.removeDefaultClientScope(scope1);
+ realm.removeDefaultClientScope(scope2);
+ commit();
+
+ // Create client and ensure clientScopes not there
+ realm = realmManager.getRealmByName("original");
+ client = realm.addClient("foo2");
+ client.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ commit();
+
+ realm = realmManager.getRealmByName("original");
+ client = realm.getClientByClientId("foo2");
+
+ clientScopes1 = client.getClientScopes(true, true);
+ Assert.assertFalse(clientScopes1.containsKey("scope1"));
+ Assert.assertFalse(clientScopes1.containsKey("scope2"));
+ Assert.assertFalse(clientScopes1.containsKey("scope3"));
+
+ clientScopes2 = client.getClientScopes(false, true);
+ Assert.assertFalse(clientScopes2.containsKey("scope1"));
+ Assert.assertFalse(clientScopes2.containsKey("scope2"));
+ Assert.assertTrue(clientScopes2.containsKey("scope3"));
+ }
+
public static void assertEquals(ClientModel expected, ClientModel actual) {
Assert.assertEquals(expected.getClientId(), actual.getClientId());
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClusterInvalidationTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClusterInvalidationTest.java
index f11499e..d51f27e 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClusterInvalidationTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ClusterInvalidationTest.java
@@ -32,7 +32,7 @@ import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -190,9 +190,9 @@ public class ClusterInvalidationTest {
assertInvalidations(listener1realms.getInvalidationsAndClear(), 1, 1, "test.top.groups");
assertInvalidations(listener2realms.getInvalidationsAndClear(), 1, 1, "test.top.groups");
- logger.info("CREATE CLIENT TEMPLATE");
+ logger.info("CREATE CLIENT SCOPE");
realm = session1.realms().getRealmByName(REALM_NAME);
- realm.addClientTemplate("foo-template");
+ realm.addClientScope("foo-scope");
session1 = commit(server1, session1, true);
assertInvalidations(listener1realms.getInvalidationsAndClear(), 2, 3, realm.getId());
@@ -229,21 +229,21 @@ public class ClusterInvalidationTest {
assertInvalidations(listener1realms.getInvalidationsAndClear(), 2, 3, testApp.getId());
assertInvalidations(listener2realms.getInvalidationsAndClear(), 2, 3, testApp.getId());
- // Cache client template on server2
+ // Cache client scope on server2
KeycloakSession session2 = server2.startSession();
realm = session2.realms().getRealmByName(REALM_NAME);
- realm.getClientTemplates().get(0);
+ realm.getClientScopes().get(0);
- logger.info("UPDATE CLIENT TEMPLATE");
+ logger.info("UPDATE CLIENT SCOPE");
realm = session1.realms().getRealmByName(REALM_NAME);
- ClientTemplateModel clientTemplate = realm.getClientTemplates().get(0);
- clientTemplate.setDescription("bar");
+ ClientScopeModel clientScope = realm.getClientScopes().get(0);
+ clientScope.setDescription("bar");
session1 = commit(server1, session1, true);
- assertInvalidations(listener1realms.getInvalidationsAndClear(), 1, 1, clientTemplate.getId());
- assertInvalidations(listener2realms.getInvalidationsAndClear(), 1, 1, clientTemplate.getId());
+ assertInvalidations(listener1realms.getInvalidationsAndClear(), 1, 1, clientScope.getId());
+ assertInvalidations(listener2realms.getInvalidationsAndClear(), 1, 1, clientScope.getId());
// Nothing yet invalidated in user cache
assertInvalidations(listener1users.getInvalidationsAndClear(), 0, 0);
@@ -283,13 +283,13 @@ public class ClusterInvalidationTest {
cacheEverything();
- logger.info("REMOVE CLIENT TEMPLATE");
+ logger.info("REMOVE CLIENT SCOPE");
realm = session1.realms().getRealmByName(REALM_NAME);
- realm.removeClientTemplate(clientTemplate.getId());
+ realm.removeClientScope(clientScope.getId());
session1 = commit(server1, session1, true);
- assertInvalidations(listener1realms.getInvalidationsAndClear(), 2, 5, realm.getId(), clientTemplate.getId());
- assertInvalidations(listener2realms.getInvalidationsAndClear(), 2, 5, realm.getId(), clientTemplate.getId());
+ assertInvalidations(listener1realms.getInvalidationsAndClear(), 2, 5, realm.getId(), clientScope.getId());
+ assertInvalidations(listener2realms.getInvalidationsAndClear(), 2, 5, realm.getId(), clientScope.getId());
cacheEverything();
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index 2e3f162..3b18762 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -25,7 +25,7 @@ import org.keycloak.common.constants.KerberosConstants;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.Constants;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderModel;
@@ -42,6 +42,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.storage.UserStorageProvider;
@@ -72,316 +73,6 @@ public class ImportTest extends AbstractModelTest {
}
@Test
- public void install() throws Exception {
- RealmRepresentation rep = AbstractModelTest.loadJson("model/testrealm.json");
- rep.setId("demo");
- RealmModel realm = realmManager.importRealm(rep);
-
- // Commit after import
- commit();
-
- realm = realmManager.getRealm("demo");
- assertDataImportedInRealm(realmManager.getSession(), realm);
-
- commit();
-
- realm = realmManager.getRealm("demo");
- realmManager.removeRealm(realm);
- }
-
- // Moved to static method, so it's possible to test this from other places too (for example export-import tests)
- /*
- BIG HELPFUL HINT!!!!
- WHEN YOU MIGRATE THIS CLASS YOU DO NOT NEED TO MIGRATE THIS METHOD.
- IT HAS ALREADY BEEN IMPLEMENTED IN THE NEW ARQUILLIAN TESTSUTE.
- SEE org.keycloak.testsuite.exportimport.ExportImportUtil
- */
- public static void assertDataImportedInRealm(KeycloakSession session, RealmModel realm) {
- Assert.assertTrue(realm.isVerifyEmail());
- Assert.assertEquals(3600000, realm.getOfflineSessionIdleTimeout());
- Assert.assertEquals(1500, realm.getAccessTokenLifespanForImplicitFlow());
-
- List<RequiredCredentialModel> creds = realm.getRequiredCredentials();
- Assert.assertEquals(1, creds.size());
- RequiredCredentialModel cred = creds.get(0);
- Assert.assertEquals("password", cred.getFormLabel());
- Assert.assertEquals(4, realm.getDefaultRoles().size());
-
- Assert.assertNotNull(realm.getRole("foo"));
- Assert.assertNotNull(realm.getRole("bar"));
-
- UserModel user = session.users().getUserByUsername("loginclient", realm);
- Assert.assertNotNull(user);
- Assert.assertEquals(0, session.users().getFederatedIdentities(user, realm).size());
-
- List<ClientModel> resources = realm.getClients();
- Assert.assertEquals(8, resources.size());
-
- // Test applications imported
- ClientModel application = realm.getClientByClientId("Application");
- ClientModel otherApp = realm.getClientByClientId("OtherApp");
- ClientModel accountApp = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
- ClientModel nonExisting = realm.getClientByClientId("NonExisting");
- Assert.assertNotNull(application);
- Assert.assertNotNull(otherApp);
- Assert.assertNull(nonExisting);
- List<ClientModel> clients = realm.getClients();
- Assert.assertEquals(8, clients.size());
- Assert.assertTrue(clients.contains(application));
- Assert.assertTrue(clients.contains(otherApp));
- Assert.assertTrue(clients.contains(accountApp));
- realm.getClients().containsAll(clients);
-
- Assert.assertEquals("Applicationn", application.getName());
- Assert.assertEquals(50, application.getNodeReRegistrationTimeout());
- Map<String, Integer> appRegisteredNodes = application.getRegisteredNodes();
- Assert.assertEquals(2, appRegisteredNodes.size());
- Assert.assertTrue(10 == appRegisteredNodes.get("node1"));
- Assert.assertTrue(20 == appRegisteredNodes.get("172.10.15.20"));
-
- // test clientAuthenticatorType
- Assert.assertEquals(application.getClientAuthenticatorType(), "client-secret");
- Assert.assertEquals(otherApp.getClientAuthenticatorType(), "client-jwt");
-
- // Test finding applications by ID
- Assert.assertNull(realm.getClientById("982734"));
- Assert.assertEquals(application, realm.getClientById(application.getId()));
-
-
- // Test role mappings
- UserModel admin = session.users().getUserByUsername("admin", realm);
- // user without creation timestamp in import
- Assert.assertNull(admin.getCreatedTimestamp());
- Set<RoleModel> allRoles = admin.getRoleMappings();
- Assert.assertEquals(3, allRoles.size());
- Assert.assertTrue(allRoles.contains(realm.getRole("admin")));
- Assert.assertTrue(allRoles.contains(application.getRole("app-admin")));
- Assert.assertTrue(allRoles.contains(otherApp.getRole("otherapp-admin")));
-
- Assert.assertTrue(application.getRole("app-admin").isScopeParamRequired());
- Assert.assertFalse(otherApp.getRole("otherapp-admin").isScopeParamRequired());
- Assert.assertFalse(otherApp.getRole("otherapp-user").isScopeParamRequired());
-
- UserModel wburke = session.users().getUserByUsername("wburke", realm);
- // user with creation timestamp in import
- Assert.assertEquals(new Long(123654), wburke.getCreatedTimestamp());
- allRoles = wburke.getRoleMappings();
- Assert.assertEquals(2, allRoles.size());
- Assert.assertFalse(allRoles.contains(realm.getRole("admin")));
- Assert.assertTrue(allRoles.contains(application.getRole("app-user")));
- Assert.assertTrue(allRoles.contains(otherApp.getRole("otherapp-user")));
-
- Assert.assertEquals(0, wburke.getRealmRoleMappings().size());
-
- UserModel loginclient = session.users().getUserByUsername("loginclient", realm);
- // user with creation timestamp as string in import
- Assert.assertEquals(new Long(123655), loginclient.getCreatedTimestamp());
-
- Set<RoleModel> realmRoles = admin.getRealmRoleMappings();
- Assert.assertEquals(1, realmRoles.size());
- Assert.assertEquals("admin", realmRoles.iterator().next().getName());
-
- Set<RoleModel> appRoles = admin.getClientRoleMappings(application);
- Assert.assertEquals(1, appRoles.size());
- Assert.assertEquals("app-admin", appRoles.iterator().next().getName());
-
- // Test attributes
- Map<String, List<String>> attrs = wburke.getAttributes();
- Assert.assertEquals(1, attrs.size());
- List<String> attrVals = attrs.get("email");
- Assert.assertEquals(1, attrVals.size());
- Assert.assertEquals("bburke@redhat.com", attrVals.get(0));
-
- attrs = admin.getAttributes();
- Assert.assertEquals(2, attrs.size());
- attrVals = attrs.get("key1");
- Assert.assertEquals(1, attrVals.size());
- Assert.assertEquals("val1", attrVals.get(0));
- attrVals = attrs.get("key2");
- Assert.assertEquals(2, attrVals.size());
- Assert.assertTrue(attrVals.contains("val21") && attrVals.contains("val22"));
-
- // Test client
- ClientModel oauthClient = realm.getClientByClientId("oauthclient");
- Assert.assertEquals("clientpassword", oauthClient.getSecret());
- Assert.assertEquals(true, oauthClient.isEnabled());
- Assert.assertNotNull(oauthClient);
-
- // Test scope relationship
- Set<RoleModel> allScopes = oauthClient.getScopeMappings();
- Assert.assertEquals(2, allScopes.size());
- Assert.assertTrue(allScopes.contains(realm.getRole("admin")));
- Assert.assertTrue(allScopes.contains(application.getRole("app-user")));
-
- Set<RoleModel> realmScopes = oauthClient.getRealmScopeMappings();
- Assert.assertTrue(realmScopes.contains(realm.getRole("admin")));
-
- Set<RoleModel> appScopes = KeycloakModelUtils.getClientScopeMappings(application, oauthClient);//application.getClientScopeMappings(oauthClient);
- Assert.assertTrue(appScopes.contains(application.getRole("app-user")));
-
-
- // Test social linking
- UserModel socialUser = session.users().getUserByUsername("mySocialUser", realm);
- Set<FederatedIdentityModel> socialLinks = session.users().getFederatedIdentities(socialUser, realm);
- Assert.assertEquals(3, socialLinks.size());
- boolean facebookFound = false;
- boolean googleFound = false;
- boolean twitterFound = false;
- for (FederatedIdentityModel federatedIdentityModel : socialLinks) {
- if ("facebook".equals(federatedIdentityModel.getIdentityProvider())) {
- facebookFound = true;
- Assert.assertEquals(federatedIdentityModel.getUserId(), "facebook1");
- Assert.assertEquals(federatedIdentityModel.getUserName(), "fbuser1");
- } else if ("google".equals(federatedIdentityModel.getIdentityProvider())) {
- googleFound = true;
- Assert.assertEquals(federatedIdentityModel.getUserId(), "google1");
- Assert.assertEquals(federatedIdentityModel.getUserName(), "mysocialuser@gmail.com");
- } else if ("twitter".equals(federatedIdentityModel.getIdentityProvider())) {
- twitterFound = true;
- Assert.assertEquals(federatedIdentityModel.getUserId(), "twitter1");
- Assert.assertEquals(federatedIdentityModel.getUserName(), "twuser1");
- }
- }
- Assert.assertTrue(facebookFound && twitterFound && googleFound);
-
- UserModel foundSocialUser = session.users().getUserByFederatedIdentity(new FederatedIdentityModel("facebook", "facebook1", "fbuser1"), realm);
- Assert.assertEquals(foundSocialUser.getUsername(), socialUser.getUsername());
- Assert.assertNull(session.users().getUserByFederatedIdentity(new FederatedIdentityModel("facebook", "not-existing", "not-existing"), realm));
-
- FederatedIdentityModel foundSocialLink = session.users().getFederatedIdentity(socialUser, "facebook", realm);
- Assert.assertEquals("facebook1", foundSocialLink.getUserId());
- Assert.assertEquals("fbuser1", foundSocialLink.getUserName());
- Assert.assertEquals("facebook", foundSocialLink.getIdentityProvider());
-
- // Test removing social link
- Assert.assertTrue(session.users().removeFederatedIdentity(realm, socialUser, "facebook"));
- Assert.assertNull(session.users().getFederatedIdentity(socialUser, "facebook", realm));
- Assert.assertFalse(session.users().removeFederatedIdentity(realm, socialUser, "facebook"));
- session.users().addFederatedIdentity(realm, socialUser, new FederatedIdentityModel("facebook", "facebook1", "fbuser1"));
-
- // Test smtp config
- Map<String, String> smtpConfig = realm.getSmtpConfig();
- Assert.assertTrue(smtpConfig.size() == 3);
- Assert.assertEquals("auto@keycloak.org", smtpConfig.get("from"));
- Assert.assertEquals("localhost", smtpConfig.get("host"));
- Assert.assertEquals("3025", smtpConfig.get("port"));
-
- // Test identity providers
- List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
- Assert.assertEquals(1, identityProviders.size());
- IdentityProviderModel google = identityProviders.get(0);
- Assert.assertEquals("google1", google.getAlias());
- Assert.assertEquals("google", google.getProviderId());
- Assert.assertTrue(google.isEnabled());
- Assert.assertEquals("googleId", google.getConfig().get("clientId"));
- Assert.assertEquals("googleSecret", google.getConfig().get("clientSecret"));
-
- // Test federation providers
- List<UserStorageProviderModel> storageProviders = realm.getUserStorageProviders();
- Assert.assertTrue(storageProviders.size() == 2);
- UserStorageProviderModel ldap1 = storageProviders.get(0);
- Assert.assertEquals("MyLDAPProvider1", ldap1.getName());
- Assert.assertEquals("ldap", ldap1.getProviderId());
- Assert.assertEquals(1, ldap1.getPriority());
- Assert.assertEquals("ldap://foo", ldap1.getConfig().getFirst(LDAPConstants.CONNECTION_URL));
-
- UserStorageProviderModel ldap2 = storageProviders.get(1);
- Assert.assertEquals("MyLDAPProvider2", ldap2.getName());
- Assert.assertEquals("ldap://bar", ldap2.getConfig().getFirst(LDAPConstants.CONNECTION_URL));
-
- // Test federation mappers
- List<ComponentModel> fedMappers1 = realm.getComponents(ldap1.getId());
- ComponentModel fullNameMapper = fedMappers1.iterator().next();
- Assert.assertEquals("FullNameMapper", fullNameMapper.getName());
- Assert.assertEquals(FullNameLDAPStorageMapperFactory.PROVIDER_ID, fullNameMapper.getProviderId());
- Assert.assertEquals(ldap1.getId(), fullNameMapper.getParentId());
- Assert.assertEquals("cn", fullNameMapper.getConfig().getFirst(FullNameLDAPStorageMapper.LDAP_FULL_NAME_ATTRIBUTE));
-
- // Assert that federation link wasn't created during import
- DummyUserFederationProviderFactory factory = (DummyUserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, "dummy");
- Assert.assertNull(factory.create(session, null).getUserByUsername("wburke", realm));
-
- // Test builtin authentication flows
- AuthenticationFlowModel clientFlow = realm.getClientAuthenticationFlow();
- Assert.assertEquals(DefaultAuthenticationFlows.CLIENT_AUTHENTICATION_FLOW, clientFlow.getAlias());
- Assert.assertNotNull(realm.getAuthenticationFlowById(clientFlow.getId()));
- Assert.assertTrue(realm.getAuthenticationExecutions(clientFlow.getId()).size() > 0);
-
- AuthenticationFlowModel resetFlow = realm.getResetCredentialsFlow();
- Assert.assertEquals(DefaultAuthenticationFlows.RESET_CREDENTIALS_FLOW, resetFlow.getAlias());
- Assert.assertNotNull(realm.getAuthenticationFlowById(resetFlow.getId()));
- Assert.assertTrue(realm.getAuthenticationExecutions(resetFlow.getId()).size() > 0);
-
- // Test protocol mappers. Default application has all the builtin protocol mappers. OtherApp just gss credential
- Assert.assertNotNull(application.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));
- Assert.assertNotNull(application.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "email"));
- Assert.assertNotNull(application.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "given name"));
- Assert.assertNull(application.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME));
-
- Assert.assertEquals(1, otherApp.getProtocolMappers().size());
- Assert.assertNull(otherApp.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "username"));
- ProtocolMapperModel gssCredentialMapper = otherApp.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
- assertGssProtocolMapper(gssCredentialMapper);
-
- // Test clientTemplates
- List<ClientTemplateModel> clientTemplates = realm.getClientTemplates();
- Assert.assertEquals(1, clientTemplates.size());
- ClientTemplateModel clientTemplate = clientTemplates.get(0);
- Assert.assertEquals("foo-template", clientTemplate.getName());
- Assert.assertEquals("foo-template-desc", clientTemplate.getDescription());
- Assert.assertEquals(OIDCLoginProtocol.LOGIN_PROTOCOL, clientTemplate.getProtocol());
- Assert.assertEquals(1, clientTemplate.getProtocolMappers().size());
- ProtocolMapperModel templateGssCredentialMapper = clientTemplate.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME);
- assertGssProtocolMapper(templateGssCredentialMapper);
-
- // Test client template scopes
- Set<RoleModel> allClientTemplateScopes = clientTemplate.getScopeMappings();
- Assert.assertEquals(3, allClientTemplateScopes.size());
- Assert.assertTrue(allClientTemplateScopes.contains(realm.getRole("admin")));
- Assert.assertTrue(allClientTemplateScopes.contains(application.getRole("app-user")));
- Assert.assertTrue(allClientTemplateScopes.contains(application.getRole("app-admin")));
-
- Set<RoleModel> clientTemplateRealmScopes = clientTemplate.getRealmScopeMappings();
- Assert.assertTrue(clientTemplateRealmScopes.contains(realm.getRole("admin")));
-
- Set<RoleModel> clientTemplateAppScopes = KeycloakModelUtils.getClientScopeMappings(application, clientTemplate);//application.getClientScopeMappings(oauthClient);
- Assert.assertTrue(clientTemplateAppScopes.contains(application.getRole("app-user")));
- Assert.assertTrue(clientTemplateAppScopes.contains(application.getRole("app-admin")));
-
- // Test user consents
- admin = session.users().getUserByUsername("admin", realm);
- Assert.assertEquals(2, session.users().getConsents(realm, admin.getId()).size());
-
- UserConsentModel appAdminConsent = session.users().getConsentByClient(realm, admin.getId(), application.getId());
- Assert.assertEquals(2, appAdminConsent.getGrantedRoles().size());
- Assert.assertTrue(appAdminConsent.getGrantedProtocolMappers() == null || appAdminConsent.getGrantedProtocolMappers().isEmpty());
- Assert.assertTrue(appAdminConsent.isRoleGranted(realm.getRole("admin")));
- Assert.assertTrue(appAdminConsent.isRoleGranted(application.getRole("app-admin")));
-
- UserConsentModel otherAppAdminConsent = session.users().getConsentByClient(realm, admin.getId(), otherApp.getId());
- Assert.assertEquals(1, otherAppAdminConsent.getGrantedRoles().size());
- Assert.assertEquals(1, otherAppAdminConsent.getGrantedProtocolMappers().size());
- Assert.assertTrue(otherAppAdminConsent.isRoleGranted(realm.getRole("admin")));
- Assert.assertFalse(otherAppAdminConsent.isRoleGranted(application.getRole("app-admin")));
- Assert.assertTrue(otherAppAdminConsent.isProtocolMapperGranted(gssCredentialMapper));
-
- Assert.assertTrue(application.isStandardFlowEnabled());
- Assert.assertTrue(application.isImplicitFlowEnabled());
- Assert.assertTrue(application.isDirectAccessGrantsEnabled());
- Assert.assertFalse(otherApp.isStandardFlowEnabled());
- Assert.assertFalse(otherApp.isImplicitFlowEnabled());
- Assert.assertFalse(otherApp.isDirectAccessGrantsEnabled());
-
- // Test service accounts
- Assert.assertFalse(application.isServiceAccountsEnabled());
- Assert.assertTrue(otherApp.isServiceAccountsEnabled());
- Assert.assertNull(session.users().getServiceAccount(application));
- UserModel linked = session.users().getServiceAccount(otherApp);
- Assert.assertNotNull(linked);
- Assert.assertEquals("my-service-user", linked.getUsername());
- }
-
- @Test
public void install2() throws Exception {
RealmManager manager = realmManager;
RealmRepresentation rep = AbstractModelTest.loadJson("model/testrealm-demo.json");
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
index ced16a0..687d903 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
@@ -22,6 +22,7 @@ import org.junit.Before;
import org.junit.Test;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -29,6 +30,7 @@ import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.storage.client.ClientStorageProviderModel;
@@ -50,33 +52,21 @@ public class UserConsentModelTest extends AbstractModelTest {
ClientModel fooClient = realm.addClient("foo-client");
ClientModel barClient = realm.addClient("bar-client");
- RoleModel realmRole = realm.addRole("realm-role");
- RoleModel barClientRole = barClient.addRole("bar-client-role");
+ ClientScopeModel fooScope = realm.addClientScope("foo");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- ProtocolMapperModel fooMapper = new ProtocolMapperModel();
- fooMapper.setName("foo");
- fooMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- fooMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
- fooMapper = fooClient.addProtocolMapper(fooMapper);
-
- ProtocolMapperModel barMapper = new ProtocolMapperModel();
- barMapper.setName("bar");
- barMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- barMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
- barMapper = barClient.addProtocolMapper(barMapper);
+ ClientScopeModel barScope = realm.addClientScope("bar");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
UserModel john = session.users().addUser(realm, "john");
UserModel mary = session.users().addUser(realm, "mary");
UserConsentModel johnFooGrant = new UserConsentModel(fooClient);
- johnFooGrant.addGrantedRole(realmRole);
- johnFooGrant.addGrantedRole(barClientRole);
- johnFooGrant.addGrantedProtocolMapper(fooMapper);
+ johnFooGrant.addGrantedClientScope(fooScope);
realmManager.getSession().users().addConsent(realm, john.getId(), johnFooGrant);
UserConsentModel johnBarGrant = new UserConsentModel(barClient);
- johnBarGrant.addGrantedProtocolMapper(barMapper);
- johnBarGrant.addGrantedRole(realmRole);
+ johnBarGrant.addGrantedClientScope(barScope);
// Update should fail as grant doesn't yet exists
try {
@@ -88,8 +78,7 @@ public class UserConsentModelTest extends AbstractModelTest {
realmManager.getSession().users().addConsent(realm, john.getId(), johnBarGrant);
UserConsentModel maryFooGrant = new UserConsentModel(fooClient);
- maryFooGrant.addGrantedRole(realmRole);
- maryFooGrant.addGrantedProtocolMapper(fooMapper);
+ maryFooGrant.addGrantedClientScope(fooScope);
realmManager.getSession().users().addConsent(realm, mary.getId(), maryFooGrant);
ClientStorageProviderModel clientStorage = new ClientStorageProviderModel();
@@ -121,35 +110,26 @@ public class UserConsentModelTest extends AbstractModelTest {
UserModel mary = session.users().getUserByUsername("mary", realm);
UserConsentModel johnFooConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 2);
- Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
- Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
Assert.assertNotNull("Created Date should be set", johnFooConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", johnFooConsent.getLastUpdatedDate());
UserConsentModel johnBarConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId());
- Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent));
- Assert.assertTrue(isMapperGranted(barClient, "bar", johnBarConsent));
+ Assert.assertEquals(johnBarConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "bar", johnBarConsent));
Assert.assertNotNull("Created Date should be set", johnBarConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", johnBarConsent.getLastUpdatedDate());
UserConsentModel maryConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), fooClient.getId());
- Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
- Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
UserConsentModel maryHardcodedConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId());
- Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
- Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
Assert.assertNotNull("Created Date should be set", maryHardcodedConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", maryHardcodedConsent.getLastUpdatedDate());
@@ -180,34 +160,25 @@ public class UserConsentModelTest extends AbstractModelTest {
}
Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId());
- Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
Assert.assertEquals(maryHardcodedConsent.getClient().getId(), hardcodedClient.getId());
- Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
- Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
}
@Test
- public void updateWithRoleRemovalTest() {
+ public void updateWithClientScopeRemovalTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(1, johnConsent.getGrantedClientScopes().size());
// Remove foo protocol mapper from johnConsent
- ProtocolMapperModel protMapperModel = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
- johnConsent.getGrantedProtocolMappers().remove(protMapperModel);
-
- // Remove realm-role and add new-realm-role to johnConsent
- RoleModel realmRole = realm.getRole("realm-role");
- johnConsent.getGrantedRoles().remove(realmRole);
-
- RoleModel newRealmRole = realm.addRole("new-realm-role");
- johnConsent.addGrantedRole(newRealmRole);
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ johnConsent.getGrantedClientScopes().remove(fooScope);
realmManager.getSession().users().updateConsent(realm, john.getId(), johnConsent);
@@ -218,11 +189,7 @@ public class UserConsentModelTest extends AbstractModelTest {
john = session.users().getUserByUsername("john", realm);
johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
- Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
- Assert.assertFalse(isRoleGranted(realm, "realm-role", johnConsent));
- Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent));
- Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent));
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate());
}
@@ -257,11 +224,11 @@ public class UserConsentModelTest extends AbstractModelTest {
}
@Test
- public void deleteProtocolMapperTest() {
+ public void deleteClientScopeTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
- ProtocolMapperModel fooMapper = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
- fooClient.removeProtocolMapper(fooMapper);
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ realm.removeClientScope(fooScope.getId());
commit();
@@ -270,29 +237,7 @@ public class UserConsentModelTest extends AbstractModelTest {
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
- Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
- Assert.assertFalse(johnConsent.isProtocolMapperGranted(fooMapper));
- }
-
- @Test
- public void deleteRoleTest() {
- RealmModel realm = realmManager.getRealm("original");
- RoleModel realmRole = realm.getRole("realm-role");
- realm.removeRole(realmRole);
-
- commit();
-
- realm = realmManager.getRealm("original");
- ClientModel fooClient = realm.getClientByClientId("foo-client");
- ClientModel barClient = realm.getClientByClientId("bar-client");
- UserModel john = session.users().getUserByUsername("john", realm);
- UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
-
- Assert.assertEquals(johnConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertFalse(johnConsent.isRoleGranted(realmRole));
- Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnConsent));
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
}
@Test
@@ -310,10 +255,8 @@ public class UserConsentModelTest extends AbstractModelTest {
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnFooConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId()));
}
@@ -336,13 +279,8 @@ public class UserConsentModelTest extends AbstractModelTest {
Assert.assertEquals(1, maryConsents.size());
}
- private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, UserConsentModel consentModel) {
- RoleModel role = roleContainer.getRole(roleName);
- return consentModel.isRoleGranted(role);
- }
-
- private boolean isMapperGranted(ClientModel client, String protocolMapperName, UserConsentModel consentModel) {
- ProtocolMapperModel protocolMapper = client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, protocolMapperName);
- return consentModel.isProtocolMapperGranted(protocolMapper);
+ private boolean isClientScopeGranted(RealmModel realm, String scopeName, UserConsentModel consentModel) {
+ ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, scopeName);
+ return consentModel.isClientScopeGranted(clientScope);
}
}
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java
index 04b5cde..002d8da 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserConsentWithUserStorageModelTest.java
@@ -22,6 +22,7 @@ import org.junit.Before;
import org.junit.Test;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.ModelException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@@ -29,6 +30,7 @@ import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
import org.keycloak.storage.UserStorageProviderModel;
@@ -60,33 +62,21 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
ClientModel fooClient = realm.addClient("foo-client");
ClientModel barClient = realm.addClient("bar-client");
- RoleModel realmRole = realm.addRole("realm-role");
- RoleModel barClientRole = barClient.addRole("bar-client-role");
+ ClientScopeModel fooScope = realm.addClientScope("foo");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- ProtocolMapperModel fooMapper = new ProtocolMapperModel();
- fooMapper.setName("foo");
- fooMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- fooMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
- fooMapper = fooClient.addProtocolMapper(fooMapper);
-
- ProtocolMapperModel barMapper = new ProtocolMapperModel();
- barMapper.setName("bar");
- barMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
- barMapper.setProtocolMapper(UserPropertyMapper.PROVIDER_ID);
- barMapper = barClient.addProtocolMapper(barMapper);
+ ClientScopeModel barScope = realm.addClientScope("bar");
+ fooScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
UserModel john = session.users().addUser(realm, "john");
UserModel mary = session.users().addUser(realm, "mary");
UserConsentModel johnFooGrant = new UserConsentModel(fooClient);
- johnFooGrant.addGrantedRole(realmRole);
- johnFooGrant.addGrantedRole(barClientRole);
- johnFooGrant.addGrantedProtocolMapper(fooMapper);
+ johnFooGrant.addGrantedClientScope(fooScope);
realmManager.getSession().users().addConsent(realm, john.getId(), johnFooGrant);
UserConsentModel johnBarGrant = new UserConsentModel(barClient);
- johnBarGrant.addGrantedProtocolMapper(barMapper);
- johnBarGrant.addGrantedRole(realmRole);
+ johnBarGrant.addGrantedClientScope(barScope);
// Update should fail as grant doesn't yet exists
try {
@@ -98,8 +88,7 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
realmManager.getSession().users().addConsent(realm, john.getId(), johnBarGrant);
UserConsentModel maryFooGrant = new UserConsentModel(fooClient);
- maryFooGrant.addGrantedRole(realmRole);
- maryFooGrant.addGrantedProtocolMapper(fooMapper);
+ maryFooGrant.addGrantedClientScope(fooScope);
realmManager.getSession().users().addConsent(realm, mary.getId(), maryFooGrant);
ClientStorageProviderModel clientStorage = new ClientStorageProviderModel();
@@ -131,35 +120,26 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
UserModel mary = session.users().getUserByUsername("mary", realm);
UserConsentModel johnFooConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 2);
- Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
- Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
Assert.assertNotNull("Created Date should be set", johnFooConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", johnFooConsent.getLastUpdatedDate());
UserConsentModel johnBarConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId());
- Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent));
- Assert.assertTrue(isMapperGranted(barClient, "bar", johnBarConsent));
+ Assert.assertEquals(johnBarConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "bar", johnBarConsent));
Assert.assertNotNull("Created Date should be set", johnBarConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", johnBarConsent.getLastUpdatedDate());
UserConsentModel maryConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), fooClient.getId());
- Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
- Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
ClientModel hardcodedClient = session.realms().getClientByClientId("hardcoded-client", realm);
UserConsentModel maryHardcodedConsent = realmManager.getSession().users().getConsentByClient(realm, mary.getId(), hardcodedClient.getId());
- Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
- Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
Assert.assertNotNull("Created Date should be set", maryHardcodedConsent.getCreatedDate());
Assert.assertNotNull("Last Updated Date should be set", maryHardcodedConsent.getLastUpdatedDate());
@@ -190,34 +170,25 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
}
Assert.assertEquals(maryConsent.getClient().getId(), fooClient.getId());
- Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(maryConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
+ Assert.assertEquals(maryConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", maryConsent));
Assert.assertEquals(maryHardcodedConsent.getClient().getId(), hardcodedClient.getId());
- Assert.assertEquals(maryHardcodedConsent.getGrantedRoles().size(), 0);
- Assert.assertEquals(maryHardcodedConsent.getGrantedProtocolMappers().size(), 0);
+ Assert.assertEquals(maryHardcodedConsent.getGrantedClientScopes().size(), 0);
}
@Test
- public void updateWithRoleRemovalTest() {
+ public void updateWithClientScopeRemovalTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
+ Assert.assertEquals(1, johnConsent.getGrantedClientScopes().size());
// Remove foo protocol mapper from johnConsent
- ProtocolMapperModel protMapperModel = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
- johnConsent.getGrantedProtocolMappers().remove(protMapperModel);
-
- // Remove realm-role and add new-realm-role to johnConsent
- RoleModel realmRole = realm.getRole("realm-role");
- johnConsent.getGrantedRoles().remove(realmRole);
-
- RoleModel newRealmRole = realm.addRole("new-realm-role");
- johnConsent.addGrantedRole(newRealmRole);
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ johnConsent.getGrantedClientScopes().remove(fooScope);
realmManager.getSession().users().updateConsent(realm, john.getId(), johnConsent);
@@ -228,11 +199,7 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
john = session.users().getUserByUsername("john", realm);
johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
- Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
- Assert.assertFalse(isRoleGranted(realm, "realm-role", johnConsent));
- Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent));
- Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent));
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate());
}
@@ -267,11 +234,11 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
}
@Test
- public void deleteProtocolMapperTest() {
+ public void deleteClientScopeTest() {
RealmModel realm = realmManager.getRealm("original");
ClientModel fooClient = realm.getClientByClientId("foo-client");
- ProtocolMapperModel fooMapper = fooClient.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, "foo");
- fooClient.removeProtocolMapper(fooMapper);
+ ClientScopeModel fooScope = KeycloakModelUtils.getClientScopeByName(realm, "foo");
+ realm.removeClientScope(fooScope.getId());
commit();
@@ -280,29 +247,7 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnConsent.getGrantedRoles().size(), 2);
- Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 0);
- Assert.assertFalse(johnConsent.isProtocolMapperGranted(fooMapper));
- }
-
- @Test
- public void deleteRoleTest() {
- RealmModel realm = realmManager.getRealm("original");
- RoleModel realmRole = realm.getRole("realm-role");
- realm.removeRole(realmRole);
-
- commit();
-
- realm = realmManager.getRealm("original");
- ClientModel fooClient = realm.getClientByClientId("foo-client");
- ClientModel barClient = realm.getClientByClientId("bar-client");
- UserModel john = session.users().getUserByUsername("john", realm);
- UserConsentModel johnConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
-
- Assert.assertEquals(johnConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(johnConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertFalse(johnConsent.isRoleGranted(realmRole));
- Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnConsent));
+ Assert.assertEquals(johnConsent.getGrantedClientScopes().size(), 0);
}
@Test
@@ -320,10 +265,8 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
UserModel john = session.users().getUserByUsername("john", realm);
UserConsentModel johnFooConsent = realmManager.getSession().users().getConsentByClient(realm, john.getId(), fooClient.getId());
- Assert.assertEquals(johnFooConsent.getGrantedRoles().size(), 1);
- Assert.assertEquals(johnFooConsent.getGrantedProtocolMappers().size(), 1);
- Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
- Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
+ Assert.assertEquals(johnFooConsent.getGrantedClientScopes().size(), 1);
+ Assert.assertTrue(isClientScopeGranted(realm, "foo", johnFooConsent));
Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, john.getId(), barClient.getId()));
}
@@ -346,13 +289,8 @@ public class UserConsentWithUserStorageModelTest extends AbstractModelTest {
Assert.assertEquals(1, maryConsents.size());
}
- private boolean isRoleGranted(RoleContainerModel roleContainer, String roleName, UserConsentModel consentModel) {
- RoleModel role = roleContainer.getRole(roleName);
- return consentModel.isRoleGranted(role);
- }
-
- private boolean isMapperGranted(ClientModel client, String protocolMapperName, UserConsentModel consentModel) {
- ProtocolMapperModel protocolMapper = client.getProtocolMapperByName(OIDCLoginProtocol.LOGIN_PROTOCOL, protocolMapperName);
- return consentModel.isProtocolMapperGranted(protocolMapper);
+ private boolean isClientScopeGranted(RealmModel realm, String scopeName, UserConsentModel consentModel) {
+ ClientScopeModel clientScope = KeycloakModelUtils.getClientScopeByName(realm, scopeName);
+ return consentModel.isClientScopeGranted(clientScope);
}
}
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
index 9574052..172dd3f 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java
@@ -38,9 +38,7 @@ import org.keycloak.models.UserManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.testsuite.rule.KeycloakRule;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -172,12 +170,10 @@ public class UserSessionInitializerTest {
}
- private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
+ private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state) {
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
clientSession.setRedirectUri(redirect);
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
- if (roles != null) clientSession.setRoles(roles);
- if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
return clientSession;
}
@@ -185,22 +181,14 @@ public class UserSessionInitializerTest {
UserSessionModel[] sessions = new UserSessionModel[3];
sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
- Set<String> roles = new HashSet<String>();
- roles.add("one");
- roles.add("two");
-
- Set<String> protocolMappers = new HashSet<String>();
- protocolMappers.add("mapper-one");
- protocolMappers.add("mapper-two");
-
- createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
- createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state");
+ createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state");
sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state");
sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state");
resetSession();
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
index 2148991..8a3ffa9 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java
@@ -30,7 +30,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.session.UserSessionPersisterProvider;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
@@ -38,9 +37,7 @@ import org.keycloak.models.UserManager;
import org.keycloak.testsuite.rule.KeycloakRule;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -190,7 +187,7 @@ public class UserSessionPersisterProviderTest {
// create new clientSession
AuthenticatedClientSessionModel clientSession = createClientSession(realm.getClientByClientId("third-party"), session.sessions().getUserSession(realm, persistedSession.getId()),
- "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ "http://redirect", "state");
persister.createClientSession(clientSession, true);
resetSession();
@@ -231,7 +228,7 @@ public class UserSessionPersisterProviderTest {
session.users().addUser(fooRealm, "user3");
UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
- createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
resetSession();
@@ -265,8 +262,8 @@ public class UserSessionPersisterProviderTest {
session.users().addUser(fooRealm, "user3");
UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
- createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
- createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
+ createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state");
resetSession();
@@ -360,12 +357,10 @@ public class UserSessionPersisterProviderTest {
}
- private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
+ private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state) {
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
clientSession.setRedirectUri(redirect);
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
- if (roles != null) clientSession.setRoles(roles);
- if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
return clientSession;
}
@@ -373,22 +368,14 @@ public class UserSessionPersisterProviderTest {
UserSessionModel[] sessions = new UserSessionModel[3];
sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
- Set<String> roles = new HashSet<String>();
- roles.add("one");
- roles.add("two");
-
- Set<String> protocolMappers = new HashSet<String>();
- protocolMappers.add("mapper-one");
- protocolMappers.add("mapper-two");
-
- createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
- createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state");
+ createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state");
sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state");
sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state");
return sessions;
}
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
index 26b535a..4f3ca20 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java
@@ -31,7 +31,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.session.UserSessionPersisterProvider;
-import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
@@ -189,7 +188,7 @@ public class UserSessionProviderOfflineTest {
session.users().addUser(fooRealm, "user3");
UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
- AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
resetSession();
@@ -238,8 +237,8 @@ public class UserSessionProviderOfflineTest {
session.users().addUser(fooRealm, "user3");
UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
- createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
- createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
+ createClientSession(fooRealm.getClientByClientId("bar-app"), userSession, "http://redirect", "state");
resetSession();
@@ -299,7 +298,7 @@ public class UserSessionProviderOfflineTest {
session.users().addUser(fooRealm, "user3");
UserSessionModel userSession = session.sessions().createUserSession(fooRealm, session.users().getUserByUsername("user3", fooRealm), "user3", "127.0.0.1", "form", true, null, null);
- AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ AuthenticatedClientSessionModel clientSession = createClientSession(fooRealm.getClientByClientId("foo-app"), userSession, "http://redirect", "state");
resetSession();
@@ -414,12 +413,10 @@ public class UserSessionProviderOfflineTest {
persister = session.getProvider(UserSessionPersisterProvider.class);
}
- private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
+ private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state) {
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(client.getRealm(), client, userSession);
clientSession.setRedirectUri(redirect);
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
- if (roles != null) clientSession.setRoles(roles);
- if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
return clientSession;
}
@@ -435,14 +432,14 @@ public class UserSessionProviderOfflineTest {
protocolMappers.add("mapper-one");
protocolMappers.add("mapper-two");
- createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
- createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state");
+ createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state");
sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state");
sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state");
return sessions;
}
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
index 2d25a07..ff59b38 100755
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java
@@ -148,12 +148,6 @@ public class UserSessionProviderTest {
assertEquals(sessions[0].getId(), session1.getUserSession().getId());
assertEquals("http://redirect", session1.getRedirectUri());
assertEquals("state", session1.getNote(OIDCLoginProtocol.STATE_PARAM));
- assertEquals(2, session1.getRoles().size());
- assertTrue(session1.getRoles().contains("one"));
- assertTrue(session1.getRoles().contains("two"));
- assertEquals(2, session1.getProtocolMappers().size());
- assertTrue(session1.getProtocolMappers().contains("mapper-one"));
- assertTrue(session1.getProtocolMappers().contains("mapper-two"));
}
@Test
@@ -385,7 +379,6 @@ public class UserSessionProviderTest {
UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0." + i, "form", false, null, null);
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, realm.getClientByClientId("test-app"), userSession);
clientSession.setRedirectUri("http://redirect");
- clientSession.setRoles(new HashSet<String>());
clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, "state");
clientSession.setTimestamp(userSession.getStarted());
userSession.setLastSessionRefresh(userSession.getStarted());
@@ -407,7 +400,7 @@ public class UserSessionProviderTest {
public void testCreateAndGetInSameTransaction() {
ClientModel client = realm.getClientByClientId("test-app");
UserSessionModel userSession = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
- AuthenticatedClientSessionModel clientSession = createClientSession(client, userSession, "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ AuthenticatedClientSessionModel clientSession = createClientSession(client, userSession, "http://redirect", "state");
UserSessionModel userSessionLoaded = session.sessions().getUserSession(realm, userSession.getId());
AuthenticatedClientSessionModel clientSessionLoaded = userSessionLoaded.getAuthenticatedClientSessions().get(client.getId());
@@ -588,12 +581,10 @@ public class UserSessionProviderTest {
assertNotNull(session.sessions().getUserLoginFailure(realm, user2.getId()));
}
- private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
+ private AuthenticatedClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state) {
AuthenticatedClientSessionModel clientSession = session.sessions().createClientSession(realm, client, userSession);
clientSession.setRedirectUri(redirect);
if (state != null) clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, state);
- if (roles != null) clientSession.setRoles(roles);
- if (protocolMappers != null) clientSession.setProtocolMappers(protocolMappers);
return clientSession;
}
@@ -601,22 +592,14 @@ public class UserSessionProviderTest {
UserSessionModel[] sessions = new UserSessionModel[3];
sessions[0] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.1", "form", true, null, null);
- Set<String> roles = new HashSet<String>();
- roles.add("one");
- roles.add("two");
-
- Set<String> protocolMappers = new HashSet<String>();
- protocolMappers.add("mapper-one");
- protocolMappers.add("mapper-two");
-
- createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state", roles, protocolMappers);
- createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[0], "http://redirect", "state");
+ createClientSession(realm.getClientByClientId("third-party"), sessions[0], "http://redirect", "state");
sessions[1] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user1", realm), "user1", "127.0.0.2", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[1], "http://redirect", "state");
sessions[2] = session.sessions().createUserSession(realm, session.users().getUserByUsername("user2", realm), "user2", "127.0.0.3", "form", true, null, null);
- createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state", new HashSet<String>(), new HashSet<String>());
+ createClientSession(realm.getClientByClientId("test-app"), sessions[2], "http://redirect", "state");
resetSession();
diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
index fcf0ed8..2f97e92 100644
--- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
+++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
@@ -53,99 +53,4 @@ public class AccountApplicationsPage extends AbstractAccountPage {
driver.findElement(By.id("revoke-" + clientId)).click();
}
- public Map<String, AppEntry> getApplications() {
- Map<String, AppEntry> table = new HashMap<String, AppEntry>();
- for (WebElement r : driver.findElements(By.tagName("tr"))) {
- int count = 0;
- AppEntry currentEntry = null;
-
- for (WebElement col : r.findElements(By.tagName("td"))) {
- count++;
- switch (count) {
- case 1:
- currentEntry = new AppEntry();
- String client = col.getText();
- table.put(client, currentEntry);
- break;
- case 2:
- String rolesStr = col.getText();
- String[] roles = rolesStr.split(",");
- for (String role : roles) {
- role = role.trim();
- currentEntry.addAvailableRole(role);
- }
- break;
- case 3:
- rolesStr = col.getText();
- if (rolesStr.isEmpty()) break;
- roles = rolesStr.split(",");
- for (String role : roles) {
- role = role.trim();
- currentEntry.addGrantedRole(role);
- }
- break;
- case 4:
- String protMappersStr = col.getText();
- if (protMappersStr.isEmpty()) break;
- String[] protMappers = protMappersStr.split(",");
- for (String protMapper : protMappers) {
- protMapper = protMapper.trim();
- currentEntry.addMapper(protMapper);
- }
- break;
- case 5:
- String additionalGrant = col.getText();
- if (additionalGrant.isEmpty()) break;
- String[] grants = additionalGrant.split(",");
- for (String grant : grants) {
- grant = grant.trim();
- currentEntry.addAdditionalGrant(grant);
- }
- break;
- }
- }
- }
- table.remove("Application");
- return table;
- }
-
- public static class AppEntry {
-
- private final List<String> rolesAvailable = new ArrayList<String>();
- private final List<String> rolesGranted = new ArrayList<String>();
- private final List<String> protocolMappersGranted = new ArrayList<String>();
- private final List<String> additionalGrants = new ArrayList<>();
-
- private void addAvailableRole(String role) {
- rolesAvailable.add(role);
- }
-
- private void addGrantedRole(String role) {
- rolesGranted.add(role);
- }
-
- private void addMapper(String protocolMapper) {
- protocolMappersGranted.add(protocolMapper);
- }
-
- private void addAdditionalGrant(String grant) {
- additionalGrants.add(grant);
- }
-
- public List<String> getRolesGranted() {
- return rolesGranted;
- }
-
- public List<String> getRolesAvailable() {
- return rolesAvailable;
- }
-
- public List<String> getProtocolMappersGranted() {
- return protocolMappersGranted;
- }
-
- public List<String> getAdditionalGrants() {
- return additionalGrants;
- }
- }
}
diff --git a/testsuite/integration-deprecated/src/test/resources/broker-test/realm-with-oidc-property-mappers.json b/testsuite/integration-deprecated/src/test/resources/broker-test/realm-with-oidc-property-mappers.json
index 9d3c7ac..2ed192c 100755
--- a/testsuite/integration-deprecated/src/test/resources/broker-test/realm-with-oidc-property-mappers.json
+++ b/testsuite/integration-deprecated/src/test/resources/broker-test/realm-with-oidc-property-mappers.json
@@ -13,6 +13,7 @@
"enabled": true,
"fullScopeAllowed": true,
"secret": "secret",
+ "defaultClientScopes": [],
"redirectUris": [
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp-property-mappers/endpoint/*"
],
diff --git a/testsuite/integration-deprecated/src/test/resources/broker-test/test-broker-realm-with-saml.json b/testsuite/integration-deprecated/src/test/resources/broker-test/test-broker-realm-with-saml.json
index 50511d5..00236b6 100755
--- a/testsuite/integration-deprecated/src/test/resources/broker-test/test-broker-realm-with-saml.json
+++ b/testsuite/integration-deprecated/src/test/resources/broker-test/test-broker-realm-with-saml.json
@@ -27,7 +27,6 @@
"name": "role list",
"protocol": "saml",
"protocolMapper": "saml-role-list-mapper",
- "consentRequired": false,
"config": {
"attribute.name": "Role",
"attribute.nameformat": "Basic",
@@ -39,7 +38,6 @@
"name": "mobile",
"protocol": "saml",
"protocolMapper": "saml-user-attribute-mapper",
- "consentRequired": false,
"config": {
"user.attribute": "mobile",
"attribute.name": "mobile",
@@ -50,7 +48,6 @@
"name": "email",
"protocol": "saml",
"protocolMapper": "saml-user-property-mapper",
- "consentRequired": false,
"config": {
"user.attribute": "email",
"attribute.name": "urn:oid:1.2.840.113549.1.9.1",
diff --git a/testsuite/integration-deprecated/src/test/resources/saml/testsaml.json b/testsuite/integration-deprecated/src/test/resources/saml/testsaml.json
index e929c24..babfcd4 100755
--- a/testsuite/integration-deprecated/src/test/resources/saml/testsaml.json
+++ b/testsuite/integration-deprecated/src/test/resources/saml/testsaml.json
@@ -232,7 +232,6 @@
"name": "email",
"protocol": "saml",
"protocolMapper": "saml-user-property-mapper",
- "consentRequired": false,
"config": {
"user.attribute": "email",
"friendly.name": "email",
@@ -244,7 +243,6 @@
"name": "phone",
"protocol": "saml",
"protocolMapper": "saml-user-attribute-mapper",
- "consentRequired": false,
"config": {
"user.attribute": "phone",
"attribute.name": "phone",
@@ -255,7 +253,6 @@
"name": "role-list",
"protocol": "saml",
"protocolMapper": "saml-role-list-mapper",
- "consentRequired": false,
"config": {
"attribute.name": "Role",
"attribute.nameformat": "Basic",
diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java
index 9792f9d..130dffc 100644
--- a/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java
+++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/util/cli/TestCacheUtils.java
@@ -18,7 +18,7 @@
package org.keycloak.testsuite.util.cli;
import org.keycloak.models.ClientModel;
-import org.keycloak.models.ClientTemplateModel;
+import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
@@ -48,8 +48,8 @@ public class TestCacheUtils {
cacheGroupRecursive(realm, group);
}
- for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) {
- realm.getClientTemplateById(clientTemplate.getId());
+ for (ClientScopeModel clientScope : realm.getClientScopes()) {
+ realm.getClientScopeById(clientScope.getId());
}
for (UserModel user : session.users().getUsers(realm)) {
diff --git a/themes/src/main/resources/theme/base/account/applications.ftl b/themes/src/main/resources/theme/base/account/applications.ftl
index 0b09256..a8edc38 100755
--- a/themes/src/main/resources/theme/base/account/applications.ftl
+++ b/themes/src/main/resources/theme/base/account/applications.ftl
@@ -15,9 +15,8 @@
<thead>
<tr>
<td>${msg("application")}</td>
- <td>${msg("availablePermissions")}</td>
+ <td>${msg("availableRoles")}</td>
<td>${msg("grantedPermissions")}</td>
- <td>${msg("grantedPersonalInfo")}</td>
<td>${msg("additionalGrants")}</td>
<td>${msg("action")}</td>
</tr>
@@ -49,26 +48,7 @@
<td>
<#if application.client.consentRequired>
- <#list application.realmRolesGranted as role>
- <#if role.description??>${advancedMsg(role.description)}<#else>${advancedMsg(role.name)}</#if>
- <#if role_has_next>, </#if>
- </#list>
- <#list application.resourceRolesGranted?keys as resource>
- <#if application.realmRolesGranted?has_content>, </#if>
- <#list application.resourceRolesGranted[resource] as clientRole>
- <#if clientRole.roleDescription??>${advancedMsg(clientRole.roleDescription)}<#else>${advancedMsg(clientRole.roleName)}</#if>
- ${msg("inResource")} <strong><#if clientRole.clientName??>${advancedMsg(clientRole.clientName)}<#else>${clientRole.clientId}</#if></strong>
- <#if clientRole_has_next>, </#if>
- </#list>
- </#list>
- <#else>
- <strong>${msg("fullAccess")}</strong>
- </#if>
- </td>
-
- <td>
- <#if application.client.consentRequired>
- <#list application.claimsGranted as claim>
+ <#list application.clientScopesGranted as claim>
${advancedMsg(claim)}<#if claim_has_next>, </#if>
</#list>
<#else>
@@ -83,7 +63,7 @@
</td>
<td>
- <#if (application.client.consentRequired && application.claimsGranted?has_content) || application.additionalGrants?has_content>
+ <#if (application.client.consentRequired && application.clientScopesGranted?has_content) || application.additionalGrants?has_content>
<button type='submit' class='${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!}' id='revoke-${application.client.clientId}' name='clientId' value="${application.client.id}">${msg("revoke")}</button>
</#if>
</td>
diff --git a/themes/src/main/resources/theme/base/account/messages/messages_en.properties b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
index 06f6094..6297113 100755
--- a/themes/src/main/resources/theme/base/account/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -54,6 +54,13 @@ country=Country
emailVerified=Email verified
gssDelegationCredential=GSS Delegation Credential
+profileScopeConsentText=User profile
+emailScopeConsentText=Email address
+addressScopeConsentText=Address
+phoneScopeConsentText=Phone number
+offlineAccessScopeConsentText=Offline Access
+samlRoleListScopeConsentText=My Roles
+
role_admin=Admin
role_realm-admin=Realm Admin
role_create-realm=Create realm
@@ -107,7 +114,7 @@ sessions=Sessions
log=Log
application=Application
-availablePermissions=Available Permissions
+availableRoles=Available Roles
grantedPermissions=Granted Permissions
grantedPersonalInfo=Granted Personal Info
additionalGrants=Additional Grants
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index 4f7fdb9..7aaf3fe 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -249,6 +249,10 @@ client.name.tooltip=Specifies display name of the client. For example 'My Client
client.enabled.tooltip=Disabled clients cannot initiate a login or have obtain access tokens.
consent-required=Consent Required
consent-required.tooltip=If enabled users have to consent to client access.
+client.display-on-consent-screen=Display Client On Consent Screen
+client.display-on-consent-screen.tooltip=Applicable just if Consent Required is on. If this switch is off, then consent screen will contain just the consents corresponding to configured client scopes. If on, then there will be also one item on consent screen about this client itself
+client.consent-screen-text=Client Consent Screen Text
+client.consent-screen-text.tooltip=Applicable just if 'Display Client On Consent Screen' is on for this client. Contains the text, which will be on consent screen about permissions specific just for this client
client-protocol=Client Protocol
client-protocol.tooltip='OpenID connect' allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server.'SAML' enables web-based authentication and authorization scenarios including cross-domain single sign-on (SSO) and uses security tokens containing assertions to pass information.
access-type=Access Type
@@ -390,8 +394,6 @@ role-name=Role Name
composite=Composite
description=Description
no-client-roles-available=No client roles available
-scope-param-required=Scope Param Required
-scope-param-required.tooltip=This role will only be granted if scope parameter with role name is used during authorization/token request.
composite-roles=Composite Roles
composite-roles.tooltip=When this role is (un)assigned to a user any role associated with it will be (un)assigned implicitly.
realm-roles=Realm Roles
@@ -658,13 +660,15 @@ allowed-protocol-mappers.label=Allowed Protocol Mappers
allowed-protocol-mappers.tooltip=Whitelist of allowed protocol mapper providers. If there is an attempt to register client, which contains some protocol mappers, which were not whitelisted, then registration request will be rejected.
consent-required-for-all-mappers.label=Consent Required For Mappers
consent-required-for-all-mappers.tooltip=If on, then all newly registered protocol mappers will automatically have consentRequired switch on. This means that user will need to approve consent screen. NOTE: Consent screen is shown just if client has consentRequired switch on. So it's usually good to use this switch together with consent-required policy.
-allowed-client-templates.label=Allowed Client Templates
-allowed-client-templates.tooltip=Whitelist of the client templates, which can be used on newly registered client. Attempt to register client with some client template, which is not whitelisted, will be rejected. By default, the whitelist is empty, so there are not any client templates are allowed.
+allowed-client-scopes.label=Allowed Client Scopes
+allowed-client-scopes.tooltip=Whitelist of the client scopes, which can be used on newly registered client. Attempt to register client with some client scope, which is not whitelisted, will be rejected. By default, the whitelist is either empty or contains just realm default client scopes (based on 'Allow Default Scopes' configuration property)
+allow-default-scopes.label=Allow Default Scopes
+allow-default-scopes.tooltip=If on, then newly registered clients will be allowed to have client scopes mentioned in realm default client scopes or realm optional client scopes
max-clients.label=Max Clients Per Realm
max-clients.tooltip=It won't be allowed to register new client if count of existing clients in realm is same or bigger than configured limit.
-client-templates=Client Templates
-client-templates.tooltip=Client templates allow you to define common configuration that is shared between multiple clients
+client-scopes=Client Scopes
+client-scopes.tooltip=Client scopes allow you to define common set of protocol mappers and roles, that are shared between multiple clients
groups=Groups
@@ -726,11 +730,68 @@ select-a-role=Select a role
select-realm-role=Select realm role
client-roles.tooltip=Client roles that can be selected.
select-client-role=Select client role
-
-client-template=Client Template
-client-template.tooltip=Client template this client inherits configuration from
+
client-saml-endpoint=Client SAML Endpoint
-add-client-template=Add client template
+add-client-scope=Add client scope
+
+default-client-scopes=Default Client Scopes
+default-client-scopes.tooltip=Client Scopes, which will be added automatically to each created client
+default-client-scopes.default=Default Client Scopes
+default-client-scopes.default.tooltip=Allow to define client scopes, which will be added as default scopes to each created client
+default-client-scopes.default.available=Available Client Scopes
+default-client-scopes.default.available.tooltip=Client scopes, which are not yet assigned as realm default scopes or realm optional scopes
+default-client-scopes.default.assigned=Assigned Default Client Scopes
+default-client-scopes.default.assigned.tooltip=Client scopes, which will be added as default scopes to each created client
+default-client-scopes.optional=Optional Client Scopes
+default-client-scopes.optional.tooltip=Allow to define client scopes, which will be added as optional scopes to each created client
+default-client-scopes.optional.available=Available Client Scopes
+default-client-scopes.optional.available.tooltip=Client scopes, which are not yet assigned as realm default scopes or realm optional scopes
+default-client-scopes.optional.assigned=Assigned Optional Client Scopes
+default-client-scopes.optional.assigned.tooltip=Client scopes, which will be added as optional scopes to each created client
+
+client-scopes.setup=Setup
+client-scopes.setup.tooltip=Allow to setup client scopes linked to this client
+client-scopes.default=Default Client Scopes
+client-scopes.default.tooltip=Default client scopes are always applied when issuing tokens for this client. Protocol mappers and role scope mappings are always applied regardless of value of used scope parameter in OIDC Authorization request
+client-scopes.default.available=Available Client Scopes
+client-scopes.default.available.tooltip=Client scopes, which are not yet assigned as default scopes or optional scopes
+client-scopes.default.assigned=Assigned Default Client Scopes
+client-scopes.default.assigned.tooltip=Client scopes, which will be used as default scopes when generating tokens for this client
+client-scopes.optional=Optional Client Scopes
+client-scopes.optional.tooltip=Optional client scopes are applied when issuing tokens for this client, however just in case when they are requested by scope parameter in OIDC Authorization request
+client-scopes.optional.available=Available Client Scopes
+client-scopes.optional.available.tooltip=Client scopes, which are not yet assigned as default scopes or optional scopes
+client-scopes.optional.assigned=Assigned Optional Client Scopes
+client-scopes.optional.assigned.tooltip=Client scopes, which may be used as optional scopes when generating tokens for this client
+
+client-scopes.evaluate=Evaluate
+client-scopes.evaluate.tooltip=Allow to see all protocol mappers and role scope mappings, which will be used in the tokens issued to this client. Also allow to generate example access token based on provided scope parameter
+scope-parameter=Scope Parameter
+scope-parameter.tooltip=You can copy/paste this value of scope parameter and use it in initial OpenID Connect Authentication Request sent from this client adapter. Default client scopes and selected optional client scopes will be used when generating token issued for this client
+client-scopes.evaluate.scopes=Client Scopes
+client-scopes.evaluate.scopes.tooltip=Allow to select optional client scopes, which may be used when generating token issued for this client
+client-scopes.evaluate.scopes.available=Available Optional Client Scopes
+client-scopes.evaluate.scopes.available.tooltip=This contains Optional Client Scopes, which can be optionally used when issuing access token for this client
+client-scopes.evaluate.scopes.assigned=Selected Optional Client Scopes
+client-scopes.evaluate.scopes.assigned.tooltip=Selected Optional Client Scopes, which will be used when issuing access token for this client. You can see above what value of OAuth Scope Parameter need to be used when you want to have these optional client scopes applied when the initial OpenID Connect Authentication request will be sent from your client adapter
+client-scopes.evaluate.scopes.effective=Effective Client Scopes
+client-scopes.evaluate.scopes.effective.tooltip=Contains all default client scopes and selected optional scopes. All protocol mappers and role scope mappings of all those client scopes will be used when generating access token issued for your client
+client-scopes.evaluate.user.tooltip=Optionally select user, for whom the example access token will be generated. If you don't select any user, then example access token won't be generated during evaluation
+send-evaluation-request=Evaluate
+send-evaluation-request.tooltip=Click this to see all protocol mappers and role scope mappings, which will be used when issuing access token for this client. It will also optionally generate example access token in case that some user was selected
+
+evaluated-protocol-mappers=Effective Protocol Mappers
+evaluated-protocol-mappers.tooltip=Allow you to see all effective protocol mappers, which will be used when issuing token for this client. Contains also protocol mappers of selected optional client scopes. For each protocol mapper, you can see from which client scope it is inherited from
+evaluated-roles=Effective Role Scope Mappings
+evaluated-roles.tooltip=Allow you to see all effective roles scope mappings, which will be used when issuing token for this client. Contains also role scope mappings of selected optional client scopes
+parent-client-scope=Parent Client Scope
+client-scopes.evaluate.not-granted-roles=Not Granted Roles
+client-scopes.evaluate.not-granted-roles.tooltip=Client doesn't have scope mappings for these roles. Those roles won't be in the access token issued to this client even if authenticated user is member of them
+client-scopes.evaluate.granted-realm-effective-roles=Granted Effective Realm Roles
+client-scopes.evaluate.granted-realm-effective-roles.tooltip=Client has scope mappings for these roles. Those roles will be in the access token issued to this client if authenticated user is member of them
+client-scopes.evaluate.granted-client-effective-roles=Granted Effective Client Roles
+generated-access-token=Generated Access Token
+generated-access-token.tooltip=See the example token, which will be generated and sent to the client when selected user is authenticated. You can see claims and roles, which the token will contain based on the effective protocol mappers and role scope mappings and also based on the claims/roles assigned to user himself
manage=Manage
authentication=Authentication
@@ -746,11 +807,13 @@ client-storage=Client Storage
no-client-storage-providers-configured=No client storage providers configured
client-stores.tooltip=Keycloak can retrieve clients and their details from external stores.
-
-
-client-template.name.tooltip=Name of the client template. Must be unique in the realm
-client-template.description.tooltip=Description of the client template
-client-template.protocol.tooltip=Which SSO protocol configuration is being supplied by this client template
+client-scope.name.tooltip=Name of the client scope. Must be unique in the realm. Name shouldn't contain space characters as it's used as value of scope parameter
+client-scope.description.tooltip=Description of the client scope
+client-scope.protocol.tooltip=Which SSO protocol configuration is being supplied by this client scope
+client-scope.display-on-consent-screen=Display On Consent Screen
+client-scope.display-on-consent-screen.tooltip=If on, and this client scope is added to some client with consent required, then the text specified by 'Consent Screen Text' will be displayed on consent screen. If off, then this client scope won't be displayed on consent screen
+client-scope.consent-screen-text=Consent Screen Text
+client-scope.consent-screen-text.tooltip=Text, which will be shown on consent screen when this client scope is added to some client with consent required. Defaults to name of client scope if it's not filled
add-user-federation-provider=Add user federation provider
add-user-storage-provider=Add user storage provider
@@ -1042,8 +1105,7 @@ system-encoding=System Encoding
operating-system=Operating System
os-architecture=OS Architecture
spi=SPI
-granted-roles=Granted Roles
-granted-protocol-mappers=Granted Protocol Mappers
+granted-client-scopes=Granted Client Scopes
additional-grants=Additional Grants
consent-created-date=Created
consent-last-updated-date=Last updated
@@ -1318,7 +1380,6 @@ authz-evaluation-authorization-data=Response
authz-evaluation-authorization-data.tooltip=Represents a token carrying authorization data as a result of the processing of an authorization request. This representation is basically what Keycloak issues to clients asking for permissions. Check the 'authorization' claim for the permissions that were granted based on the current authorization request.
authz-show-authorization-data=Show Authorization Data
-kid=KID
keys=Keys
all=All
status=Status
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index b76e18f..6aaff54 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -937,9 +937,6 @@ module.config([ '$routeProvider', function($routeProvider) {
client : function(ClientLoader) {
return ClientLoader();
},
- templates : function(ClientTemplateListLoader) {
- return ClientTemplateListLoader();
- },
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
@@ -1001,60 +998,111 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ClientProtocolMapperCreateCtrl'
})
- .when('/realms/:realm/client-templates/:template/mappers', {
- templateUrl : resourceUrl + '/partials/client-template-mappers.html',
+ .when('/realms/:realm/clients/:client/client-scopes/setup-scopes', {
+ templateUrl : resourceUrl + '/partials/client-scopes-setup.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
- template : function(ClientTemplateLoader) {
- return ClientTemplateLoader();
+ client : function(ClientLoader) {
+ return ClientLoader();
+ },
+ clientScopes : function(ClientScopeListLoader) {
+ return ClientScopeListLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
+ },
+ clientDefaultClientScopes : function(ClientDefaultClientScopesLoader) {
+ return ClientDefaultClientScopesLoader();
+ },
+ clientOptionalClientScopes : function(ClientOptionalClientScopesLoader) {
+ return ClientOptionalClientScopesLoader();
}
},
- controller : 'ClientTemplateProtocolMapperListCtrl'
+ controller : 'ClientClientScopesSetupCtrl'
})
- .when('/realms/:realm/client-templates/:template/add-mappers', {
- templateUrl : resourceUrl + '/partials/client-template-mappers-add.html',
+ .when('/realms/:realm/clients/:client/client-scopes/evaluate-scopes', {
+ templateUrl : resourceUrl + '/partials/client-scopes-evaluate.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
- template : function(ClientTemplateLoader) {
- return ClientTemplateLoader();
+ client : function(ClientLoader) {
+ return ClientLoader();
+ },
+ clients : function(ClientListLoader) {
+ return ClientListLoader();
+ },
+ clientScopes : function(ClientScopeListLoader) {
+ return ClientScopeListLoader();
+ },
+ clientDefaultClientScopes : function(ClientDefaultClientScopesLoader) {
+ return ClientDefaultClientScopesLoader();
+ },
+ clientOptionalClientScopes : function(ClientOptionalClientScopesLoader) {
+ return ClientOptionalClientScopesLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
- controller : 'ClientTemplateAddBuiltinProtocolMapperCtrl'
+ controller : 'ClientClientScopesEvaluateCtrl'
})
- .when('/realms/:realm/client-templates/:template/mappers/:id', {
- templateUrl : resourceUrl + '/partials/client-template-protocol-mapper-detail.html',
+ .when('/realms/:realm/client-scopes/:clientScope/mappers', {
+ templateUrl : resourceUrl + '/partials/client-scope-mappers.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
- template : function(ClientTemplateLoader) {
- return ClientTemplateLoader();
+ clientScope : function(ClientScopeLoader) {
+ return ClientScopeLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
+ }
+ },
+ controller : 'ClientScopeProtocolMapperListCtrl'
+ })
+ .when('/realms/:realm/client-scopes/:clientScope/add-mappers', {
+ templateUrl : resourceUrl + '/partials/client-scope-mappers-add.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
},
- mapper : function(ClientTemplateProtocolMapperLoader) {
- return ClientTemplateProtocolMapperLoader();
+ clientScope : function(ClientScopeLoader) {
+ return ClientScopeLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ }
+ },
+ controller : 'ClientScopeAddBuiltinProtocolMapperCtrl'
+ })
+ .when('/realms/:realm/client-scopes/:clientScope/mappers/:id', {
+ templateUrl : resourceUrl + '/partials/client-scope-protocol-mapper-detail.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ clientScope : function(ClientScopeLoader) {
+ return ClientScopeLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ },
+ mapper : function(ClientScopeProtocolMapperLoader) {
+ return ClientScopeProtocolMapperLoader();
},
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
- controller : 'ClientTemplateProtocolMapperCtrl'
+ controller : 'ClientScopeProtocolMapperCtrl'
})
- .when('/create/client-template/:realm/:template/mappers', {
- templateUrl : resourceUrl + '/partials/client-template-protocol-mapper-detail.html',
+ .when('/create/client-scope/:realm/:clientScope/mappers', {
+ templateUrl : resourceUrl + '/partials/client-scope-protocol-mapper-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
@@ -1062,14 +1110,14 @@ module.config([ '$routeProvider', function($routeProvider) {
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
},
- template : function(ClientTemplateLoader) {
- return ClientTemplateLoader();
+ clientScope : function(ClientScopeLoader) {
+ return ClientScopeLoader();
},
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
- controller : 'ClientTemplateProtocolMapperCreateCtrl'
+ controller : 'ClientScopeProtocolMapperCreateCtrl'
})
.when('/realms/:realm/clients/:client/sessions', {
templateUrl : resourceUrl + '/partials/client-sessions.html',
@@ -1275,9 +1323,6 @@ module.config([ '$routeProvider', function($routeProvider) {
client : function(ClientLoader) {
return ClientLoader();
},
- templates : function(ClientTemplateListLoader) {
- return ClientTemplateListLoader();
- },
clients : function(ClientListLoader) {
return ClientListLoader();
}
@@ -1323,9 +1368,6 @@ module.config([ '$routeProvider', function($routeProvider) {
realm : function(RealmLoader) {
return RealmLoader();
},
- templates : function(ClientTemplateListLoader) {
- return ClientTemplateListLoader();
- },
clients : function(ClientListLoader) {
return ClientListLoader();
},
@@ -1347,9 +1389,6 @@ module.config([ '$routeProvider', function($routeProvider) {
realm : function(RealmLoader) {
return RealmLoader();
},
- templates : function(ClientTemplateListLoader) {
- return ClientTemplateListLoader();
- },
clients : function(ClientListLoader) {
return ClientListLoader();
},
@@ -1365,56 +1404,50 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ClientDetailCtrl'
})
- .when('/create/client-template/:realm', {
- templateUrl : resourceUrl + '/partials/client-template-detail.html',
+ .when('/create/client-scope/:realm', {
+ templateUrl : resourceUrl + '/partials/client-scope-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
- templates : function(ClientTemplateListLoader) {
- return ClientTemplateListLoader();
- },
- template : function() {
+ clientScope : function() {
return {};
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
- controller : 'ClientTemplateDetailCtrl'
+ controller : 'ClientScopeDetailCtrl'
})
- .when('/realms/:realm/client-templates/:template', {
- templateUrl : resourceUrl + '/partials/client-template-detail.html',
+ .when('/realms/:realm/client-scopes/:clientScope', {
+ templateUrl : resourceUrl + '/partials/client-scope-detail.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
- templates : function(ClientTemplateListLoader) {
- return ClientTemplateListLoader();
- },
- template : function(ClientTemplateLoader) {
- return ClientTemplateLoader();
+ clientScope : function(ClientScopeLoader) {
+ return ClientScopeLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
- controller : 'ClientTemplateDetailCtrl'
+ controller : 'ClientScopeDetailCtrl'
})
- .when('/realms/:realm/client-templates/:template/scope-mappings', {
- templateUrl : resourceUrl + '/partials/client-template-scope-mappings.html',
+ .when('/realms/:realm/client-scopes/:clientScope/scope-mappings', {
+ templateUrl : resourceUrl + '/partials/client-scope-scope-mappings.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
- template : function(ClientTemplateLoader) {
- return ClientTemplateLoader();
+ clientScope : function(ClientScopeLoader) {
+ return ClientScopeLoader();
},
clients : function(ClientListLoader) {
return ClientListLoader();
}
},
- controller : 'ClientTemplateScopeMappingCtrl'
+ controller : 'ClientScopeScopeMappingCtrl'
})
.when('/realms/:realm/clients', {
templateUrl : resourceUrl + '/partials/client-list.html',
@@ -1429,21 +1462,42 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'ClientListCtrl'
})
- .when('/realms/:realm/client-templates', {
- templateUrl : resourceUrl + '/partials/client-template-list.html',
+ .when('/realms/:realm/client-scopes', {
+ templateUrl : resourceUrl + '/partials/client-scope-list.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
- templates : function(ClientTemplateListLoader) {
- return ClientTemplateListLoader();
+ clientScopes : function(ClientScopeListLoader) {
+ return ClientScopeListLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
- controller : 'ClientTemplateListCtrl'
+ controller : 'ClientScopeListCtrl'
+ })
+ .when('/realms/:realm/default-client-scopes', {
+ templateUrl : resourceUrl + '/partials/client-scopes-realm-default.html',
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ clientScopes : function(ClientScopeListLoader) {
+ return ClientScopeListLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ },
+ realmDefaultClientScopes : function(RealmDefaultClientScopesLoader) {
+ return RealmDefaultClientScopesLoader();
+ },
+ realmOptionalClientScopes : function(RealmOptionalClientScopesLoader) {
+ return RealmOptionalClientScopesLoader();
+ }
+ },
+ controller : 'ClientScopesRealmDefaultCtrl'
})
.when('/import/client/:realm', {
templateUrl : resourceUrl + '/partials/client-import.html',
@@ -2501,12 +2555,12 @@ module.directive('kcTabsClient', function () {
}
});
-module.directive('kcTabsClientTemplate', function () {
+module.directive('kcTabsClientScope', function () {
return {
scope: true,
restrict: 'E',
replace: true,
- templateUrl: resourceUrl + '/templates/kc-tabs-client-template.html'
+ templateUrl: resourceUrl + '/templates/kc-tabs-client-scope.html'
}
});
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index 73a98c9..87cb434 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -829,7 +829,8 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, serv
}
});
-module.controller('ClientDetailCtrl', function($scope, realm, client, templates, flows, $route, serverInfo, Client, ClientDescriptionConverter, Components, ClientStorageOperations, $location, $modal, Dialog, Notifications) {
+
+module.controller('ClientDetailCtrl', function($scope, realm, client, flows, $route, serverInfo, Client, ClientDescriptionConverter, Components, ClientStorageOperations, $location, $modal, Dialog, Notifications) {
$scope.flows = [];
$scope.clientFlows = [];
var emptyFlow = {
@@ -856,12 +857,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
$scope.protocols = serverInfo.listProviderIds('login-protocol');
- $scope.templates = [ {name:'NONE'}];
- for (var i = 0; i < templates.length; i++) {
- var template = templates[i];
- $scope.templates.push(template);
- }
-
$scope.signatureAlgorithms = [
"RSA_SHA1",
"RSA_SHA256",
@@ -1081,6 +1076,14 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
$scope.tlsClientCertificateBoundAccessTokens = false;
}
}
+
+ if ($scope.client.attributes["display.on.consent.screen"]) {
+ if ($scope.client.attributes["display.on.consent.screen"] == "true") {
+ $scope.displayOnConsentScreen = true;
+ } else {
+ $scope.displayOnConsentScreen = false;
+ }
+ }
}
if (!$scope.create) {
@@ -1330,6 +1333,12 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
$scope.clientEdit.attributes["tls.client.certificate.bound.access.tokens"] = "false";
}
+ if ($scope.displayOnConsentScreen == true) {
+ $scope.clientEdit.attributes["display.on.consent.screen"] = "true";
+ } else {
+ $scope.clientEdit.attributes["display.on.consent.screen"] = "false";
+ }
+
$scope.clientEdit.protocol = $scope.protocol;
$scope.clientEdit.attributes['saml.signature.algorithm'] = $scope.signatureAlgorithm;
$scope.clientEdit.attributes['saml_name_id_format'] = $scope.nameIdFormat;
@@ -1356,16 +1365,9 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
};
});
-module.controller('CreateClientCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
+module.controller('CreateClientCtrl', function($scope, realm, client, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
$scope.protocols = serverInfo.listProviderIds('login-protocol');
$scope.create = true;
- $scope.templates = [ {name:'NONE'}];
- var templateNameMap = new Object();
- for (var i = 0; i < templates.length; i++) {
- var template = templates[i];
- templateNameMap[template.name] = template;
- $scope.templates.push(template);
- }
$scope.realm = realm;
@@ -1406,18 +1408,6 @@ module.controller('CreateClientCtrl', function($scope, realm, client, templates,
$scope.changed = true;
}
- $scope.changeTemplate = function() {
- if ($scope.client.clientTemplate == 'NONE') {
- $scope.protocol = 'openid-connect';
- $scope.client.protocol = 'openid-connect';
- $scope.client.clientTemplate = null;
-
- } else {
- var template = templateNameMap[$scope.client.clientTemplate];
- $scope.protocol = template.protocol;
- $scope.client.protocol = template.protocol;
- }
- }
$scope.changeProtocol = function() {
if ($scope.protocol == "openid-connect") {
$scope.client.protocol = "openid-connect";
@@ -1468,8 +1458,8 @@ module.controller('CreateClientCtrl', function($scope, realm, client, templates,
};
});
-module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, client, clients, templates, Notifications,
- Client, ClientTemplate,
+module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, client, clients, Notifications,
+ Client, ClientScope,
ClientRealmScopeMapping, ClientClientScopeMapping, ClientRole,
ClientAvailableRealmScopeMapping, ClientAvailableClientScopeMapping,
ClientCompositeRealmScopeMapping, ClientCompositeClientScopeMapping) {
@@ -1486,21 +1476,8 @@ module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, clien
$scope.clientMappings = [];
$scope.dummymodel = [];
- if (client.clientTemplate) {
- for (var i = 0; i < templates.length; i++) {
- if (templates[i].name == client.clientTemplate) {
- ClientTemplate.get({realm: realm.realm, template: templates[i].id}, function(data) {
- $scope.template = data;
- });
- break;
- }
- }
-
- }
-
$scope.hideRoleSelector = function() {
- return ($scope.client.useTemplateScope && $scope.template && template.fullScopeAllowed)
- || (!$scope.template && $scope.client.fullScopeAllowed);
+ return $scope.client.fullScopeAllowed;
}
$scope.changeFlag = function() {
@@ -1816,7 +1793,7 @@ module.controller('AddBuiltinProtocolMapperCtrl', function($scope, realm, client
});
-module.controller('ClientProtocolMapperListCtrl', function($scope, realm, client, templates, serverInfo,
+module.controller('ClientProtocolMapperListCtrl', function($scope, realm, client, serverInfo,
Client,
ClientProtocolMappersByProtocol, ClientProtocolMapper,
$route, Dialog, Notifications) {
@@ -1825,14 +1802,7 @@ module.controller('ClientProtocolMapperListCtrl', function($scope, realm, client
if (client.protocol == null) {
client.protocol = 'openid-connect';
}
- if (client.clientTemplate) {
- for (var i = 0; i < templates.length; i++) {
- if (client.clientTemplate == templates[i].name) {
- $scope.template = templates[i];
- break;
- }
- }
- }
+
$scope.changeFlag = function() {
Client.update({
realm : realm.realm,
@@ -2015,15 +1985,398 @@ module.controller('ClientProtocolMapperCreateCtrl', function($scope, realm, serv
});
-module.controller('ClientTemplateTabCtrl', function(Dialog, $scope, Current, Notifications, $location) {
- $scope.removeClientTemplate = function() {
- Dialog.confirmDelete($scope.template.name, 'client template', function() {
- $scope.template.$remove({
+
+module.controller('ClientClientScopesSetupCtrl', function($scope, realm, Realm, client, clientScopes, serverInfo,
+ clientDefaultClientScopes, ClientDefaultClientScopes, clientOptionalClientScopes, ClientOptionalClientScopes, $route, Notifications, $location) {
+ console.log('ClientClientScopesSetupCtrl');
+
+ $scope.realm = realm;
+ $scope.client = client;
+
+ $scope.clientDefaultClientScopes = clientDefaultClientScopes;
+ $scope.clientOptionalClientScopes = clientOptionalClientScopes;
+
+ $scope.availableClientScopes = [];
+ $scope.selectedDefaultClientScopes = [];
+ $scope.selectedDefDefaultClientScopes = [];
+
+ $scope.selectedOptionalClientScopes = [];
+ $scope.selectedDefOptionalClientScopes = [];
+
+ // Populate available client scopes. Available client scopes are neither already assigned to 'default' or 'optional'
+ for (var i = 0; i < clientScopes.length; i++) {
+ var clientScope = clientScopes[i];
+ var scopeName = clientScopes[i].name;
+
+ var available = true;
+ if (clientScope.protocol != client.protocol) {
+ available = false;
+ }
+
+ for (var j = 0; j < $scope.clientDefaultClientScopes.length; j++) {
+ if (scopeName === $scope.clientDefaultClientScopes[j].name) {
+ available = false;
+ }
+ }
+ for (var j = 0; j < $scope.clientOptionalClientScopes.length; j++) {
+ if (scopeName === $scope.clientOptionalClientScopes[j].name) {
+ available = false;
+ }
+ }
+
+ if (available) {
+ $scope.availableClientScopes.push(clientScope);
+ }
+ }
+
+ $scope.addDefaultClientScope = function () {
+
+ toAdd = $scope.selectedDefaultClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedDefaultClientScopes.length; i++) {
+ var currentScope = $scope.selectedDefaultClientScopes[i];
+
+ ClientDefaultClientScopes.update({
+ realm : realm.realm,
+ client : client.id,
+ clientScopeId : currentScope.id
+ }, function () {
+ toAdd = toAdd - 1;
+ if (toAdd === 0) {
+ $route.reload();
+ Notifications.success("Default scopes updated.");
+ }
+ });
+ }
+ };
+
+ $scope.deleteDefaultClientScope = function () {
+
+ toRemove = $scope.selectedDefDefaultClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedDefDefaultClientScopes.length; i++) {
+ var currentScope = $scope.selectedDefDefaultClientScopes[i];
+
+ ClientDefaultClientScopes.remove({
+ realm : realm.realm,
+ client : client.id,
+ clientScopeId : currentScope.id
+ }, function () {
+ toRemove = toRemove - 1;
+ if (toRemove === 0) {
+ $route.reload();
+ Notifications.success("Default scopes updated.");
+ }
+ });
+ }
+ };
+
+ $scope.addOptionalClientScope = function () {
+
+ toAdd = $scope.selectedOptionalClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedOptionalClientScopes.length; i++) {
+ var currentScope = $scope.selectedOptionalClientScopes[i];
+
+ ClientOptionalClientScopes.update({
+ realm : realm.realm,
+ client : client.id,
+ clientScopeId : currentScope.id
+ }, function () {
+ toAdd = toAdd - 1;
+ if (toAdd === 0) {
+ $route.reload();
+ Notifications.success("Optional scopes updated.");
+ }
+ });
+ }
+ };
+
+ $scope.deleteOptionalClientScope = function () {
+
+ toRemove = $scope.selectedDefOptionalClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedDefOptionalClientScopes.length; i++) {
+ var currentScope = $scope.selectedDefOptionalClientScopes[i];
+
+ ClientOptionalClientScopes.remove({
+ realm : realm.realm,
+ client : client.id,
+ clientScopeId : currentScope.id
+ }, function () {
+ toRemove = toRemove - 1;
+ if (toRemove === 0) {
+ $route.reload();
+ Notifications.success("Optional scopes updated.");
+ }
+ });
+ }
+ };
+
+});
+
+module.controller('ClientClientScopesEvaluateCtrl', function($scope, Realm, User, ClientEvaluateProtocolMappers, ClientEvaluateGrantedRoles,
+ ClientEvaluateNotGrantedRoles, ClientEvaluateGenerateExampleToken, realm, client, clients, clientScopes, serverInfo,
+ clientOptionalClientScopes, clientDefaultClientScopes, $route, $routeParams, $http, Notifications, $location) {
+
+ console.log('ClientClientScopesEvaluateCtrl');
+
+ var protocolMappers = serverInfo.protocolMapperTypes[client.protocol];
+ var mapperTypes = {};
+ for (var i = 0; i < protocolMappers.length; i++) {
+ mapperTypes[protocolMappers[i].id] = protocolMappers[i];
+ }
+ $scope.mapperTypes = mapperTypes;
+
+ $scope.realm = realm;
+ $scope.client = client;
+ $scope.clients = clients;
+ $scope.userId = null;
+
+ $scope.availableClientScopes = [];
+ $scope.assignedClientScopes = [];
+ $scope.selectedClientScopes = [];
+ $scope.selectedDefClientScopes = [];
+ $scope.effectiveClientScopes = [];
+
+ // Populate available client scopes. Available client scopes are neither already assigned to 'default' or 'optional'
+ for (var i = 0; i < clientOptionalClientScopes.length; i++) {
+ $scope.availableClientScopes.push(clientOptionalClientScopes[i]);
+ }
+
+ function clearEvalResponse() {
+ $scope.protocolMappers = null;
+ $scope.grantedRealmRoles = null;
+ $scope.notGrantedRealmRoles = null;
+ $scope.grantedClientRoles = null;
+ $scope.notGrantedClientRoles = null;
+ $scope.targetClient = null;
+ $scope.oidcAccessToken = null;
+
+ $scope.selectedTab = 0;
+ }
+
+ function updateState() {
+ // Compute scope parameter
+ $scope.scopeParam = 'openid';
+ for (var i = 0; i < $scope.assignedClientScopes.length; i++) {
+ var currentScopeParam = $scope.assignedClientScopes[i].name;
+ $scope.scopeParam = $scope.scopeParam + ' ' + currentScopeParam;
+ }
+
+ // Compute effective scopes
+ $scope.effectiveClientScopes = [];
+
+ for (var i = 0; i < clientDefaultClientScopes.length; i++) {
+ var currentScope = clientDefaultClientScopes[i];
+ $scope.effectiveClientScopes.push(currentScope);
+ }
+ for (var i = 0; i < $scope.assignedClientScopes.length; i++) {
+ var currentScope = $scope.assignedClientScopes[i];
+ $scope.effectiveClientScopes.push(currentScope);
+ }
+
+ // Clear the evaluation response
+ clearEvalResponse();
+ }
+
+ updateState();
+
+
+ $scope.addAppliedClientScope = function () {
+
+ for (var i = 0; i < $scope.selectedClientScopes.length; i++) {
+ var currentScope = $scope.selectedClientScopes[i];
+
+ $scope.assignedClientScopes.push(currentScope);
+
+ var index = $scope.availableClientScopes.indexOf(currentScope);
+ if (index > -1) {
+ $scope.availableClientScopes.splice(index, 1);
+ }
+ }
+
+ $scope.selectedClientScopes = [];
+
+ updateState();
+ };
+
+
+ $scope.deleteAppliedClientScope = function () {
+ for (var i = 0; i < $scope.selectedDefClientScopes.length; i++) {
+ var currentScope = $scope.selectedDefClientScopes[i];
+
+ $scope.availableClientScopes.push(currentScope);
+
+ var index = $scope.assignedClientScopes.indexOf(currentScope);
+ if (index > -1) {
+ $scope.assignedClientScopes.splice(index, 1);
+ }
+ }
+
+ $scope.selectedDefClientScopes = [];
+
+ updateState();
+ };
+
+ $scope.usersUiSelect = {
+ minimumInputLength: 1,
+ delay: 500,
+ allowClear: true,
+ query: function (query) {
+ var data = {results: []};
+ if ('' == query.term.trim()) {
+ query.callback(data);
+ return;
+ }
+ User.query({realm: $route.current.params.realm, search: query.term.trim(), max: 20}, function(response) {
+ data.results = response;
+ query.callback(data);
+ });
+ },
+ formatResult: function(object, container, query) {
+ object.text = object.username;
+ return object.username;
+ }
+ };
+
+ $scope.selectedUser = null;
+
+ $scope.selectUser = function(user) {
+ clearEvalResponse();
+
+ if (!user || !user.id) {
+ $scope.selectedUser = null;
+ $scope.userId = '';
+ return;
+ }
+
+ $scope.userId = user.id;
+ }
+
+
+ $scope.sendEvaluationRequest = function () {
+
+ // Send request for retrieve protocolMappers
+ $scope.protocolMappers = ClientEvaluateProtocolMappers.query({
+ realm: realm.realm,
+ client: client.id,
+ scopeParam: $scope.scopeParam
+ });
+
+ // Send request for retrieve realmRoles
+ updateScopeRealmRoles();
+
+ // Send request for retrieve accessToken (in case user was selected)
+ if (client.protocol === 'openid-connect' && $scope.userId != null && $scope.userId !== '') {
+ var url = ClientEvaluateGenerateExampleToken.url({
+ realm: realm.realm,
+ client: client.id,
+ userId: $scope.userId,
+ scopeParam: $scope.scopeParam
+ });
+
+ $http.get(url).then(function (response) {
+ if (response.data) {
+ var oidcAccessToken = angular.fromJson(response.data);
+ oidcAccessToken = angular.toJson(oidcAccessToken, true);
+ $scope.oidcAccessToken = oidcAccessToken;
+ } else {
+ $scope.oidcAccessToken = null;
+ }
+ });
+ }
+
+ $scope.showTab(1);
+ };
+
+
+ $scope.isResponseAvailable = function () {
+ return $scope.protocolMappers != null;
+ }
+
+ $scope.isTokenAvailable = function () {
+ return $scope.oidcAccessToken != null;
+ }
+
+ $scope.showTab = function (tab) {
+ $scope.selectedTab = tab;
+
+ // Check if there is more clever way to do it... :/
+ if (tab === 1) {
+ $scope.tabCss = { tab1: 'active', tab2: '', tab3: '' }
+ } else if (tab === 2) {
+ $scope.tabCss = { tab1: '', tab2: 'active', tab3: '' }
+ } else if (tab === 3) {
+ $scope.tabCss = { tab1: '', tab2: '', tab3: 'active' }
+ }
+ }
+
+ $scope.protocolMappersShown = function () {
+ return $scope.selectedTab === 1;
+ }
+
+ $scope.rolesShown = function () {
+ return $scope.selectedTab === 2;
+ }
+
+ $scope.tokenShown = function () {
+ return $scope.selectedTab === 3;
+ }
+
+
+ // Roles
+
+ function updateScopeRealmRoles() {
+ $scope.grantedRealmRoles = ClientEvaluateGrantedRoles.query({
+ realm: realm.realm,
+ client: client.id,
+ roleContainer: realm.realm,
+ scopeParam: $scope.scopeParam
+ });
+ $scope.notGrantedRealmRoles = ClientEvaluateNotGrantedRoles.query({
+ realm: realm.realm,
+ client: client.id,
+ roleContainer: realm.realm,
+ scopeParam: $scope.scopeParam
+ });
+ }
+
+ function updateScopeClientRoles() {
+ if ($scope.targetClient) {
+ $scope.grantedClientRoles = ClientEvaluateGrantedRoles.query({
+ realm: realm.realm,
+ client: client.id,
+ roleContainer: $scope.targetClient.id,
+ scopeParam: $scope.scopeParam
+ });
+ $scope.notGrantedClientRoles = ClientEvaluateNotGrantedRoles.query({
+ realm: realm.realm,
+ client: client.id,
+ roleContainer: $scope.targetClient.id,
+ scopeParam: $scope.scopeParam
+ });
+ } else {
+ $scope.grantedClientRoles = null;
+ $scope.notGrantedClientRoles = null;
+ }
+ }
+
+ $scope.changeClient = function() {
+ updateScopeClientRoles();
+ };
+});
+
+
+module.controller('ClientScopeTabCtrl', function(Dialog, $scope, Current, Notifications, $location) {
+ $scope.removeClientScope = function() {
+ Dialog.confirmDelete($scope.clientScope.name, 'client scope', function() {
+ $scope.clientScope.$remove({
realm : Current.realm.realm,
- template : $scope.template.id
+ clientScope : $scope.clientScope.id
}, function() {
- $location.url("/realms/" + Current.realm.realm + "/client-templates");
- Notifications.success("The client template has been deleted.");
+ $location.url("/realms/" + Current.realm.realm + "/client-scopes");
+ Notifications.success("The client scope has been deleted.");
});
});
};
@@ -2031,46 +2384,179 @@ module.controller('ClientTemplateTabCtrl', function(Dialog, $scope, Current, Not
-module.controller('ClientTemplateListCtrl', function($scope, realm, templates, ClientTemplate, serverInfo, $route, Dialog, Notifications, $location) {
+module.controller('ClientScopeListCtrl', function($scope, realm, clientScopes, ClientScope, serverInfo, $route, Dialog, Notifications, $location) {
$scope.realm = realm;
- $scope.templates = templates;
+ $scope.clientScopes = clientScopes;
- $scope.removeClientTemplate = function(template) {
- Dialog.confirmDelete(template.name, 'client template', function() {
- ClientTemplate.remove({
+ $scope.removeClientScope = function(clientScope) {
+ Dialog.confirmDelete(clientScope.name, 'client scope', function() {
+ ClientScope.remove({
realm : realm.realm,
- template : template.id
+ clientScope : clientScope.id
}, function() {
$route.reload();
- Notifications.success("The client template been deleted.");
+ Notifications.success("The client scope been deleted.");
});
});
};
});
-module.controller('ClientTemplateDetailCtrl', function($scope, realm, template, $route, serverInfo, ClientTemplate, $location, $modal, Dialog, Notifications) {
+module.controller('ClientScopesRealmDefaultCtrl', function($scope, realm, Realm, clientScopes, realmDefaultClientScopes, RealmDefaultClientScopes,
+ realmOptionalClientScopes, RealmOptionalClientScopes, serverInfo, $route, Dialog, Notifications, $location) {
+
+ console.log('ClientScopesRealmDefaultCtrl');
+
+ $scope.realm = realm;
+ $scope.realmDefaultClientScopes = realmDefaultClientScopes;
+ $scope.realmOptionalClientScopes = realmOptionalClientScopes;
+
+ $scope.availableClientScopes = [];
+ $scope.selectedDefaultClientScopes = [];
+ $scope.selectedDefDefaultClientScopes = [];
+
+ $scope.selectedOptionalClientScopes = [];
+ $scope.selectedDefOptionalClientScopes = [];
+
+ // Populate available client scopes. Available client scopes are neither already assigned to 'default' or 'optional'
+ for (var i = 0; i < clientScopes.length; i++) {
+ var scopeName = clientScopes[i].name;
+
+ var available = true;
+ for (var j = 0; j < $scope.realmDefaultClientScopes.length; j++) {
+ if (scopeName === $scope.realmDefaultClientScopes[j].name) {
+ available = false;
+ }
+ }
+ for (var j = 0; j < $scope.realmOptionalClientScopes.length; j++) {
+ if (scopeName === $scope.realmOptionalClientScopes[j].name) {
+ available = false;
+ }
+ }
+
+ if (available) {
+ $scope.availableClientScopes.push(clientScopes[i]);
+ }
+ }
+
+ $scope.addDefaultClientScope = function () {
+
+ toAdd = $scope.selectedDefaultClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedDefaultClientScopes.length; i++) {
+ var currentScope = $scope.selectedDefaultClientScopes[i];
+
+ RealmDefaultClientScopes.update({
+ realm : realm.realm,
+ clientScopeId : currentScope.id
+ }, function () {
+ toAdd = toAdd - 1;
+ console.log('toAdd: ' + toAdd);
+ if (toAdd === 0) {
+ $route.reload();
+ Notifications.success("Realm default scopes updated.");
+ }
+ });
+ }
+ };
+
+ $scope.deleteDefaultClientScope = function () {
+
+ toRemove = $scope.selectedDefDefaultClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedDefDefaultClientScopes.length; i++) {
+ var currentScope = $scope.selectedDefDefaultClientScopes[i];
+
+ RealmDefaultClientScopes.remove({
+ realm : realm.realm,
+ clientScopeId : currentScope.id
+ }, function () {
+ toRemove = toRemove - 1;
+ if (toRemove === 0) {
+ $route.reload();
+ Notifications.success("Realm default scopes updated.");
+ }
+ });
+ }
+ };
+
+ $scope.addOptionalClientScope = function () {
+
+ toAdd = $scope.selectedOptionalClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedOptionalClientScopes.length; i++) {
+ var currentScope = $scope.selectedOptionalClientScopes[i];
+
+ RealmOptionalClientScopes.update({
+ realm : realm.realm,
+ clientScopeId : currentScope.id
+ }, function () {
+ toAdd = toAdd - 1;
+ console.log('toAdd: ' + toAdd);
+ if (toAdd === 0) {
+ $route.reload();
+ Notifications.success("Realm optional scopes updated.");
+ }
+ });
+ }
+ };
+
+ $scope.deleteOptionalClientScope = function () {
+
+ toRemove = $scope.selectedDefOptionalClientScopes.length;
+
+ for (var i = 0; i < $scope.selectedDefOptionalClientScopes.length; i++) {
+ var currentScope = $scope.selectedDefOptionalClientScopes[i];
+
+ RealmOptionalClientScopes.remove({
+ realm : realm.realm,
+ clientScopeId : currentScope.id
+ }, function () {
+ toRemove = toRemove - 1;
+ if (toRemove === 0) {
+ $route.reload();
+ Notifications.success("Realm optional scopes updated.");
+ }
+ });
+ }
+ };
+});
+
+module.controller('ClientScopeDetailCtrl', function($scope, realm, clientScope, $route, serverInfo, ClientScope, $location, $modal, Dialog, Notifications) {
$scope.protocols = serverInfo.listProviderIds('login-protocol');
$scope.realm = realm;
- $scope.create = !template.name;
+ $scope.create = !clientScope.name;
function updateProperties() {
- if ($scope.template.protocol) {
- $scope.protocol = $scope.protocols[$scope.protocols.indexOf($scope.template.protocol)];
+ if (!$scope.clientScope.attributes) {
+ $scope.clientScope.attributes = {};
+ }
+
+ if ($scope.clientScope.protocol) {
+ $scope.protocol = $scope.protocols[$scope.protocols.indexOf($scope.clientScope.protocol)];
} else {
$scope.protocol = $scope.protocols[0];
}
+
+ if ($scope.clientScope.attributes["display.on.consent.screen"]) {
+ if ($scope.clientScope.attributes["display.on.consent.screen"] == "true") {
+ $scope.displayOnConsentScreen = true;
+ } else {
+ $scope.displayOnConsentScreen = false;
+ }
+ } else {
+ $scope.displayOnConsentScreen = true;
+ }
}
if (!$scope.create) {
- $scope.template = angular.copy(template);
- updateProperties();
+ $scope.clientScope = angular.copy(clientScope);
} else {
- $scope.template = {
- };
- $scope.protocol = $scope.protocols[0];
+ $scope.clientScope = {};
}
+ updateProperties();
+
$scope.switchChange = function() {
$scope.changed = true;
@@ -2078,9 +2564,9 @@ module.controller('ClientTemplateDetailCtrl', function($scope, realm, template,
$scope.changeProtocol = function() {
if ($scope.protocol == "openid-connect") {
- $scope.template.protocol = "openid-connect";
+ $scope.clientScope.protocol = "openid-connect";
} else if ($scope.protocol == "saml") {
- $scope.template.protocol = "saml";
+ $scope.clientScope.protocol = "saml";
}
};
@@ -2091,39 +2577,45 @@ module.controller('ClientTemplateDetailCtrl', function($scope, realm, template,
});
function isChanged() {
- if (!angular.equals($scope.template, template)) {
+ if (!angular.equals($scope.clientScope, clientScope)) {
return true;
}
return false;
}
- $scope.$watch('template', function() {
+ $scope.$watch('clientScope', function() {
$scope.changed = isChanged();
}, true);
$scope.save = function() {
- $scope.template.protocol = $scope.protocol;
+ $scope.clientScope.protocol = $scope.protocol;
+
+ if ($scope.displayOnConsentScreen == true) {
+ $scope.clientScope.attributes["display.on.consent.screen"] = "true";
+ } else {
+ $scope.clientScope.attributes["display.on.consent.screen"] = "false";
+ }
if ($scope.create) {
- ClientTemplate.save({
+ ClientScope.save({
realm: realm.realm,
- template: ''
- }, $scope.template, function (data, headers) {
+ clientScope: ''
+ }, $scope.clientScope, function (data, headers) {
$scope.changed = false;
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
- $location.url("/realms/" + realm.realm + "/client-templates/" + id);
- Notifications.success("The client template has been created.");
+ $location.url("/realms/" + realm.realm + "/client-scopes/" + id);
+ Notifications.success("The client scope has been created.");
});
} else {
- ClientTemplate.update({
+ ClientScope.update({
realm : realm.realm,
- template : template.id
- }, $scope.template, function() {
+ clientScope : clientScope.id
+ }, $scope.clientScope, function() {
$scope.changed = false;
- template = angular.copy($scope.template);
- $location.url("/realms/" + realm.realm + "/client-templates/" + template.id);
- Notifications.success("Your changes have been saved to the client template.");
+ clientScope = angular.copy($scope.clientScope);
+ $location.url("/realms/" + realm.realm + "/client-scopes/" + clientScope.id);
+ Notifications.success("Your changes have been saved to the client scope.");
});
}
};
@@ -2133,20 +2625,20 @@ module.controller('ClientTemplateDetailCtrl', function($scope, realm, template,
};
$scope.cancel = function() {
- $location.url("/realms/" + realm.realm + "/client-templates");
+ $location.url("/realms/" + realm.realm + "/client-scopes");
};
});
-module.controller('ClientTemplateProtocolMapperListCtrl', function($scope, realm, template, serverInfo,
- ClientTemplateProtocolMappersByProtocol, ClientTemplateProtocolMapper,
+module.controller('ClientScopeProtocolMapperListCtrl', function($scope, realm, clientScope, serverInfo,
+ ClientScopeProtocolMappersByProtocol, ClientScopeProtocolMapper,
$route, Dialog, Notifications) {
$scope.realm = realm;
- $scope.template = template;
- if (template.protocol == null) {
- template.protocol = 'openid-connect';
+ $scope.clientScope = clientScope;
+ if (clientScope.protocol == null) {
+ clientScope.protocol = 'openid-connect';
}
- var protocolMappers = serverInfo.protocolMapperTypes[template.protocol];
+ var protocolMappers = serverInfo.protocolMapperTypes[clientScope.protocol];
var mapperTypes = {};
for (var i = 0; i < protocolMappers.length; i++) {
mapperTypes[protocolMappers[i].id] = protocolMappers[i];
@@ -2156,7 +2648,7 @@ module.controller('ClientTemplateProtocolMapperListCtrl', function($scope, realm
$scope.removeMapper = function(mapper) {
console.debug(mapper);
Dialog.confirmDelete(mapper.name, 'mapper', function() {
- ClientTemplateProtocolMapper.remove({ realm: realm.realm, template: template.id, id : mapper.id }, function() {
+ ClientScopeProtocolMapper.remove({ realm: realm.realm, clientScope: clientScope.id, id : mapper.id }, function() {
Notifications.success("The mapper has been deleted.");
$route.reload();
});
@@ -2164,30 +2656,30 @@ module.controller('ClientTemplateProtocolMapperListCtrl', function($scope, realm
};
var updateMappers = function() {
- $scope.mappers = ClientTemplateProtocolMappersByProtocol.query({realm : realm.realm, template : template.id, protocol : template.protocol});
+ $scope.mappers = ClientScopeProtocolMappersByProtocol.query({realm : realm.realm, clientScope : clientScope.id, protocol : clientScope.protocol});
};
updateMappers();
});
-module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, serverInfo, template, mapper, clients, ClientTemplateProtocolMapper, Notifications, Dialog, $location, $route) {
+module.controller('ClientScopeProtocolMapperCtrl', function($scope, realm, serverInfo, clientScope, mapper, clients, ClientScopeProtocolMapper, Notifications, Dialog, $location, $route) {
$scope.realm = realm;
$scope.clients = clients;
- if (template.protocol == null) {
- template.protocol = 'openid-connect';
+ if (clientScope.protocol == null) {
+ clientScope.protocol = 'openid-connect';
}
$scope.model = {
realm: realm,
- template: template,
+ clientScope: clientScope,
create: false,
- protocol: template.protocol,
+ protocol: clientScope.protocol,
mapper: angular.copy(mapper),
changed: false
}
- var protocolMappers = serverInfo.protocolMapperTypes[template.protocol];
+ var protocolMappers = serverInfo.protocolMapperTypes[clientScope.protocol];
for (var i = 0; i < protocolMappers.length; i++) {
if (protocolMappers[i].id == mapper.protocolMapper) {
$scope.model.mapperType = protocolMappers[i];
@@ -2206,9 +2698,9 @@ module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, se
}, true);
$scope.save = function() {
- ClientTemplateProtocolMapper.update({
+ ClientScopeProtocolMapper.update({
realm : realm.realm,
- template: template.id,
+ clientScope: clientScope.id,
id : mapper.id
}, $scope.model.mapper, function() {
$route.reload();
@@ -2228,29 +2720,29 @@ module.controller('ClientTemplateProtocolMapperCtrl', function($scope, realm, se
$scope.remove = function() {
Dialog.confirmDelete($scope.model.mapper.name, 'mapper', function() {
- ClientTemplateProtocolMapper.remove({ realm: realm.realm, template: template.id, id : $scope.model.mapper.id }, function() {
+ ClientScopeProtocolMapper.remove({ realm: realm.realm, clientScope: clientScope.id, id : $scope.model.mapper.id }, function() {
Notifications.success("The mapper has been deleted.");
- $location.url("/realms/" + realm.realm + '/client-templates/' + template.id + "/mappers");
+ $location.url("/realms/" + realm.realm + '/client-scopes/' + clientScope.id + "/mappers");
});
});
};
});
-module.controller('ClientTemplateProtocolMapperCreateCtrl', function($scope, realm, serverInfo, template, clients, ClientTemplateProtocolMapper, Notifications, Dialog, $location) {
+module.controller('ClientScopeProtocolMapperCreateCtrl', function($scope, realm, serverInfo, clientScope, clients, ClientScopeProtocolMapper, Notifications, Dialog, $location) {
$scope.realm = realm;
$scope.clients = clients;
- if (template.protocol == null) {
- template.protocol = 'openid-connect';
+ if (clientScope.protocol == null) {
+ clientScope.protocol = 'openid-connect';
}
- var protocol = template.protocol;
+ var protocol = clientScope.protocol;
$scope.model = {
realm: realm,
- template: template,
+ clientScope: clientScope,
create: true,
- protocol: template.protocol,
- mapper: { protocol : template.protocol, config: {}},
+ protocol: clientScope.protocol,
+ mapper: { protocol : clientScope.protocol, config: {}},
changed: false,
mapperTypes: serverInfo.protocolMapperTypes[protocol]
}
@@ -2265,12 +2757,12 @@ module.controller('ClientTemplateProtocolMapperCreateCtrl', function($scope, rea
$scope.save = function() {
$scope.model.mapper.protocolMapper = $scope.model.mapperType.id;
- ClientTemplateProtocolMapper.save({
- realm : realm.realm, template: template.id
+ ClientScopeProtocolMapper.save({
+ realm : realm.realm, clientScope: clientScope.id
}, $scope.model.mapper, function(data, headers) {
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
- $location.url("/realms/" + realm.realm + '/client-templates/' + template.id + "/mappers/" + id);
+ $location.url("/realms/" + realm.realm + '/client-scopes/' + clientScope.id + "/mappers/" + id);
Notifications.success("Mapper has been created.");
});
};
@@ -2283,16 +2775,16 @@ module.controller('ClientTemplateProtocolMapperCreateCtrl', function($scope, rea
});
-module.controller('ClientTemplateAddBuiltinProtocolMapperCtrl', function($scope, realm, template, serverInfo,
- ClientTemplateProtocolMappersByProtocol,
+module.controller('ClientScopeAddBuiltinProtocolMapperCtrl', function($scope, realm, clientScope, serverInfo,
+ ClientScopeProtocolMappersByProtocol,
$http, $location, Dialog, Notifications) {
$scope.realm = realm;
- $scope.template = template;
- if (template.protocol == null) {
- template.protocol = 'openid-connect';
+ $scope.clientScope = clientScope;
+ if (clientScope.protocol == null) {
+ clientScope.protocol = 'openid-connect';
}
- var protocolMappers = serverInfo.protocolMapperTypes[template.protocol];
+ var protocolMappers = serverInfo.protocolMapperTypes[clientScope.protocol];
var mapperTypes = {};
for (var i = 0; i < protocolMappers.length; i++) {
mapperTypes[protocolMappers[i].id] = protocolMappers[i];
@@ -2303,8 +2795,8 @@ module.controller('ClientTemplateAddBuiltinProtocolMapperCtrl', function($scope,
var updateMappers = function() {
- var clientMappers = ClientTemplateProtocolMappersByProtocol.query({realm : realm.realm, template : template.id, protocol : template.protocol}, function() {
- var builtinMappers = serverInfo.builtinProtocolMappers[template.protocol];
+ var clientMappers = ClientScopeProtocolMappersByProtocol.query({realm : realm.realm, clientScope : clientScope.id, protocol : clientScope.protocol}, function() {
+ var builtinMappers = serverInfo.builtinProtocolMappers[clientScope.protocol];
for (var i = 0; i < clientMappers.length; i++) {
for (var j = 0; j < builtinMappers.length; j++) {
if (builtinMappers[j].name == clientMappers[i].name
@@ -2333,26 +2825,26 @@ module.controller('ClientTemplateAddBuiltinProtocolMapperCtrl', function($scope,
toAdd.push($scope.mappers[i]);
}
}
- $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id + '/protocol-mappers/add-models',
+ $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/protocol-mappers/add-models',
toAdd).then(function() {
Notifications.success("Mappers added");
- $location.url('/realms/' + realm.realm + '/client-templates/' + template.id + '/mappers');
+ $location.url('/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/mappers');
}).catch(function() {
Notifications.error("Error adding mappers");
- $location.url('/realms/' + realm.realm + '/client-templates/' + template.id + '/mappers');
+ $location.url('/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/mappers');
});
};
});
-module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, realm, template, clients, Notifications,
- ClientTemplate,
- ClientTemplateRealmScopeMapping, ClientTemplateClientScopeMapping, ClientRole,
- ClientTemplateAvailableRealmScopeMapping, ClientTemplateAvailableClientScopeMapping,
- ClientTemplateCompositeRealmScopeMapping, ClientTemplateCompositeClientScopeMapping) {
+module.controller('ClientScopeScopeMappingCtrl', function($scope, $http, realm, clientScope, clients, Notifications,
+ ClientScope,
+ ClientScopeRealmScopeMapping, ClientScopeClientScopeMapping, ClientRole,
+ ClientScopeAvailableRealmScopeMapping, ClientScopeAvailableClientScopeMapping,
+ ClientScopeCompositeRealmScopeMapping, ClientScopeCompositeClientScopeMapping) {
$scope.realm = realm;
- $scope.template = angular.copy(template);
+ $scope.clientScope = angular.copy(clientScope);
$scope.selectedRealmRoles = [];
$scope.selectedRealmMappings = [];
$scope.realmMappings = [];
@@ -2364,32 +2856,17 @@ module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, real
$scope.clientMappings = [];
$scope.dummymodel = [];
-
- $scope.changeFullScopeAllowed = function() {
- ClientTemplate.update({
- realm : realm.realm,
- template : template.id
- }, $scope.template, function() {
- $scope.changed = false;
- template = angular.copy($scope.template);
- updateTemplateRealmRoles();
- Notifications.success("Scope mappings updated.");
- });
- }
-
-
-
- function updateTemplateRealmRoles() {
- $scope.realmRoles = ClientTemplateAvailableRealmScopeMapping.query({realm : realm.realm, template : template.id});
- $scope.realmMappings = ClientTemplateRealmScopeMapping.query({realm : realm.realm, template : template.id});
- $scope.realmComposite = ClientTemplateCompositeRealmScopeMapping.query({realm : realm.realm, template : template.id});
+ function updateScopeRealmRoles() {
+ $scope.realmRoles = ClientScopeAvailableRealmScopeMapping.query({realm : realm.realm, clientScope : clientScope.id});
+ $scope.realmMappings = ClientScopeRealmScopeMapping.query({realm : realm.realm, clientScope : clientScope.id});
+ $scope.realmComposite = ClientScopeCompositeRealmScopeMapping.query({realm : realm.realm, clientScope : clientScope.id});
}
- function updateTemplateClientRoles() {
+ function updateScopeClientRoles() {
if ($scope.targetClient) {
- $scope.clientRoles = ClientTemplateAvailableClientScopeMapping.query({realm : realm.realm, template : template.id, targetClient : $scope.targetClient.id});
- $scope.clientMappings = ClientTemplateClientScopeMapping.query({realm : realm.realm, template : template.id, targetClient : $scope.targetClient.id});
- $scope.clientComposite = ClientTemplateCompositeClientScopeMapping.query({realm : realm.realm, template : template.id, targetClient : $scope.targetClient.id});
+ $scope.clientRoles = ClientScopeAvailableClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.targetClient.id});
+ $scope.clientMappings = ClientScopeClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.targetClient.id});
+ $scope.clientComposite = ClientScopeCompositeClientScopeMapping.query({realm : realm.realm, clientScope : clientScope.id, targetClient : $scope.targetClient.id});
} else {
$scope.clientRoles = null;
$scope.clientMappings = null;
@@ -2398,15 +2875,15 @@ module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, real
}
$scope.changeClient = function() {
- updateTemplateClientRoles();
+ updateScopeClientRoles();
};
$scope.addRealmRole = function() {
var roles = $scope.selectedRealmRoles;
$scope.selectedRealmRoles = [];
- $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id + '/scope-mappings/realm',
+ $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/realm',
roles).then(function() {
- updateTemplateRealmRoles();
+ updateScopeRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
@@ -2414,9 +2891,9 @@ module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, real
$scope.deleteRealmRole = function() {
var roles = $scope.selectedRealmMappings;
$scope.selectedRealmMappings = [];
- $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id + '/scope-mappings/realm',
+ $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/realm',
{data : roles, headers : {"content-type" : "application/json"}}).then(function () {
- updateTemplateRealmRoles();
+ updateScopeRealmRoles();
Notifications.success("Scope mappings updated.");
});
};
@@ -2424,9 +2901,9 @@ module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, real
$scope.addClientRole = function() {
var roles = $scope.selectedClientRoles;
$scope.selectedClientRoles = [];
- $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id + '/scope-mappings/clients/' + $scope.targetClient.id,
+ $http.post(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/clients/' + $scope.targetClient.id,
roles).then(function () {
- updateTemplateClientRoles();
+ updateScopeClientRoles();
Notifications.success("Scope mappings updated.");
});
};
@@ -2434,14 +2911,14 @@ module.controller('ClientTemplateScopeMappingCtrl', function($scope, $http, real
$scope.deleteClientRole = function() {
var roles = $scope.selectedClientMappings;
$scope.selectedClientMappings = [];
- $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-templates/' + template.id + '/scope-mappings/clients/' + $scope.targetClient.id,
+ $http.delete(authUrl + '/admin/realms/' + realm.realm + '/client-scopes/' + clientScope.id + '/scope-mappings/clients/' + $scope.targetClient.id,
{data : roles, headers : {"content-type" : "application/json"}}).then(function () {
- updateTemplateClientRoles();
+ updateScopeClientRoles();
Notifications.success("Scope mappings updated.");
});
};
- updateTemplateRealmRoles();
+ updateScopeRealmRoles();
});
module.controller('ClientStoresCtrl', function($scope, $location, $route, realm, serverInfo, Components, Notifications, Dialog) {
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index 47bbc02..af26911 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -115,11 +115,11 @@ module.factory('ClientProtocolMapperLoader', function(Loader, ClientProtocolMapp
});
});
-module.factory('ClientTemplateProtocolMapperLoader', function(Loader, ClientTemplateProtocolMapper, $route, $q) {
- return Loader.get(ClientTemplateProtocolMapper, function() {
+module.factory('ClientScopeProtocolMapperLoader', function(Loader, ClientScopeProtocolMapper, $route, $q) {
+ return Loader.get(ClientScopeProtocolMapper, function() {
return {
realm : $route.current.params.realm,
- template : $route.current.params.template,
+ clientScope : $route.current.params.clientScope,
id: $route.current.params.id
}
});
@@ -286,8 +286,17 @@ module.factory('ClientOfflineSessionCountLoader', function(Loader, ClientOffline
});
});
-module.factory('ClientClaimsLoader', function(Loader, ClientClaims, $route, $q) {
- return Loader.get(ClientClaims, function() {
+module.factory('ClientDefaultClientScopesLoader', function(Loader, ClientDefaultClientScopes, $route, $q) {
+ return Loader.query(ClientDefaultClientScopes, function() {
+ return {
+ realm : $route.current.params.realm,
+ client : $route.current.params.client
+ }
+ });
+});
+
+module.factory('ClientOptionalClientScopesLoader', function(Loader, ClientOptionalClientScopes, $route, $q) {
+ return Loader.query(ClientOptionalClientScopes, function() {
return {
realm : $route.current.params.realm,
client : $route.current.params.client
@@ -323,17 +332,33 @@ module.factory('ClientListLoader', function(Loader, Client, $route, $q) {
});
});
-module.factory('ClientTemplateLoader', function(Loader, ClientTemplate, $route, $q) {
- return Loader.get(ClientTemplate, function() {
+module.factory('ClientScopeLoader', function(Loader, ClientScope, $route, $q) {
+ return Loader.get(ClientScope, function() {
return {
realm : $route.current.params.realm,
- template : $route.current.params.template
+ clientScope : $route.current.params.clientScope
+ }
+ });
+});
+
+module.factory('ClientScopeListLoader', function(Loader, ClientScope, $route, $q) {
+ return Loader.query(ClientScope, function() {
+ return {
+ realm : $route.current.params.realm
+ }
+ });
+});
+
+module.factory('RealmDefaultClientScopesLoader', function(Loader, RealmDefaultClientScopes, $route, $q) {
+ return Loader.query(RealmDefaultClientScopes, function() {
+ return {
+ realm : $route.current.params.realm
}
});
});
-module.factory('ClientTemplateListLoader', function(Loader, ClientTemplate, $route, $q) {
- return Loader.query(ClientTemplate, function() {
+module.factory('RealmOptionalClientScopesLoader', function(Loader, RealmOptionalClientScopes, $route, $q) {
+ return Loader.query(RealmOptionalClientScopes, function() {
return {
realm : $route.current.params.realm
}
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js
index 5700792..c80cc40 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -425,10 +425,10 @@ module.factory('ClientProtocolMapper', function($resource) {
});
});
-module.factory('ClientTemplateProtocolMapper', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/protocol-mappers/models/:id', {
+module.factory('ClientScopeProtocolMapper', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/protocol-mappers/models/:id', {
realm : '@realm',
- template: '@template',
+ clientScope: '@clientScope',
id : "@id"
}, {
update : {
@@ -945,17 +945,70 @@ module.factory('ClientRole', function($resource) {
});
});
-module.factory('ClientClaims', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/clients/:client/claims', {
+module.factory('ClientDefaultClientScopes', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/default-client-scopes/:clientScopeId', {
realm : '@realm',
- client : "@client"
- }, {
+ client : "@client",
+ clientScopeId : '@clientScopeId'
+ }, {
+ update : {
+ method : 'PUT'
+ }
+ });
+});
+
+module.factory('ClientOptionalClientScopes', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/optional-client-scopes/:clientScopeId', {
+ realm : '@realm',
+ client : "@client",
+ clientScopeId : '@clientScopeId'
+ }, {
update : {
method : 'PUT'
}
});
});
+module.factory('ClientEvaluateProtocolMappers', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/evaluate-scopes/protocol-mappers?scope=:scopeParam', {
+ realm : '@realm',
+ client : "@client",
+ scopeParam : "@scopeParam"
+ });
+});
+
+module.factory('ClientEvaluateGrantedRoles', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/evaluate-scopes/scope-mappings/:roleContainer/granted?scope=:scopeParam', {
+ realm : '@realm',
+ client : "@client",
+ roleContainer : "@roleContainer",
+ scopeParam : "@scopeParam"
+ });
+});
+
+module.factory('ClientEvaluateNotGrantedRoles', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/clients/:client/evaluate-scopes/scope-mappings/:roleContainer/not-granted?scope=:scopeParam', {
+ realm : '@realm',
+ client : "@client",
+ roleContainer : "@roleContainer",
+ scopeParam : "@scopeParam"
+ });
+});
+
+module.factory('ClientEvaluateGenerateExampleToken', function($resource) {
+ var url = authUrl + '/admin/realms/:realm/clients/:client/evaluate-scopes/generate-example-access-token?scope=:scopeParam&userId=:userId';
+ return {
+ url : function(parameters)
+ {
+ return url
+ .replace(':realm', parameters.realm)
+ .replace(':client', parameters.client)
+ .replace(':scopeParam', parameters.scopeParam)
+ .replace(':userId', parameters.userId);
+ }
+ }
+});
+
module.factory('ClientProtocolMappersByProtocol', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/clients/:client/protocol-mappers/protocol/:protocol', {
realm : '@realm',
@@ -964,55 +1017,55 @@ module.factory('ClientProtocolMappersByProtocol', function($resource) {
});
});
-module.factory('ClientTemplateProtocolMappersByProtocol', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/protocol-mappers/protocol/:protocol', {
+module.factory('ClientScopeProtocolMappersByProtocol', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/protocol-mappers/protocol/:protocol', {
realm : '@realm',
- template : "@template",
+ clientScope : "@clientScope",
protocol : "@protocol"
});
});
-module.factory('ClientTemplateRealmScopeMapping', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/realm', {
+module.factory('ClientScopeRealmScopeMapping', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/scope-mappings/realm', {
realm : '@realm',
- template : '@template'
+ clientScope : '@clientScope'
});
});
-module.factory('ClientTemplateAvailableRealmScopeMapping', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/realm/available', {
+module.factory('ClientScopeAvailableRealmScopeMapping', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/scope-mappings/realm/available', {
realm : '@realm',
- template : '@template'
+ clientScope : '@clientScope'
});
});
-module.factory('ClientTemplateCompositeRealmScopeMapping', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/realm/composite', {
+module.factory('ClientScopeCompositeRealmScopeMapping', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/scope-mappings/realm/composite', {
realm : '@realm',
- template : '@template'
+ clientScope : '@clientScope'
});
});
-module.factory('ClientTemplateClientScopeMapping', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/clients/:targetClient', {
+module.factory('ClientScopeClientScopeMapping', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/scope-mappings/clients/:targetClient', {
realm : '@realm',
- template : '@template',
+ clientScope : '@clientScope',
targetClient : '@targetClient'
});
});
-module.factory('ClientTemplateAvailableClientScopeMapping', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/clients/:targetClient/available', {
+module.factory('ClientScopeAvailableClientScopeMapping', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/scope-mappings/clients/:targetClient/available', {
realm : '@realm',
- template : '@template',
+ clientScope : '@clientScope',
targetClient : '@targetClient'
});
});
-module.factory('ClientTemplateCompositeClientScopeMapping', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template/scope-mappings/clients/:targetClient/composite', {
+module.factory('ClientScopeCompositeClientScopeMapping', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope/scope-mappings/clients/:targetClient/composite', {
realm : '@realm',
- template : '@template',
+ clientScope : '@clientScope',
targetClient : '@targetClient'
});
});
@@ -1133,10 +1186,10 @@ module.factory('Client', function($resource) {
});
});
-module.factory('ClientTemplate', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/client-templates/:template', {
+module.factory('ClientScope', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/client-scopes/:clientScope', {
realm : '@realm',
- template : '@template'
+ clientScope : '@clientScope'
}, {
update : {
method : 'PUT'
@@ -1144,6 +1197,28 @@ module.factory('ClientTemplate', function($resource) {
});
});
+module.factory('RealmDefaultClientScopes', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/default-default-client-scopes/:clientScopeId', {
+ realm : '@realm',
+ clientScopeId : '@clientScopeId'
+ }, {
+ update : {
+ method : 'PUT'
+ }
+ });
+});
+
+module.factory('RealmOptionalClientScopes', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/default-optional-client-scopes/:clientScopeId', {
+ realm : '@realm',
+ clientScopeId : '@clientScopeId'
+ }, {
+ update : {
+ method : 'PUT'
+ }
+ });
+});
+
module.factory('ClientDescriptionConverter', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/client-description-converter', {
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index d0fcac5..d9019ae 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -44,6 +44,7 @@
</div>
<kc-tooltip>{{:: 'client-origin.tooltip' | translate}}</kc-tooltip>
</div>
+
<div class="form-group clearfix block" data-ng-show="protocol != 'docker-v2'">
<label class="col-md-2 control-label" for="consentRequired">{{:: 'consent-required' | translate}}</label>
<div class="col-sm-6">
@@ -51,6 +52,21 @@
</div>
<kc-tooltip>{{:: 'consent-required.tooltip' | translate}}</kc-tooltip>
</div>
+ <div class="form-group clearfix block" data-ng-show="clientEdit.consentRequired && protocol != 'docker-v2'">
+ <label class="col-md-2 control-label" for="displayOnConsentScreen">{{:: 'client.display-on-consent-screen' | translate}}</label>
+ <div class="col-sm-6">
+ <input ng-model="displayOnConsentScreen" ng-click="switchChange()" name="displayOnConsentScreen" id="displayOnConsentScreen" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
+ </div>
+ <kc-tooltip>{{:: 'client.display-on-consent-screen.tooltip' | translate}}</kc-tooltip>
+ </div>
+ <div class="form-group" data-ng-show="clientEdit.consentRequired && protocol != 'docker-v2' && displayOnConsentScreen">
+ <label class="col-md-2 control-label" for="consentScreenText">{{:: 'client.consent-screen-text' | translate}} </label>
+ <div class="col-sm-6">
+ <input class="form-control" type="text" id="consentScreenText" name="consentScreenText" data-ng-model="clientEdit.attributes['consent.screen.text']">
+ </div>
+ <kc-tooltip>{{:: 'client.consent-screen-text.tooltip' | translate}}</kc-tooltip>
+ </div>
+
<div class="form-group">
<label class="col-md-2 control-label" for="loginTheme">{{:: 'login-theme' | translate}}</label>
<div class="col-sm-6">
@@ -75,18 +91,6 @@
</div>
<kc-tooltip>{{:: 'client-protocol.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group" kc-read-only="!client.access.manage">
- <label class="col-md-2 control-label" for="protocol">{{:: 'client-template' | translate}}</label>
- <div class="col-sm-6">
- <div>
- <select class="form-control" id="template"
- ng-model="clientEdit.clientTemplate"
- ng-options="template.name as template.name for template in templates">
- </select>
- </div>
- </div>
- <kc-tooltip>{{:: 'client-template.tooltip' | translate}}</kc-tooltip>
- </div>
<div class="form-group" data-ng-show="protocol == 'openid-connect'">
<label class="col-md-2 control-label" for="accessType">{{:: 'access-type' | translate}}</label>
<div class="col-sm-6">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
index 5648c19..f600b08 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
@@ -7,20 +7,6 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="allowScope" novalidate kc-read-only="!client.access.manage">
- <fieldset class="border-top">
- <div class="form-group" ng-show="client.clientTemplate">
- <label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Mappers</label>
- <kc-tooltip>Inherit mappers from client template</kc-tooltip>
- <div class="col-md-1">
- <input ng-model="client.useTemplateMappers" ng-click="changeFlag()" name="useTemplateScope" id="useTemplateScope" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
- </div>
- <div class="col-md-2">
- <a href="#/realms/{{realm.realm}}/client-templates/{{template.id}}/mappers">view template mappers</a>
- </div>
- </div>
- </fieldset>
- </form>
<table class="table table-striped table-bordered">
<thead>
<tr>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
index 67c96fc..f17872e 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
@@ -30,13 +30,6 @@
required> -->
</div>
</div>
- <div class="form-group">
- <label class="col-md-2 control-label" for="scopeParamRequired">{{:: 'scope-param-required' | translate}} </label>
- <kc-tooltip>{{:: 'scope-param-required.tooltip' | translate}}</kc-tooltip>
- <div class="col-md-6">
- <input ng-model="role.scopeParamRequired" name="scopeParamRequired" id="scopeParamRequired" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
- </div>
- </div>
<div class="form-group clearfix block" data-ng-hide="create">
<label class="col-md-2 control-label" for="compositeSwitch" class="control-label">{{:: 'composite-roles' | translate}}</label>
<div class="col-md-6">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
index 6cb01f4..4ba8f38 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
@@ -11,33 +11,13 @@
<p class="subtitle"></p>
<form class="form-horizontal" name="allowScope" novalidate kc-read-only="!client.access.manage">
<fieldset class="border-top">
- <div class="form-group" ng-show="client.clientTemplate">
- <label class="col-md-2 control-label" for="useTemplateScope">Inherit Template Scope</label>
- <kc-tooltip>Inherit scope from client template</kc-tooltip>
- <div class="col-md-1">
- <input ng-model="client.useTemplateScope" ng-click="changeFlag()" name="useTemplateScope" id="useTemplateScope" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
- </div>
- <div class="col-md-2">
- <a href="#/realms/{{realm.realm}}/client-templates/{{template.id}}/scope-mappings">view template scope</a>
- </div>
- </div>
- <div class="form-group" ng-hide="client.useTemplateScope && template && template.fullScopeAllowed">
+ <div class="form-group">
<label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
<kc-tooltip>{{:: 'full-scope-allowed.tooltip' | translate}}</kc-tooltip>
<div class="col-md-6">
<input kc-read-only="!client.access.manage" ng-model="client.fullScopeAllowed" ng-click="changeFlag()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
</div>
- <div class="form-group" ng-show="client.useTemplateScope && template && template.fullScopeAllowed">
- <label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
- <kc-tooltip>Client template has full scope allowed, which means this client will have the full scope of all roles.</kc-tooltip>
- <div class="col-md-1">
- <input kc-read-only="!client.access.manage" ng-model="template.fullScopeAllowed" name="fullScopeAllowed" id="fullScopeAllowed" ng-disabled="true" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
- </div>
- <div class="col-md-1">
- <i>inherited</i>
- </div>
- </div>
</fieldset>
</form>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html
new file mode 100644
index 0000000..34b8971
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-evaluate.html
@@ -0,0 +1,239 @@
+<!--
+ ~ Copyright 2017 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+ <li>{{client.clientId}}</li>
+ </ol>
+
+ <kc-tabs-client></kc-tabs-client>
+
+ <ul class="nav nav-tabs nav-tabs-pf">
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/client-scopes/setup-scopes">{{:: 'client-scopes.setup' | translate}}</a>
+ <kc-tooltip>{{:: 'client-scopes.setup.tooltip' | translate}}</kc-tooltip>
+ </li>
+ <li class="active">
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/client-scopes/evaluate-scopes">{{:: 'client-scopes.evaluate' | translate}}</a>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.tooltip' | translate}}</kc-tooltip>
+ </li>
+ </ul>
+
+ <form class="form-horizontal" name="evaluateForm" novalidate kc-read-only="!access.viewClients">
+ <fieldset>
+ <div class="form-group clearfix" data-ng-show="client.protocol == 'openid-connect'">
+ <label class="col-md-2 control-label" for="scopeParam">{{:: 'scope-parameter' | translate}}</label>
+ <div class="col-md-6">
+ <input class="form-control" id="scopeParam" type="text" value="{{scopeParam}}" readonly kc-select-action="click">
+ </div>
+ <kc-tooltip>{{:: 'scope-parameter.tooltip' | translate}}</kc-tooltip>
+ </div>
+ </fieldset>
+
+ <div class="form-group">
+ <label class="col-md-2 control-label" class="control-label">{{:: 'client-scopes.evaluate.scopes' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.scopes.tooltip' | translate}}</kc-tooltip>
+
+ <div class="col-md-10">
+ <div class="row">
+ <div class="col-md-4">
+ <label class="control-label" for="available">{{:: 'client-scopes.evaluate.scopes.available' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.scopes.available.tooltip' | translate}}</kc-tooltip>
+ <select id="available" class="form-control" multiple size="5"
+ ng-multiple="true"
+ ng-model="selectedClientScopes"
+ ng-options="r.name for r in availableClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="addAppliedClientScope()">
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
+ </button>
+ </div>
+ <div class="col-md-4">
+ <label class="control-label" for="assigned">{{:: 'client-scopes.evaluate.scopes.assigned' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.scopes.assigned.tooltip' | translate}}</kc-tooltip>
+ <select id="assigned" class="form-control" multiple size=5
+ ng-multiple="true"
+ ng-model="selectedDefClientScopes"
+ ng-options="r.name for r in assignedClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedDefClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="deleteAppliedClientScope()">
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
+ </button>
+ </div>
+ <div class="col-md-4">
+ <label class="control-label" for="assigned">{{:: 'client-scopes.evaluate.scopes.effective' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.scopes.effective.tooltip' | translate}}</kc-tooltip>
+ <select id="effective" class="form-control" multiple size=5
+ disabled="true"
+ ng-model="dummymodel"
+ ng-options="r.name for r in effectiveClientScopes | orderBy:'toString()'">
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-group clearfix" data-ng-show="access.viewUsers">
+ <label class="col-md-2 control-label" for="users">{{:: 'user' | translate}}</label>
+
+ <div class="col-md-6">
+ <input type="hidden" ui-select2="usersUiSelect" id="users" data-ng-model="selectedUser" data-ng-change="selectUser(selectedUser);" data-placeholder="{{:: 'authz-select-user' | translate}}...">
+ </input>
+ </div>
+
+ <kc-tooltip>{{:: 'client-scopes.evaluate.user.tooltip' | translate}}</kc-tooltip>
+ </div>
+
+ <div class="form-group clearfix">
+ <div class="col-md-10 col-md-offset-1" data-ng-show="client.access.view">
+ <button type="submit" data-ng-click="sendEvaluationRequest()" class="btn btn-primary" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'send-evaluation-request.tooltip' | translate}}" tooltip-placement="bottom">{{:: 'send-evaluation-request' | translate}}</button>
+ </div>
+ </div>
+ </form>
+
+
+ <ul class="nav nav-tabs nav-tabs-pf" data-ng-show="isResponseAvailable()">
+ <li class="{{tabCss.tab1}}" data-ng-click="showTab(1)">
+ <a href="">{{:: 'evaluated-protocol-mappers' | translate}}</a>
+ <kc-tooltip>{{:: 'evaluated-protocol-mappers.tooltip' | translate}}</kc-tooltip>
+ </li>
+ <li class="{{tabCss.tab2}}" data-ng-click="showTab(2)">
+ <a href="">{{:: 'evaluated-roles' | translate}}</a>
+ <kc-tooltip>{{:: 'evaluated-roles.tooltip' | translate}}</kc-tooltip>
+ </li>
+ <li class="{{tabCss.tab3}}" data-ng-click="showTab(3)" data-ng-show="isTokenAvailable()">
+ <a href="">{{:: 'generated-access-token' | translate}}</a>
+ <kc-tooltip>{{:: 'generated-access-token.tooltip' | translate}}</kc-tooltip>
+ </li>
+ </ul>
+
+ <!-- Effective protocol mappers -->
+ <table class="table table-striped table-bordered" data-ng-show="protocolMappersShown()">
+ <thead>
+ <tr>
+ <th class="kc-table-actions" colspan="4">
+ <div class="form-inline">
+ <div>
+ <div class="input-group">
+ <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.mapperName" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+ <div class="input-group-addon">
+ <i class="fa fa-search" type="submit"></i>
+ </div>
+ </div>
+ </div>
+ </div>
+ </th>
+ </tr>
+ <tr data-ng-hide="protocolMappers.length == 0">
+ <th>{{:: 'name' | translate}}</th>
+ <th>{{:: 'parent-client-scope' | translate}}</th>
+ <th>{{:: 'category' | translate}}</th>
+ <th>{{:: 'type' | translate}}</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr ng-repeat="mapper in protocolMappers | filter:search">
+ <td><a href="#/realms/{{realm.realm}}/{{mapper.containerType}}s/{{mapper.containerId}}/mappers/{{mapper.mapperId}}">{{mapper.mapperName}}</a></td>
+ <td><a href="#/realms/{{realm.realm}}/{{mapper.containerType}}s/{{mapper.containerId}}">{{mapper.containerName}}</a></td>
+ <td>{{mapperTypes[mapper.protocolMapper].category}}</td>
+ <td>{{mapperTypes[mapper.protocolMapper].name}}</td>
+ </tr>
+ <tr data-ng-show="mappers.length == 0">
+ <td>{{:: 'no-mappers-available' | translate}}</td>
+ </tr>
+ </tbody>
+ </table>
+
+
+ <!-- Effective role scope mappings -->
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.viewClients">
+ <div class="form-group" data-ng-show="rolesShown()">
+ <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
+ <div class="col-md-10">
+ <div class="row">
+ <div class="col-md-3">
+ <label class="control-label" for="available-realm-roles">{{:: 'client-scopes.evaluate.not-granted-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.not-granted-roles.tooltip' | translate}}</kc-tooltip>
+
+ <select id="available-realm-roles" class="form-control" multiple size="5"
+ disabled="true"
+ ng-model="dummymodel1"
+ ng-options="r.name for r in notGrantedRealmRoles | orderBy:'name'">
+ </select>
+ </div>
+ <div class="col-md-3">
+ <label class="control-label" for="realm-composite">{{:: 'client-scopes.evaluate.granted-realm-effective-roles' | translate}} </label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.granted-realm-effective-roles.tooltip' | translate}}</kc-tooltip>
+ <select id="realm-composite" class="form-control" multiple size=5
+ disabled="true"
+ ng-model="dummymodel2"
+ ng-options="r.name for r in grantedRealmRoles | orderBy:'name'">
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-group" data-ng-show="rolesShown()">
+ <label class="col-md-2 control-label" class="control-label">
+ <span>{{:: 'client-roles' | translate}}</span>
+ <select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients | orderBy:'clientId'" ng-disabled="false"></select>
+ </label>
+
+ <div class="col-md-10">
+ <div class="row" data-ng-hide="targetClient">
+ <div class="col-md-4"><span class="text-muted">{{:: 'select-client-roles.tooltip' | translate}}</span></div>
+ </div>
+ <div class="row" data-ng-show="targetClient">
+ <div class="col-md-3">
+ <label class="control-label" for="available-client">{{:: 'client-scopes.evaluate.not-granted-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.not-granted-roles.tooltip' | translate}}</kc-tooltip>
+ <select id="available-client" class="form-control" multiple size="5"
+ disabled="true"
+ ng-model="dummymodel"
+ ng-options="r.name for r in notGrantedClientRoles | orderBy:'name'">
+ </select>
+ </div>
+ <div class="col-md-3">
+ <label class="control-label" for="client-composite">{{:: 'client-scopes.evaluate.granted-client-effective-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.granted-realm-effective-roles.tooltip' | translate}}</kc-tooltip>
+ <select id="client-composite" class="form-control" multiple size=5
+ disabled="true"
+ ng-model="dummymodel"
+ ng-options="r.name for r in grantedClientRoles | orderBy:'name'">
+ </select>
+ </div>
+ </div>
+ </div>
+ </div>
+ </form>
+
+
+ <!-- Access token -->
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.viewClients">
+ <div class="form-group">
+ <div class="col-md-10 col-md-offset-1" data-ng-show="tokenShown()">
+ <textarea class="form-control" rows="20" kc-select-action="click" readonly>{{oidcAccessToken}}</textarea>
+ </div>
+ </div>
+ </form>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-realm-default.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-realm-default.html
new file mode 100644
index 0000000..f03f704
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-realm-default.html
@@ -0,0 +1,91 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+ <h1>
+ <span>{{:: 'default-client-scopes' | translate}}</span>
+ <kc-tooltip>{{:: 'default-client-scopes.tooltip' | translate}}</kc-tooltip>
+ </h1>
+
+ <ul class="nav nav-tabs">
+ <li>
+ <a href="#/realms/{{realm.realm}}/client-scopes">{{:: 'client-scopes' | translate}}</a>
+ <kc-tooltip>{{:: 'client-scopes.tooltip' | translate}}</kc-tooltip>
+ </li>
+ <li class="active">
+ <a href="#/realms/{{realm.realm}}/default-client-scopes">{{:: 'default-client-scopes' | translate}}</a>
+ <kc-tooltip>{{:: 'default-client-scopes.tooltip' | translate}}</kc-tooltip>
+ </li>
+ </ul>
+
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">
+ <div class="form-group">
+ <label class="col-md-2 control-label" class="control-label">{{:: 'default-client-scopes.default' | translate}}</label>
+ <kc-tooltip>{{:: 'default-client-scopes.default.tooltip' | translate}}</kc-tooltip>
+
+ <div class="col-md-10">
+ <div class="row">
+ <div class="col-md-4">
+ <label class="control-label" for="available">{{:: 'default-client-scopes.default.available' | translate}}</label>
+ <kc-tooltip>{{:: 'default-client-scopes.default.available.tooltip' | translate}}</kc-tooltip>
+ <select id="available" class="form-control" multiple size="5"
+ ng-multiple="true"
+ ng-model="selectedDefaultClientScopes"
+ ng-options="r.name for r in availableClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedDefaultClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="addDefaultClientScope()">
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
+ </button>
+ </div>
+ <div class="col-md-4">
+ <label class="control-label" for="assigned">{{:: 'default-client-scopes.default.assigned' | translate}}</label>
+ <kc-tooltip>{{:: 'default-client-scopes.default.assigned.tooltip' | translate}}</kc-tooltip>
+ <select id="assigned" class="form-control" multiple size=5
+ ng-multiple="true"
+ ng-model="selectedDefDefaultClientScopes"
+ ng-options="r.name for r in realmDefaultClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedDefDefaultClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="deleteDefaultClientScope()">
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-group">
+ <label class="col-md-2 control-label" class="control-label">{{:: 'default-client-scopes.optional' | translate}}</label>
+ <kc-tooltip>{{:: 'default-client-scopes.optional.tooltip' | translate}}</kc-tooltip>
+
+ <div class="col-md-10">
+ <div class="row">
+ <div class="col-md-4">
+ <label class="control-label" for="available-opt">{{:: 'default-client-scopes.optional.available' | translate}}</label>
+ <kc-tooltip>{{:: 'default-client-scopes.optional.available.tooltip' | translate}}</kc-tooltip>
+ <select id="available-opt" class="form-control" multiple size="5"
+ ng-multiple="true"
+ ng-model="selectedOptionalClientScopes"
+ ng-options="r.name for r in availableClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedOptionalClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="addOptionalClientScope()">
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
+ </button>
+ </div>
+ <div class="col-md-4">
+ <label class="control-label" for="assigned-opt">{{:: 'default-client-scopes.optional.assigned' | translate}}</label>
+ <kc-tooltip>{{:: 'default-client-scopes.optional.assigned.tooltip' | translate}}</kc-tooltip>
+ <select id="assigned-opt" class="form-control" multiple size=5
+ ng-multiple="true"
+ ng-model="selectedDefOptionalClientScopes"
+ ng-options="r.name for r in realmOptionalClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedDefOptionalClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="deleteOptionalClientScope()">
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </form>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-setup.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-setup.html
new file mode 100644
index 0000000..e5d4168
--- /dev/null
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-scopes-setup.html
@@ -0,0 +1,111 @@
+<!--
+ ~ Copyright 2017 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+ <li>{{client.clientId}}</li>
+ </ol>
+
+ <kc-tabs-client></kc-tabs-client>
+
+ <ul class="nav nav-tabs nav-tabs-pf">
+ <li class="active">
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/client-scopes/setup-scopes">{{:: 'client-scopes.setup' | translate}}</a>
+ <kc-tooltip>{{:: 'client-scopes.setup.tooltip' | translate}}</kc-tooltip>
+ </li>
+ <li>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/client-scopes/evaluate-scopes">{{:: 'client-scopes.evaluate' | translate}}</a>
+ <kc-tooltip>{{:: 'client-scopes.evaluate.tooltip' | translate}}</kc-tooltip>
+ </li>
+ </ul>
+
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">
+ <div class="form-group">
+ <label class="col-md-2 control-label" class="control-label">{{:: 'client-scopes.default' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.default.tooltip' | translate}}</kc-tooltip>
+
+ <div class="col-md-10">
+ <div class="row">
+ <div class="col-md-4">
+ <label class="control-label" for="available">{{:: 'client-scopes.default.available' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.default.available.tooltip' | translate}}</kc-tooltip>
+ <select id="available" class="form-control" multiple size="5"
+ ng-multiple="true"
+ ng-model="selectedDefaultClientScopes"
+ ng-options="r.name for r in availableClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedDefaultClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="addDefaultClientScope()">
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
+ </button>
+ </div>
+ <div class="col-md-4">
+ <label class="control-label" for="assigned">{{:: 'client-scopes.default.assigned' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.default.assigned.tooltip' | translate}}</kc-tooltip>
+ <select id="assigned" class="form-control" multiple size=5
+ ng-multiple="true"
+ ng-model="selectedDefDefaultClientScopes"
+ ng-options="r.name for r in clientDefaultClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedDefDefaultClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="deleteDefaultClientScope()">
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="form-group" data-ng-show="client.protocol == 'openid-connect'">
+ <label class="col-md-2 control-label" class="control-label">{{:: 'client-scopes.optional' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.optional.tooltip' | translate}}</kc-tooltip>
+
+ <div class="col-md-10">
+ <div class="row">
+ <div class="col-md-4">
+ <label class="control-label" for="available-opt">{{:: 'client-scopes.optional.available' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.optional.available.tooltip' | translate}}</kc-tooltip>
+ <select id="available-opt" class="form-control" multiple size="5"
+ ng-multiple="true"
+ ng-model="selectedOptionalClientScopes"
+ ng-options="r.name for r in availableClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedOptionalClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="addOptionalClientScope()">
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
+ </button>
+ </div>
+ <div class="col-md-4">
+ <label class="control-label" for="assigned-opt">{{:: 'client-scopes.optional.assigned' | translate}}</label>
+ <kc-tooltip>{{:: 'client-scopes.optional.assigned.tooltip' | translate}}</kc-tooltip>
+ <select id="assigned-opt" class="form-control" multiple size=5
+ ng-multiple="true"
+ ng-model="selectedDefOptionalClientScopes"
+ ng-options="r.name for r in clientOptionalClientScopes | orderBy:'toString()'">
+ </select>
+ <button ng-disabled="selectedDefOptionalClientScopes.length == 0" class="btn btn-default" type="submit" ng-click="deleteOptionalClientScope()">
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
+ </button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </form>
+
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/create-client.html b/themes/src/main/resources/theme/base/admin/resources/partials/create-client.html
index 98609c8..0c47b31 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/create-client.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/create-client.html
@@ -44,19 +44,6 @@
</div>
<kc-tooltip>{{:: 'client-protocol.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group">
- <label class="col-md-2 control-label" for="protocol">{{:: 'client-template' | translate}}</label>
- <div class="col-sm-6">
- <div>
- <select class="form-control" id="template"
- ng-change="changeTemplate()"
- ng-model="client.clientTemplate"
- ng-options="template.name as template.name for template in templates">
- </select>
- </div>
- </div>
- <kc-tooltip>{{:: 'client-template.tooltip' | translate}}</kc-tooltip>
- </div>
<div class="form-group" data-ng-hide="protocol == 'saml'">
<label class="col-md-2 control-label" for="rootUrl">{{:: 'root-url' | translate}}</label>
<div class="col-sm-6">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
index e630bf9..e555aaf 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
@@ -25,21 +25,6 @@
</div>
<kc-tooltip>{{:: 'mapper.name.tooltip' | translate}}</kc-tooltip>
</div>
- <div class="form-group">
- <label for="consentRequired" class="col-md-2 control-label">{{:: 'consent-required' | translate}}</label>
- <div class="col-md-6">
- <input ng-model="model.mapper.consentRequired" name="consentRequired" id="consentRequired" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
- </div>
- <kc-tooltip>{{:: 'mapper.consent-required.tooltip' | translate}}</kc-tooltip>
- </div>
- <div class="form-group" data-ng-show="model.mapper.consentRequired">
- <label class="col-md-2 control-label" for="consentText">{{:: 'consent-text' | translate}} </label>
-
- <div class="col-md-6">
- <textarea class="form-control" rows="5" cols="50" id="consentText" name="consentText" data-ng-model="model.mapper.consentText"></textarea>
- </div>
- <kc-tooltip>{{:: 'consent-text.tooltip' | translate}}</kc-tooltip>
- </div>
<div class="form-group" data-ng-show="model.create">
<label class="col-md-2 control-label" for="mapperTypeCreate">{{:: 'mapper-type' | translate}}</label>
<div class="col-md-6">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
index 3bcaa66..6cf75fb 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
@@ -25,13 +25,6 @@
<textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="role.description"></textarea>
</div>
</div>
- <div class="form-group">
- <label class="col-md-2 control-label" for="scopeParamRequired">{{:: 'scope-param-required' | translate}} </label>
- <kc-tooltip>{{:: 'scope-param-required.tooltip' | translate}}</kc-tooltip>
- <div class="col-md-6">
- <input ng-model="role.scopeParamRequired" name="scopeParamRequired" id="scopeParamRequired" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
- </div>
- </div>
<div class="form-group" data-ng-hide="create">
<label class="col-md-2 control-label" for="compositeSwitch" class="control-label">{{:: 'composite-roles' | translate}}</label>
<div class="col-md-6">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
index 670d708..76eb0a8 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
@@ -10,8 +10,7 @@
<thead>
<tr>
<th>{{:: 'client' | translate}}</th>
- <th>{{:: 'granted-roles' | translate}}</th>
- <th>{{:: 'granted-protocol-mappers' | translate}}</th>
+ <th>{{:: 'granted-client-scopes' | translate}}</th>
<th>{{:: 'additional-grants' | translate}}</th>
<th>{{:: 'consent-created-date' | translate}}</th>
<th>{{:: 'consent-last-updated-date' | translate}}</th>
@@ -22,20 +21,8 @@
<tr data-ng-repeat="consent in userConsents">
<td>{{consent.clientId}}</td>
<td>
- <span data-ng-repeat="realmRole in consent.grantedRealmRoles">
- <span ng-if="!$first">, </span>{{realmRole}}
- </span>
- <span data-ng-repeat="(clientId, clientRoles) in consent.grantedClientRoles">
- <span data-ng-repeat="clientRole in clientRoles">
- <span ng-if="!$first || consent.grantedRealmRoles.length > 0">, </span>{{clientRole}} in {{clientId}}
- </span>
- </span>
- </td>
- <td>
- <span data-ng-repeat="protocol in consent.grantedProtocolMappers">
- <span data-ng-repeat="protocolMapper in protocol">
- <span ng-if="!$first">, </span>{{protocolMapper}}
- </span>
+ <span data-ng-repeat="clientScope in consent.grantedClientScopes">
+ <span ng-if="!$first">, </span>{{clientScope}}
</span>
</td>
<td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
index 95c5fd1..9e3dff6 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
@@ -33,7 +33,7 @@
<a href="#/realms/{{realm.realm}}"><span class="pficon pficon-settings"></span> {{:: 'realm-settings' | translate}}</a>
</li>
<li data-ng-show="access.queryClients" data-ng-class="(path[1] == 'client' || path[2] == 'clients' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
- <li data-ng-show="access.viewClients" data-ng-class="(path[1] == 'client-template' || path[2] == 'client-templates' || path[3] == 'client-templates') && 'active'"><a href="#/realms/{{realm.realm}}/client-templates"><i class="fa fa-cubes"></i> {{:: 'client-templates' | translate}}</a></li>
+ <li data-ng-show="access.viewClients" data-ng-class="(path[1] == 'client-scope' || path[2] == 'client-scopes' || path[3] == 'client-scopes') && 'active'"><a href="#/realms/{{realm.realm}}/client-scopes"><i class="fa fa-cubes"></i> {{:: 'client-scopes' | translate}}</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'role' || path[2] == 'roles' || path[2] == 'default-roles') && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
<li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[1] == 'identity-provider' || path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
index c294692..528e2d8 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
@@ -14,6 +14,10 @@
</li>
<li ng-class="{active: path[4] == 'saml'}" data-ng-show="client.protocol == 'saml' && (client.attributes['saml.client.signature'] == 'true' || client.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">{{:: 'saml-keys' | translate}}</a></li>
<li ng-class="{active: path[4] == 'roles'}" data-ng-show="!client.origin"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">{{:: 'roles' | translate}}</a></li>
+ <li ng-class="{active: path[4] == 'client-scopes'}" data-ng-show="!client.bearerOnly">
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/client-scopes/setup-scopes">{{:: 'client-scopes' | translate}}</a>
+ <kc-tooltip>{{:: 'client-scopes.tooltip' | translate}}</kc-tooltip>
+ </li>
<li ng-class="{active: path[4] == 'mappers'}" data-ng-show="!client.bearerOnly">
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">{{:: 'mappers' | translate}}</a>
<kc-tooltip>{{:: 'mappers.tooltip' | translate}}</kc-tooltip>
diff --git a/themes/src/main/resources/theme/base/login/login-oauth-grant.ftl b/themes/src/main/resources/theme/base/login/login-oauth-grant.ftl
index 8413db6..8c59276 100755
--- a/themes/src/main/resources/theme/base/login/login-oauth-grant.ftl
+++ b/themes/src/main/resources/theme/base/login/login-oauth-grant.ftl
@@ -10,40 +10,13 @@
<div id="kc-oauth" class="content-area">
<h3>${msg("oauthGrantRequest")}</h3>
<ul>
- <#if oauth.claimsRequested??>
- <li>
- <span>
- <strong>${msg("personalInfo")}</strong>
- <#list oauth.claimsRequested as claim>
- ${advancedMsg(claim)}<#if claim_has_next>, </#if>
- </#list>
- </span>
- </li>
- </#if>
- <#if oauth.accessRequestMessage??>
- <li>
- <span>
- ${oauth.accessRequestMessage}
- </span>
- </li>
- </#if>
- <#if oauth.realmRolesRequested??>
- <#list oauth.realmRolesRequested as role>
+ <#if oauth.clientScopesRequested??>
+ <#list oauth.clientScopesRequested as clientScope>
<li>
- <span><#if role.description??>${advancedMsg(role.description)}<#else>${advancedMsg(role.name)}</#if></span>
+ <span>${advancedMsg(clientScope.consentScreenText)}</span>
</li>
</#list>
</#if>
- <#if oauth.resourceRolesRequested??>
- <#list oauth.resourceRolesRequested?keys as resource>
- <#list oauth.resourceRolesRequested[resource] as clientRole>
- <li>
- <span class="kc-role"><#if clientRole.roleDescription??>${advancedMsg(clientRole.roleDescription)}<#else>${advancedMsg(clientRole.roleName)}</#if></span>
- <span class="kc-resource">${msg("inResource")} <strong><#if clientRole.clientName??>${advancedMsg(clientRole.clientName)}<#else>${clientRole.clientId}</#if></strong> </span>
- </li>
- </#list>
- </#list>
- </#if>
</ul>
<form class="form-actions" action="${url.oauthAction}" method="POST">
diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties
index 2684dc3..2ca0d70 100755
--- a/themes/src/main/resources/theme/base/login/messages/messages_en.properties
+++ b/themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -73,6 +73,13 @@ country=Country
emailVerified=Email verified
gssDelegationCredential=GSS Delegation Credential
+profileScopeConsentText=User profile
+emailScopeConsentText=Email address
+addressScopeConsentText=Address
+phoneScopeConsentText=Phone number
+offlineAccessScopeConsentText=Offline Access
+samlRoleListScopeConsentText=My Roles
+
loginTotpIntro=You are required to set up a One Time Password generator to access this account
loginTotpStep1=Install one of the following applications on your mobile
loginTotpStep2=Open the application and scan the barcode
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/index.ftl b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
index cddcee9..71b10a1 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
+++ b/themes/src/main/resources/theme/keycloak-preview/account/index.ftl
@@ -1,237 +1,237 @@
-<!DOCTYPE html>
-<html class="layout-pf-alt layout-pf-alt-fixed">
- <head>
- <title>${msg("accountManagementTitle")}</title>
-
- <meta charset="UTF-8">
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- <meta name="robots" content="noindex, nofollow">
- <meta name="viewport" content="width=device-width, initial-scale=1">
-
- <script>
- var authUrl = '${authUrl}';
- var baseUrl = '${baseUrl}';
- var realm = '${realm.name}';
- var resourceUrl = '${resourceUrl}';
- var isRegistrationEmailAsUsername = ${realm.registrationEmailAsUsername?c};
- var isEditUserNameAllowed = ${realm.editUsernameAllowed?c};
- var isInternationalizationEnabled = ${realm.internationalizationEnabled?c};
-
- var availableLocales = [];
- <#list supportedLocales as locale, label>
- availableLocales.push({locale : '${locale}', label : '${label}'});
- </#list>
-
- <#if referrer??>
- var referrer = '${referrer}';
- var referrer_uri = '${referrer_uri}';
- </#if>
-
- <#if msg??>
- var locale = '${locale}';
- var l18n_msg = JSON.parse('${msgJSON?no_esc}');
- <#else>
- var locale = 'en';
- var l18n_msg = {};
- </#if>
- </script>
-
- <base href="${baseUrl}/">
-
- <link rel="icon" href="${resourceUrl}/app/assets/img/favicon.ico" type="image/x-icon"/>
-
- <!-- PatternFly -->
- <!-- iPad retina icon -->
- <link rel="apple-touch-icon-precomposed" sizes="152x152"
- href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-152.png">
- <!-- iPad retina icon (iOS < 7) -->
- <link rel="apple-touch-icon-precomposed" sizes="144x144"
- href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-144.png">
- <!-- iPad non-retina icon -->
- <link rel="apple-touch-icon-precomposed" sizes="76x76"
- href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-76.png">
- <!-- iPad non-retina icon (iOS < 7) -->
- <link rel="apple-touch-icon-precomposed" sizes="72x72"
- href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-72.png">
- <!-- iPhone 6 Plus icon -->
- <link rel="apple-touch-icon-precomposed" sizes="120x120"
- href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-180.png">
- <!-- iPhone retina icon (iOS < 7) -->
- <link rel="apple-touch-icon-precomposed" sizes="114x114"
- href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-114.png">
- <!-- iPhone non-retina icon (iOS < 7) -->
- <link rel="apple-touch-icon-precomposed" sizes="57x57"
- href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-57.png">
- <link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly.min.css" rel="stylesheet"
- media="screen, print">
- <link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly-additions.min.css" rel="stylesheet"
- media="screen, print">
- <link rel="stylesheet" href="${resourceUrl}/node_modules/patternfly-ng/dist/css/patternfly-ng.min.css" media="screen, print">
-
- <script src="${resourceUrl}/node_modules/jquery/dist/jquery.min.js"></script>
- <script src="${resourceUrl}/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
- <script src="${resourceUrl}/node_modules/patternfly/dist/js/patternfly.min.js"></script>
- <script src="${authUrl}/js/keycloak.js"></script>
-
- <!-- TODO: We should save these css and js into variables and then load in
- main.ts for better performance. These might be loaded twice.
- -->
- <#if properties.styles?has_content>
- <#list properties.styles?split(' ') as style>
- <link href="${resourceUrl}/${style}" rel="stylesheet"/>
- </#list>
- <a href="../../../../../../../../keycloak-quickstarts/app-profile-jee-html5/src/main/webapp/index.html"></a>
- </#if>
-
- <#if properties.scripts?has_content>
- <#list properties.scripts?split(' ') as script>
- <script type="text/javascript" src="${resourceUrl}/${script}"></script>
- </#list>
- </#if>
- </head>
-
- <body>
-
- <script>
- var keycloak = Keycloak('${authUrl}/realms/${realm.name}/account/keycloak.json');
- var loadjs = function (url,loadListener) {
- const script = document.createElement("script");
- script.src = resourceUrl + url;
- if (loadListener) script.addEventListener("load", loadListener);
- document.head.appendChild(script);
- };
- keycloak.init({onLoad: 'check-sso'}).success(function(authenticated) {
- loadjs("/node_modules/core-js/client/shim.min.js", function(){
- loadjs("/node_modules/zone.js/dist/zone.min.js");
- loadjs("/node_modules/systemjs/dist/system.src.js", function() {
- loadjs("/systemjs.config.js");
- System.import('${resourceUrl}/main.js').catch(function (err) {
- console.error(err);
- });
- if (!keycloak.authenticated) document.getElementById("signInButton").style.visibility='visible';
- });
- });
- }).error(function() {
- alert('failed to initialize keycloak');
- });
- </script>
-
-
-<!-- Top Navigation -->
- <nav class="navbar navbar-pf-alt">
-
- <div class="navbar-header">
- <a href="http://www.keycloak.org" class="navbar-brand">
- <img class="navbar-brand-icon" type="image/svg+xml" src="${resourceUrl}/app/assets/img/keycloak-logo-min.png" alt="" width="auto" height="30px"/>
- </a>
- </div>
- <nav class="collapse navbar-collapse">
- <ul class="nav navbar-nav">
- </ul>
-
- <!-- This sign in button is only displayed in the rare case where we go directly to this page and we aren't logged in.
- Note javascript code above that changes its visibility for that case. Also, because we are not logged in
- we are unable to localize the button's message. Not sure what to do about that yet.
- -->
- <ul class="nav navbar-nav navbar-right navbar-iconic">
- <li><button id="signInButton" style="visibility:hidden" onclick="keycloak.login();" class="btn btn-primary btn-lg btn-sign" type="button">${msg("doLogIn")}</button></li>
- <#if realm.internationalizationEnabled && supportedLocales?size gt 1>
- <li class="dropdown">
- <a href="#0" class="dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
- ${msg("locale_" + locale)} <span class="caret"></span>
- </a>
- <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
- <#list supportedLocales as locale, label>
- <li><a href="${baseUrl}/?kc_locale=${locale}">${label}</a></li>
- </#list>
- </ul>
- </li>
- </#if>
- </ul>
- </nav>
- </nav>
-
-<!--Top Nav -->
-
-<!-- Home Page -->
-
- <div class="cards-pf" id="welcomeScreen">
- <div class="text-center">
- <h1>${msg("accountManagementWelcomeMessage")}</h1>
- </div>
- <div class="container-fluid container-cards-pf">
- <div class="row row-cards-pf">
- <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
- <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
- <div class="card-pf-body text-center row">
- <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
- <span class="fa pficon-user card-pf-icon-circle"></span>
- </div>
- <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
- <h2>${msg("personalInfoHtmlTitle")}</h2>
- <p class="card-pf-content-intro">${msg("personalInfoIntroMessage")}</p>
- <h3><a href="${baseUrl}/#/account">${msg("personalInfoHtmlTitle")}</a></h3>
- </div>
- </div>
- </div>
- </div>
- <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
- <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
- <div class="card-pf-body text-center row">
- <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
- <span class="fa fa-shield card-pf-icon-circle"></span>
- </div>
- <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
- <h2>${msg("accountSecurityTitle")}</h2>
- <p class="card-pf-content-intro">${msg("accountSecurityIntroMessage")}</p>
- <h3><a href="${baseUrl}/#/password">${msg("changePasswordHtmlTitle")}</a></h3>
- <h3><a href="${baseUrl}/#/authenticator">${msg("authenticatorTitle")}</a></h3>
- <h3><a href="${baseUrl}/#/device-activity">${msg("deviceActivityHtmlTitle")}</a></h3>
- <h3><a href="${baseUrl}/#/linked-accounts">${msg("linkedAccountsHtmlTitle")}</a></h3>
- </div>
- </div>
- </div>
- </div>
- <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
- <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
- <div class="card-pf-body text-center row">
- <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
- <span class="fa fa-th card-pf-icon-circle"></span>
- </div>
- <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
- <h2>${msg("applicationsHtmlTitle")}</h2>
- <p class="card-pf-content-intro">${msg("applicationsIntroMessage")}</p>
- <h3><a href="${baseUrl}/#/applications">${msg("applicationsHtmlTitle")}</a></h3>
- </div>
- </div>
- </div>
- </div>
- <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
- <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
- <div class="card-pf-body text-center row">
- <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
- <span class="fa pficon-repository card-pf-icon-circle"></span>
- </div>
- <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
- <h2>${msg("myResources")}</h2>
- <p class="card-pf-content-intro">${msg("resourceIntroMessage")}</p>
- <h3><a href="${baseUrl}/#/my-resources">${msg("myResources")}</a></h3>
- </div>
- </div>
- </div>
- </div>
-
- </div>
- </div>
- </div>
-
- <script>
- var winHash = window.location.hash;
- if ((winHash.indexOf('#/') == 0) && (!winHash.indexOf('#/&state') == 0)) {
- document.getElementById("welcomeScreen").style.display='none';
- }
- </script>
-
- <app-root></app-root>
- </body>
-</html>
+<!DOCTYPE html>
+<html class="layout-pf-alt layout-pf-alt-fixed">
+ <head>
+ <title>${msg("accountManagementTitle")}</title>
+
+ <meta charset="UTF-8">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <meta name="robots" content="noindex, nofollow">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <script>
+ var authUrl = '${authUrl}';
+ var baseUrl = '${baseUrl}';
+ var realm = '${realm.name}';
+ var resourceUrl = '${resourceUrl}';
+ var isRegistrationEmailAsUsername = ${realm.registrationEmailAsUsername?c};
+ var isEditUserNameAllowed = ${realm.editUsernameAllowed?c};
+ var isInternationalizationEnabled = ${realm.internationalizationEnabled?c};
+
+ var availableLocales = [];
+ <#list supportedLocales as locale, label>
+ availableLocales.push({locale : '${locale}', label : '${label}'});
+ </#list>
+
+ <#if referrer??>
+ var referrer = '${referrer}';
+ var referrer_uri = '${referrer_uri}';
+ </#if>
+
+ <#if msg??>
+ var locale = '${locale}';
+ var l18n_msg = JSON.parse('${msgJSON?no_esc}');
+ <#else>
+ var locale = 'en';
+ var l18n_msg = {};
+ </#if>
+ </script>
+
+ <base href="${baseUrl}/">
+
+ <link rel="icon" href="${resourceUrl}/app/assets/img/favicon.ico" type="image/x-icon"/>
+
+ <!-- PatternFly -->
+ <!-- iPad retina icon -->
+ <link rel="apple-touch-icon-precomposed" sizes="152x152"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-152.png">
+ <!-- iPad retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="144x144"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-144.png">
+ <!-- iPad non-retina icon -->
+ <link rel="apple-touch-icon-precomposed" sizes="76x76"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-76.png">
+ <!-- iPad non-retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="72x72"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-72.png">
+ <!-- iPhone 6 Plus icon -->
+ <link rel="apple-touch-icon-precomposed" sizes="120x120"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-180.png">
+ <!-- iPhone retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="114x114"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-114.png">
+ <!-- iPhone non-retina icon (iOS < 7) -->
+ <link rel="apple-touch-icon-precomposed" sizes="57x57"
+ href="${resourceUrl}/node_modules/patternfly/dist/img/apple-touch-icon-precomposed-57.png">
+ <link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly.min.css" rel="stylesheet"
+ media="screen, print">
+ <link href="${resourceUrl}/node_modules/patternfly/dist/css/patternfly-additions.min.css" rel="stylesheet"
+ media="screen, print">
+ <link rel="stylesheet" href="${resourceUrl}/node_modules/patternfly-ng/dist/css/patternfly-ng.min.css" media="screen, print">
+
+ <script src="${resourceUrl}/node_modules/jquery/dist/jquery.min.js"></script>
+ <script src="${resourceUrl}/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
+ <script src="${resourceUrl}/node_modules/patternfly/dist/js/patternfly.min.js"></script>
+ <script src="${authUrl}/js/keycloak.js"></script>
+
+ <!-- TODO: We should save these css and js into variables and then load in
+ main.ts for better performance. These might be loaded twice.
+ -->
+ <#if properties.styles?has_content>
+ <#list properties.styles?split(' ') as style>
+ <link href="${resourceUrl}/${style}" rel="stylesheet"/>
+ </#list>
+ <a href="../../../../../../../../keycloak-quickstarts/app-profile-jee-html5/src/main/webapp/index.html"></a>
+ </#if>
+
+ <#if properties.scripts?has_content>
+ <#list properties.scripts?split(' ') as script>
+ <script type="text/javascript" src="${resourceUrl}/${script}"></script>
+ </#list>
+ </#if>
+ </head>
+
+ <body>
+
+ <script>
+ var keycloak = Keycloak('${authUrl}/realms/${realm.name}/account/keycloak.json');
+ var loadjs = function (url,loadListener) {
+ const script = document.createElement("script");
+ script.src = resourceUrl + url;
+ if (loadListener) script.addEventListener("load", loadListener);
+ document.head.appendChild(script);
+ };
+ keycloak.init({onLoad: 'check-sso'}).success(function(authenticated) {
+ loadjs("/node_modules/core-js/client/shim.min.js", function(){
+ loadjs("/node_modules/zone.js/dist/zone.min.js");
+ loadjs("/node_modules/systemjs/dist/system.src.js", function() {
+ loadjs("/systemjs.config.js");
+ System.import('${resourceUrl}/main.js').catch(function (err) {
+ console.error(err);
+ });
+ if (!keycloak.authenticated) document.getElementById("signInButton").style.visibility='visible';
+ });
+ });
+ }).error(function() {
+ alert('failed to initialize keycloak');
+ });
+ </script>
+
+
+<!-- Top Navigation -->
+ <nav class="navbar navbar-pf-alt">
+
+ <div class="navbar-header">
+ <a href="http://www.keycloak.org" class="navbar-brand">
+ <img class="navbar-brand-icon" type="image/svg+xml" src="${resourceUrl}/app/assets/img/keycloak-logo-min.png" alt="" width="auto" height="30px"/>
+ </a>
+ </div>
+ <nav class="collapse navbar-collapse">
+ <ul class="nav navbar-nav">
+ </ul>
+
+ <!-- This sign in button is only displayed in the rare case where we go directly to this page and we aren't logged in.
+ Note javascript code above that changes its visibility for that case. Also, because we are not logged in
+ we are unable to localize the button's message. Not sure what to do about that yet.
+ -->
+ <ul class="nav navbar-nav navbar-right navbar-iconic">
+ <li><button id="signInButton" style="visibility:hidden" onclick="keycloak.login();" class="btn btn-primary btn-lg btn-sign" type="button">${msg("doLogIn")}</button></li>
+ <#if realm.internationalizationEnabled && supportedLocales?size gt 1>
+ <li class="dropdown">
+ <a href="#0" class="dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+ ${msg("locale_" + locale)} <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
+ <#list supportedLocales as locale, label>
+ <li><a href="${baseUrl}/?kc_locale=${locale}">${label}</a></li>
+ </#list>
+ </ul>
+ </li>
+ </#if>
+ </ul>
+ </nav>
+ </nav>
+
+<!--Top Nav -->
+
+<!-- Home Page -->
+
+ <div class="cards-pf" id="welcomeScreen">
+ <div class="text-center">
+ <h1>${msg("accountManagementWelcomeMessage")}</h1>
+ </div>
+ <div class="container-fluid container-cards-pf">
+ <div class="row row-cards-pf">
+ <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
+ <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+ <div class="card-pf-body text-center row">
+ <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
+ <span class="fa pficon-user card-pf-icon-circle"></span>
+ </div>
+ <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
+ <h2>${msg("personalInfoHtmlTitle")}</h2>
+ <p class="card-pf-content-intro">${msg("personalInfoIntroMessage")}</p>
+ <h3><a href="${baseUrl}/#/account">${msg("personalInfoHtmlTitle")}</a></h3>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
+ <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+ <div class="card-pf-body text-center row">
+ <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
+ <span class="fa fa-shield card-pf-icon-circle"></span>
+ </div>
+ <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
+ <h2>${msg("accountSecurityTitle")}</h2>
+ <p class="card-pf-content-intro">${msg("accountSecurityIntroMessage")}</p>
+ <h3><a href="${baseUrl}/#/password">${msg("changePasswordHtmlTitle")}</a></h3>
+ <h3><a href="${baseUrl}/#/authenticator">${msg("authenticatorTitle")}</a></h3>
+ <h3><a href="${baseUrl}/#/device-activity">${msg("deviceActivityHtmlTitle")}</a></h3>
+ <h3><a href="${baseUrl}/#/linked-accounts">${msg("linkedAccountsHtmlTitle")}</a></h3>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
+ <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+ <div class="card-pf-body text-center row">
+ <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
+ <span class="fa fa-th card-pf-icon-circle"></span>
+ </div>
+ <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
+ <h2>${msg("applicationsHtmlTitle")}</h2>
+ <p class="card-pf-content-intro">${msg("applicationsIntroMessage")}</p>
+ <h3><a href="${baseUrl}/#/applications">${msg("applicationsHtmlTitle")}</a></h3>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-xs-12 col-sm-4 col-md-4 col-lg-3">
+ <div class="card-pf card-pf-view card-pf-view-select card-pf-view-single-select">
+ <div class="card-pf-body text-center row">
+ <div class="card-pf-top-element col-xs-2 col-sm-12 col-md-12 col-lg-12">
+ <span class="fa pficon-repository card-pf-icon-circle"></span>
+ </div>
+ <div class="card-pf-content col-xs-10 col-sm-12 col-md-12 col-lg-12">
+ <h2>${msg("myResources")}</h2>
+ <p class="card-pf-content-intro">${msg("resourceIntroMessage")}</p>
+ <h3><a href="${baseUrl}/#/my-resources">${msg("myResources")}</a></h3>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ <script>
+ var winHash = window.location.hash;
+ if ((winHash.indexOf('#/') == 0) && (!winHash.indexOf('#/&state') == 0)) {
+ document.getElementById("welcomeScreen").style.display='none';
+ }
+ </script>
+
+ <app-root></app-root>
+ </body>
+</html>
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
index 3391ed7..82a2eec 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app.module.ts
@@ -1,83 +1,83 @@
-/*
- * Copyright 2017 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { BrowserModule } from '@angular/platform-browser';
-import { NgModule } from '@angular/core';
-import { FormsModule } from '@angular/forms';
-import { HttpModule } from '@angular/http';
-import { RouterModule, Routes } from '@angular/router';
-import { LocationStrategy, HashLocationStrategy } from '@angular/common';
-
-import { TranslateLoader } from '@ngx-translate/core';
-import { TranslateModule } from '@ngx-translate/core';
-
-import { KeycloakService } from './keycloak-service/keycloak.service';
-import { KEYCLOAK_HTTP_PROVIDER } from './keycloak-service/keycloak.http';
-import {KeycloakGuard} from './keycloak-service/keycloak.guard';
-
-import {ResponsivenessService} from './responsiveness-service/responsiveness.service'
-
-import { AccountServiceClient } from './account-service/account.service';
-import {TranslateUtil} from './ngx-translate/translate.util';
-
-import { DeclaredVarTranslateLoader } from './ngx-translate/declared.var.translate.loader';
-import { AppComponent } from './app.component';
-import { TopNavComponent } from './top-nav/top-nav.component';
-import { NotificationComponent } from './top-nav/notification.component';
-import { ToastNotifier } from './top-nav/toast.notifier';
-import { SideNavComponent } from './side-nav/side-nav.component';
-import {VerticalNavComponent} from './vertical-nav/vertical-nav.component';
-
-import { NavigationModule } from 'patternfly-ng/navigation';
-
-/* Routing Module */
-import { AppRoutingModule } from './app-routing.module';
-
-const decs = [
- AppComponent,
- TopNavComponent,
- NotificationComponent,
- SideNavComponent,
- VerticalNavComponent,
-];
-
-export const ORIGINAL_INCOMING_URL: Location = window.location;
-
-@NgModule({
- declarations: decs,
- imports: [
- BrowserModule,
- FormsModule,
- HttpModule,
- NavigationModule,
- TranslateModule.forRoot({
- loader: {provide: TranslateLoader, useClass: DeclaredVarTranslateLoader}
- }),
- AppRoutingModule,
- ],
- providers: [
- KeycloakService,
- KeycloakGuard,
- KEYCLOAK_HTTP_PROVIDER,
- ResponsivenessService,
- AccountServiceClient,
- TranslateUtil,
- ToastNotifier,
- { provide: LocationStrategy, useClass: HashLocationStrategy }
- ],
- bootstrap: [AppComponent]
-})
-export class AppModule { }
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { HttpModule } from '@angular/http';
+import { RouterModule, Routes } from '@angular/router';
+import { LocationStrategy, HashLocationStrategy } from '@angular/common';
+
+import { TranslateLoader } from '@ngx-translate/core';
+import { TranslateModule } from '@ngx-translate/core';
+
+import { KeycloakService } from './keycloak-service/keycloak.service';
+import { KEYCLOAK_HTTP_PROVIDER } from './keycloak-service/keycloak.http';
+import {KeycloakGuard} from './keycloak-service/keycloak.guard';
+
+import {ResponsivenessService} from './responsiveness-service/responsiveness.service'
+
+import { AccountServiceClient } from './account-service/account.service';
+import {TranslateUtil} from './ngx-translate/translate.util';
+
+import { DeclaredVarTranslateLoader } from './ngx-translate/declared.var.translate.loader';
+import { AppComponent } from './app.component';
+import { TopNavComponent } from './top-nav/top-nav.component';
+import { NotificationComponent } from './top-nav/notification.component';
+import { ToastNotifier } from './top-nav/toast.notifier';
+import { SideNavComponent } from './side-nav/side-nav.component';
+import {VerticalNavComponent} from './vertical-nav/vertical-nav.component';
+
+import { NavigationModule } from 'patternfly-ng/navigation';
+
+/* Routing Module */
+import { AppRoutingModule } from './app-routing.module';
+
+const decs = [
+ AppComponent,
+ TopNavComponent,
+ NotificationComponent,
+ SideNavComponent,
+ VerticalNavComponent,
+];
+
+export const ORIGINAL_INCOMING_URL: Location = window.location;
+
+@NgModule({
+ declarations: decs,
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HttpModule,
+ NavigationModule,
+ TranslateModule.forRoot({
+ loader: {provide: TranslateLoader, useClass: DeclaredVarTranslateLoader}
+ }),
+ AppRoutingModule,
+ ],
+ providers: [
+ KeycloakService,
+ KeycloakGuard,
+ KEYCLOAK_HTTP_PROVIDER,
+ ResponsivenessService,
+ AccountServiceClient,
+ TranslateUtil,
+ ToastNotifier,
+ { provide: LocationStrategy, useClass: HashLocationStrategy }
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts
index 1753028..0c74289 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/app-routing.module.ts
@@ -1,47 +1,47 @@
-/*
- * Copyright 2018 Red Hat, Inc. and/or its affiliates
- * and other contributors as indicated by the @author tags.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import { NgModule } from '@angular/core';
-import { Routes, RouterModule } from '@angular/router';
-
-import {KeycloakGuard} from './keycloak-service/keycloak.guard';
-
-import { HomePageComponent } from './content/home-page/home-page.component';
-
-declare const resourceUrl: string;
-
-export const routes: Routes = [
- {path: '', canActivateChild:[KeycloakGuard], children: [
- { path: 'account', loadChildren: resourceUrl + '/app/content/account-page/account.module.js#AccountModule' },
- { path: 'password', loadChildren: resourceUrl + '/app/content/password-page/password.module.js#PasswordModule' },
- { path: 'authenticator', loadChildren: resourceUrl + '/app/content/authenticator-page/authenticator.module.js#AuthenticatorModule' },
- { path: 'device-activity', loadChildren: resourceUrl + '/app/content/device-activity-page/device-activity.module.js#DeviceActivityModule' },
- { path: 'sessions', loadChildren: resourceUrl + '/app/content/sessions-page/sessions.module.js#SessionsModule' },
- { path: 'applications', loadChildren: resourceUrl + '/app/content/applications-page/applications.module.js#ApplicationsModule' },
- { path: 'linked-accounts', loadChildren: resourceUrl + '/app/content/linked-accounts-page/linked-accounts.module.js#LinkedAccountsModule' },
- { path: 'my-resources', loadChildren: resourceUrl + '/app/content/my-resources-page/my-resources.module.js#MyResourcesModule' },
- { path: ':**', loadChildren: resourceUrl + '/app/content/page-not-found/page-not-found.module.js#PageNotFoundModule' },
- ]
- }
- ];
-
-@NgModule({
- imports: [RouterModule.forRoot(routes)],
- exports: [RouterModule],
- declarations: [HomePageComponent]
-})
-export class AppRoutingModule {}
-
+/*
+ * Copyright 2018 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import {KeycloakGuard} from './keycloak-service/keycloak.guard';
+
+import { HomePageComponent } from './content/home-page/home-page.component';
+
+declare const resourceUrl: string;
+
+export const routes: Routes = [
+ {path: '', canActivateChild:[KeycloakGuard], children: [
+ { path: 'account', loadChildren: resourceUrl + '/app/content/account-page/account.module.js#AccountModule' },
+ { path: 'password', loadChildren: resourceUrl + '/app/content/password-page/password.module.js#PasswordModule' },
+ { path: 'authenticator', loadChildren: resourceUrl + '/app/content/authenticator-page/authenticator.module.js#AuthenticatorModule' },
+ { path: 'device-activity', loadChildren: resourceUrl + '/app/content/device-activity-page/device-activity.module.js#DeviceActivityModule' },
+ { path: 'sessions', loadChildren: resourceUrl + '/app/content/sessions-page/sessions.module.js#SessionsModule' },
+ { path: 'applications', loadChildren: resourceUrl + '/app/content/applications-page/applications.module.js#ApplicationsModule' },
+ { path: 'linked-accounts', loadChildren: resourceUrl + '/app/content/linked-accounts-page/linked-accounts.module.js#LinkedAccountsModule' },
+ { path: 'my-resources', loadChildren: resourceUrl + '/app/content/my-resources-page/my-resources.module.js#MyResourcesModule' },
+ { path: ':**', loadChildren: resourceUrl + '/app/content/page-not-found/page-not-found.module.js#PageNotFoundModule' },
+ ]
+ }
+ ];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule],
+ declarations: [HomePageComponent]
+})
+export class AppRoutingModule {}
+
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
index 271cd50..c5cfa0d 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/app/keycloak-service/keycloak.js
@@ -250,7 +250,16 @@
baseUrl = kc.endpoints.authorize();
}
- var scope = (options && options.scope) ? "openid " + options.scope : "openid";
+ var scope;
+ if (options && options.scope) {
+ if (options.scope.indexOf("openid") != -1) {
+ scope = options.scope;
+ } else {
+ scope = "openid " + options.scope;
+ }
+ } else {
+ scope = "openid";
+ }
var url = baseUrl
+ '?client_id=' + encodeURIComponent(kc.clientId)
diff --git a/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css b/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
index 525187a..2738cf6 100644
--- a/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
+++ b/themes/src/main/resources/theme/keycloak-preview/account/resources/styles.css
@@ -1,329 +1,329 @@
-/* Welcome Page */
-
-body {
- background: #f5f5f5;
-}
-.layout-pf-alt.layout-pf-alt-fixed body {
- padding-top: 55px;
-}
-.cards-pf {
- background: #f5f5f5;
-}
-.card-pf .row-cards-pf:first-child {
- padding-top: 0px;
-}
-.cards-pf .row-cards-pf {
- padding: 0 20px;
-}
-.card-pf-title {
- margin: 0 0 20px;
-}
-p.description {
- font-size: 14px;
- margin: 10px auto 20px;
- max-width: 60%;
- padding: 0 10px;
- text-align: center;
-}
-.card-pf-content-intro {
- padding-bottom: 10px;
- border-bottom: 1px solid #f5f5f5;
-}
-.card-pf-content h3 {
- padding-bottom: 10px;
- border-bottom: 1px solid #f5f5f5;
-}
-.card-pf-content h3:last-child {
- border-bottom: 0;
-}
-
-.btn-sign {
- margin-top: 10px;
-}
-
-.layout-pf-alt.layout-pf-alt-fixed .container-pf-alt-nav-pf-vertical-alt {
- margin-left: 210px;
-}
-
-.nav .nav-item-iconic {
- color: #d1d1d1;
-}
-
-.content-area {
- padding: 30px 30px 20px;
- background: #ffffff;
- margin-bottom: 10px;
-}
-
-
-/*Responsive Design*/
-@media (max-width: 767px) {
- .container-fluid {
- padding-left: 0;
- padding-right: 0;
- }
- .cards-pf {
- background: #ffffff;
- }
- .cards-pf .row-cards-pf {
- padding: 0;
- }
-
- .card-pf {
- margin: 0 0 10px;
- background: #ffffff !important;
- box-shadow: 0 0 0;
- border-bottom: 1px solid #f5f5f5;
- border-top: 0;
- padding: 0;
- }
- .card-pf-body {
- padding: 20px 20px 10px;
- margin-top: 0;
- }
- .card-pf-view.card-pf-view-select:hover {
- box-shadow: 0 0 0;
- }
- .cards-pf .row-cards-pf:first-child {
- padding-top: 0;
- }
- .container-cards-pf {
- margin-top: 0;
- }
- .col-xs-12,
- .col-xs-2,
- .col-xs-10,
- .col-sm-12,
- .col-sm-4,
- .col-md-4,
- .col-md-8,
- .col-lg-3,
- .container-cards-pf {
- padding-left: 0;
- padding-right: 0;
- }
- #welcomeScreen .text-center h1,
- #welcomeScreen .card-pf-body hr{
- display: none;
- }
- .card-pf-view .card-pf-top-element .card-pf-icon-circle {
- width: 30px;
- height: 30px;
- font-size: 16px;
- line-height: 26px;
- margin-top: -8px;
- }
- .card-pf-content-intro {
- border-bottom: 0;
- }
- .card-pf-content p {
- padding-bottom: 5px;
- }
- .card-pf-content h3 {
- border-bottom: 0;
- margin-top: 5px;
- }
- .card-pf-content {
- text-align: left;
- margin-top: -20px;
- }
- .card-pf-content h2 {
- font-size: 16px;
- font-weight: bold;
- }
- .card-pf-content h3{
- font-size: 14px;
- padding-bottom: 0;
- }
- .page-header {
- background: #f5f5f5;
- margin: 0;
- }
- .page-header h1 {
- margin-top: 0;
- line-height: 30px;
- padding-top: 20px;
- padding-left: 20px;
- font-size: 18px;
- }
- .content-area {
- padding-top: 20px;
- padding-left: 20px;
- padding-right: 20px;
- }
- .list-view-pf-view {
- margin-top: 0;
- }
- .list-view-pf-main-info {
- padding-bottom: 10px;
- }
- .row {
- margin-left: 0;
- margin-right: 0;
- }
- .pull-right-sm {
- float: none !important;
- margin-bottom: 10px;
- }
- .p-b {
- padding-bottom: 20px;
- }
-
-}
-
-@media (min-width: 768px) {
- .cards-pf .row-cards-pf {
- padding: 0;
- }
- .container-cards-pf {
- margin-top: 0;
- }
-}
-
-/* personal Info Style */
-.subtitle {
- color: #4d5258;
- font-size: 12px;
- margin-bottom: 10px;
-}
-
-.content-area .required{
- color: #cc0000;
-}
-
-.page-header {
- border-bottom: 0;
-}
-
-.non-edit {
- border: 1px solid #bbb;
- padding: 3px 10px;
- background-color: #f5f5f5;
-}
-
-/* Introduction Message on the left */
-.introMessage {
- margin: 10px 20px 20px 0;
-}
-
-/* Device Activity */
-
-.card-title {
- margin-left: 20px;
- margin-bottom: 30px;
-}
-.detail-description {
- margin-top:-10px;
-}
-.list-view-pf-view {
- margin-top: 0;
-}
-.m-l {
- margin-left: 20px;
-}
-.block-box {
- margin-left: 0;
- display: block;
-}
-.activity-item {
- padding-left: 60px;
-}
-.activity-item ul {
- list-style: none;
-}
-.activity-item h3 .fa {
- margin-left: -30px;
- margin-right: 5px;
- font-size: 20px;
-}
-.btn-logout {
- margin-left: 40px;
- margin-bottom: 20px;
- margin-top: 5px;
-}
-.list-view-pf .current-color {
- font-weight: bold;
- color: #4F9207!important;
-}
-.list-view-pf .list-group-item.list-view-pf-expand-active,
-.list-view-pf .list-group-item.list-view-pf-expand-active:first-child {
- border-top-color: #d8d8d8;
- border: solid 1px #d8d8d8;
-}
-.list-group-item-container {
- border-top: solid 1px #d8d8d8;
-}
-
-/* Responsive Design for Devices Activiy*/
-@media (max-width: 767px) {
- .activity-item {
- padding-left: 0;
- }
- .list-group-item-header {
- padding: 0;
- }
- .list-view-pf .list-group-item.list-view-pf-expand-active {
- margin-left: -21px;
- margin-right: -21px;
- }
- .list-view-pf .list-group-item.list-view-pf-expand-active .list-group-item-header {
- padding-left: 20px;
- padding-right: 20px;
- }
- .list-group {
- margin-bottom: 0;
- }
- .list-view-pf .list-group-item:last-child {
- border-bottom: 0;
- }
- .list-group-item-container {
- padding-left: 5px;
- padding-right: 25px;
- }
-}
-
-
-/* Application Style */
-.card-pf-application {
- padding: 0;
-}
-.card-pf-application .card-pf-body {
- padding-bottom: 0;
-}
-.card-pf-application .toolbar-pf {
- margin: 0;
- border-bottom: 0;
-}
-#toolbar-application-mobile {
- display: none;
-}
-.card-pf-application .close {
- position: absolute;
- right: 10px;
-}
-
-@media (max-width: 767px) {
- .card-pf-application h2 {
- margin-top: -10px;
- }
- .card-pf-application h2 a {
- text-decoration: none;
- }
- .card-pf-application .list-group-item-container {
- padding-left: 20px;
- padding-right: 20px;
- margin-left: -20px;
- }
- .card-pf-application .form-horizontal {
- margin-bottom: 10px;
- }
- #toolbar-application-mobile {
- display: block;
- }
- #toolbar-application {
- display: none;
- }
- .non-display {
- display: none;
- }
-}
+/* Welcome Page */
+
+body {
+ background: #f5f5f5;
+}
+.layout-pf-alt.layout-pf-alt-fixed body {
+ padding-top: 55px;
+}
+.cards-pf {
+ background: #f5f5f5;
+}
+.card-pf .row-cards-pf:first-child {
+ padding-top: 0px;
+}
+.cards-pf .row-cards-pf {
+ padding: 0 20px;
+}
+.card-pf-title {
+ margin: 0 0 20px;
+}
+p.description {
+ font-size: 14px;
+ margin: 10px auto 20px;
+ max-width: 60%;
+ padding: 0 10px;
+ text-align: center;
+}
+.card-pf-content-intro {
+ padding-bottom: 10px;
+ border-bottom: 1px solid #f5f5f5;
+}
+.card-pf-content h3 {
+ padding-bottom: 10px;
+ border-bottom: 1px solid #f5f5f5;
+}
+.card-pf-content h3:last-child {
+ border-bottom: 0;
+}
+
+.btn-sign {
+ margin-top: 10px;
+}
+
+.layout-pf-alt.layout-pf-alt-fixed .container-pf-alt-nav-pf-vertical-alt {
+ margin-left: 210px;
+}
+
+.nav .nav-item-iconic {
+ color: #d1d1d1;
+}
+
+.content-area {
+ padding: 30px 30px 20px;
+ background: #ffffff;
+ margin-bottom: 10px;
+}
+
+
+/*Responsive Design*/
+@media (max-width: 767px) {
+ .container-fluid {
+ padding-left: 0;
+ padding-right: 0;
+ }
+ .cards-pf {
+ background: #ffffff;
+ }
+ .cards-pf .row-cards-pf {
+ padding: 0;
+ }
+
+ .card-pf {
+ margin: 0 0 10px;
+ background: #ffffff !important;
+ box-shadow: 0 0 0;
+ border-bottom: 1px solid #f5f5f5;
+ border-top: 0;
+ padding: 0;
+ }
+ .card-pf-body {
+ padding: 20px 20px 10px;
+ margin-top: 0;
+ }
+ .card-pf-view.card-pf-view-select:hover {
+ box-shadow: 0 0 0;
+ }
+ .cards-pf .row-cards-pf:first-child {
+ padding-top: 0;
+ }
+ .container-cards-pf {
+ margin-top: 0;
+ }
+ .col-xs-12,
+ .col-xs-2,
+ .col-xs-10,
+ .col-sm-12,
+ .col-sm-4,
+ .col-md-4,
+ .col-md-8,
+ .col-lg-3,
+ .container-cards-pf {
+ padding-left: 0;
+ padding-right: 0;
+ }
+ #welcomeScreen .text-center h1,
+ #welcomeScreen .card-pf-body hr{
+ display: none;
+ }
+ .card-pf-view .card-pf-top-element .card-pf-icon-circle {
+ width: 30px;
+ height: 30px;
+ font-size: 16px;
+ line-height: 26px;
+ margin-top: -8px;
+ }
+ .card-pf-content-intro {
+ border-bottom: 0;
+ }
+ .card-pf-content p {
+ padding-bottom: 5px;
+ }
+ .card-pf-content h3 {
+ border-bottom: 0;
+ margin-top: 5px;
+ }
+ .card-pf-content {
+ text-align: left;
+ margin-top: -20px;
+ }
+ .card-pf-content h2 {
+ font-size: 16px;
+ font-weight: bold;
+ }
+ .card-pf-content h3{
+ font-size: 14px;
+ padding-bottom: 0;
+ }
+ .page-header {
+ background: #f5f5f5;
+ margin: 0;
+ }
+ .page-header h1 {
+ margin-top: 0;
+ line-height: 30px;
+ padding-top: 20px;
+ padding-left: 20px;
+ font-size: 18px;
+ }
+ .content-area {
+ padding-top: 20px;
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+ .list-view-pf-view {
+ margin-top: 0;
+ }
+ .list-view-pf-main-info {
+ padding-bottom: 10px;
+ }
+ .row {
+ margin-left: 0;
+ margin-right: 0;
+ }
+ .pull-right-sm {
+ float: none !important;
+ margin-bottom: 10px;
+ }
+ .p-b {
+ padding-bottom: 20px;
+ }
+
+}
+
+@media (min-width: 768px) {
+ .cards-pf .row-cards-pf {
+ padding: 0;
+ }
+ .container-cards-pf {
+ margin-top: 0;
+ }
+}
+
+/* personal Info Style */
+.subtitle {
+ color: #4d5258;
+ font-size: 12px;
+ margin-bottom: 10px;
+}
+
+.content-area .required{
+ color: #cc0000;
+}
+
+.page-header {
+ border-bottom: 0;
+}
+
+.non-edit {
+ border: 1px solid #bbb;
+ padding: 3px 10px;
+ background-color: #f5f5f5;
+}
+
+/* Introduction Message on the left */
+.introMessage {
+ margin: 10px 20px 20px 0;
+}
+
+/* Device Activity */
+
+.card-title {
+ margin-left: 20px;
+ margin-bottom: 30px;
+}
+.detail-description {
+ margin-top:-10px;
+}
+.list-view-pf-view {
+ margin-top: 0;
+}
+.m-l {
+ margin-left: 20px;
+}
+.block-box {
+ margin-left: 0;
+ display: block;
+}
+.activity-item {
+ padding-left: 60px;
+}
+.activity-item ul {
+ list-style: none;
+}
+.activity-item h3 .fa {
+ margin-left: -30px;
+ margin-right: 5px;
+ font-size: 20px;
+}
+.btn-logout {
+ margin-left: 40px;
+ margin-bottom: 20px;
+ margin-top: 5px;
+}
+.list-view-pf .current-color {
+ font-weight: bold;
+ color: #4F9207!important;
+}
+.list-view-pf .list-group-item.list-view-pf-expand-active,
+.list-view-pf .list-group-item.list-view-pf-expand-active:first-child {
+ border-top-color: #d8d8d8;
+ border: solid 1px #d8d8d8;
+}
+.list-group-item-container {
+ border-top: solid 1px #d8d8d8;
+}
+
+/* Responsive Design for Devices Activiy*/
+@media (max-width: 767px) {
+ .activity-item {
+ padding-left: 0;
+ }
+ .list-group-item-header {
+ padding: 0;
+ }
+ .list-view-pf .list-group-item.list-view-pf-expand-active {
+ margin-left: -21px;
+ margin-right: -21px;
+ }
+ .list-view-pf .list-group-item.list-view-pf-expand-active .list-group-item-header {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+ .list-group {
+ margin-bottom: 0;
+ }
+ .list-view-pf .list-group-item:last-child {
+ border-bottom: 0;
+ }
+ .list-group-item-container {
+ padding-left: 5px;
+ padding-right: 25px;
+ }
+}
+
+
+/* Application Style */
+.card-pf-application {
+ padding: 0;
+}
+.card-pf-application .card-pf-body {
+ padding-bottom: 0;
+}
+.card-pf-application .toolbar-pf {
+ margin: 0;
+ border-bottom: 0;
+}
+#toolbar-application-mobile {
+ display: none;
+}
+.card-pf-application .close {
+ position: absolute;
+ right: 10px;
+}
+
+@media (max-width: 767px) {
+ .card-pf-application h2 {
+ margin-top: -10px;
+ }
+ .card-pf-application h2 a {
+ text-decoration: none;
+ }
+ .card-pf-application .list-group-item-container {
+ padding-left: 20px;
+ padding-right: 20px;
+ margin-left: -20px;
+ }
+ .card-pf-application .form-horizontal {
+ margin-bottom: 10px;
+ }
+ #toolbar-application-mobile {
+ display: block;
+ }
+ #toolbar-application {
+ display: none;
+ }
+ .non-display {
+ display: none;
+ }
+}
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties
index 56eb33a..7de9fff 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ja.properties
@@ -572,8 +572,8 @@ allowed-client-templates.tooltip=新規に登録されたクライアントで�
max-clients.label=レルムあたりの最大クライアント数
max-clients.tooltip=レルム内の既存のクライアントの数が設定された制限と同じかそれ以上の場合は、新しいクライアントを登録することはできません。
-client-templates=クライアントテンプレート
-client-templates.tooltip=クライアントテンプレートでは、複数のクライアントで共有する共通設定を定義することができます。
+client-scopes=クライアントテンプレート
+client-scopes.tooltip=クライアントテンプレートでは、複数のクライアントで共有する共通設定を定義することができます。
groups=グループ
@@ -634,7 +634,7 @@ select-client-role=クライアントロールを選択
client-template=クライアントテンプレート
client-template.tooltip=設定を引き継ぐクライアントテンプレートを選択します。
client-saml-endpoint=クライアント SAML エンドポイント
-add-client-template=クライアントテンプレートを追加
+add-client-scope=クライアントテンプレートを追加
manage=管理
authentication=認証
@@ -646,9 +646,9 @@ configure=設定
select-realm=レルムの選択
add=追加
-client-template.name.tooltip=クライアントテンプレートの名前です。レルム内でユニークにする必要があります。
-client-template.description.tooltip=クライアントテンプレートの説明です。
-client-template.protocol.tooltip=このクライアントテンプレートにより、どの SSO プロトコルが提供されるか設定します。
+client-scope.name.tooltip=クライアントテンプレートの名前です。レルム内でユニークにする必要があります。
+client-scope.description.tooltip=クライアントテンプレートの説明です。
+client-scope.protocol.tooltip=このクライアントテンプレートにより、どの SSO プロトコルが提供されるか設定します。
add-user-federation-provider=ユーザー フェデレーション プロバイダーの追加
add-user-storage-provider=ユーザー ストレージ プロバイダーの追加
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_lt.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_lt.properties
index b9641bf..4c9a48c 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_lt.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_lt.properties
@@ -571,8 +571,8 @@ allowed-client-templates.tooltip=Leid\u017Eiam\u0173 kliento \u0161ablon\u0173 s
max-clients.label=Mksimalus srities klient\u0173 skai\u010Dius
max-clients.tooltip=Nauj\u0173 klient\u0173 registracija draud\u017Eiama, jei u\u017Eregistruot\u0173 klient\u0173 skai\u010Dius yra toks pats arba didesnis nei nustatytas limitas.
-client-templates=Klient\u0173 \u0161ablonai
-client-templates.tooltip=Klient\u0173 \u0161ablonai leid\u017Eia nurodyti bendr\u0105 vis\u0173 klient\u0173 konfig\u016Bracij\u0105
+client-scopes=Klient\u0173 \u0161ablonai
+client-scopes.tooltip=Klient\u0173 \u0161ablonai leid\u017Eia nurodyti bendr\u0105 vis\u0173 klient\u0173 konfig\u016Bracij\u0105
groups=Grup\u0117s
@@ -633,7 +633,7 @@ select-client-role=Pasirinkti kliento rol\u0119
client-template=Kliento \u0161ablonas
client-template.tooltip=Kliento \u0161ablonas, i\u0161 kurio paveldima konfig\u016Bracija
client-saml-endpoint=Kliento SAML adresas
-add-client-template=Kliento \u0161ablono k\u016Brimas
+add-client-scope=Kliento \u0161ablono k\u016Brimas
manage=Valdyti
authentication=Autentifikavimas
@@ -645,9 +645,9 @@ configure=Konfig\u016Bruoti
select-realm=Pasirinkite srit\u012F
add=Prid\u0117ti
-client-template.name.tooltip=Kliento \u0161ablono pavadinimas. Privalo b\u016Bti unikalus \u0161ioje srityje
-client-template.description.tooltip=Kliento \u0161ablono apra\u0161ymas
-client-template.protocol.tooltip=Kurio SSO protokolo konfig\u016Bracija teikia \u0161is \u0161ablonas
+client-scope.name.tooltip=Kliento \u0161ablono pavadinimas. Privalo b\u016Bti unikalus \u0161ioje srityje
+client-scope.description.tooltip=Kliento \u0161ablono apra\u0161ymas
+client-scope.protocol.tooltip=Kurio SSO protokolo konfig\u016Bracija teikia \u0161is \u0161ablonas
add-user-federation-provider=Prid\u0117ti naudotoj\u0173 federacijos teik\u0117ja
required-settings=Privalomi nustatymai
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties
index 62eafb2..c1d777a 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_no.properties
@@ -531,8 +531,8 @@ client-reg-count.tooltip=Tillat antall klientregistreringsforesp\u00F8rsler fra
client-reg-remainingCount.tooltip=Gjenv\u00E6rende antall klientregistreringsforesp\u00F8rsler fra denne verten. Du m\u00E5 restarte denne n\u00E5r grensen er n\u00E5dd.
reset-remaining-count=Tilbakestill gjenst\u00E5ende antall
-client-templates=Klientmaler
-client-templates.tooltip=Klientmaler tillater deg \u00E5 definere felles konfigurasjon som er delt av flere klienter.
+client-scopes=Klientmaler
+client-scopes.tooltip=Klientmaler tillater deg \u00E5 definere felles konfigurasjon som er delt av flere klienter.
groups=Grupper
@@ -593,7 +593,7 @@ select-client-role=Velg klientrolle
client-template=Klientmal
client-template.tooltip=Klientmal som denne klienten arver konfigurasjonen fra
client-saml-endpoint=Endepunkt for klient-SAML
-add-client-template=Legg til klientmal
+add-client-scope=Legg til klientmal
manage=H\u00E5ndter
authentication=Autentisering
@@ -605,9 +605,9 @@ configure=Konfigurer
select-realm=Velg sikkerhetsdomene
add=Legg til
-client-template.name.tooltip=Navn p\u00E5 klientmal. M\u00E5 v\u00E6re unik i sikkerhetsdomenet.
-client-template.description.tooltip=Beskrivelse av klientmal
-client-template.protocol.tooltip=Hvilken SSO protokoll-konfigurasjon som blir levert av denne klientmalen
+client-scope.name.tooltip=Navn p\u00E5 klientmal. M\u00E5 v\u00E6re unik i sikkerhetsdomenet.
+client-scope.description.tooltip=Beskrivelse av klientmal
+client-scope.protocol.tooltip=Hvilken SSO protokoll-konfigurasjon som blir levert av denne klientmalen
add-user-federation-provider=Legg til leverand\u00F8r for brukerfederering
add-user-storage-provider=Legg til brukerlagringsleverand\u00F8r
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_pt_BR.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_pt_BR.properties
index a9b32b6..0c7300b 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_pt_BR.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_pt_BR.properties
@@ -390,7 +390,7 @@ continue=Continuar
initial-access-token.confirm.title=Copiar o token de acesso inicial
initial-access-token.confirm.text=Por favor copie e cole o token de acesso inicial antes de confirmar pois não é possível recuperá-lo depois
-client-templates=Modelos de cliente
+client-scopes=Modelos de cliente
groups=Grupos
@@ -425,7 +425,7 @@ select-client-role=Selecione um role de cliente
client-template=Modelos de Cliente
client-saml-endpoint=Cliente SAML Endpoint
-add-client-template=Adicionar modelo de cliente
+add-client-scope=Adicionar modelo de cliente
manage=Administração
authentication=Autenticação
@@ -943,7 +943,7 @@ saml.import-from-url.tooltip=Importar metadata de um descritor de entidade IDP S
social.client-id.tooltip=O identificador do cliente registrado com o provedor de identificação.
social.default-scopes.tooltip=Os escopos que serão enviados ao solicitar autorização. Veja a documentação para valores possíveis, separador e valores padrão.
stackoverflow.key.tooltip=A chave de cliente obtida do registro no Stack Overflow.
-client-templates.tooltip=Modelos de cliente permitem que você defina configurações comuns que serão compartilhadas entre múltiplos clientes.
+client-scopes.tooltip=Modelos de cliente permitem que você defina configurações comuns que serão compartilhadas entre múltiplos clientes.
group.add-selected.tooltip=Roles do Realm que serão associadas ao grupo.
group.effective-roles.tooltip=Todos os mapeamentos de roles do Realm. Alguns roles exibidos podem ter sido herdados de um role composto mapeado.
group.available-roles.tooltip=Roles associáveis deste cliente.
@@ -969,10 +969,10 @@ realm-roles.tooltip=Roles do Realm que podem ser selecionados.
authz-policy-role-realm-roles.tooltip=Especifica quais role(s) de *realm* são permitidos por esta política.
client-roles.tooltip=Roles do cliente que podem ser selecionados.
authz-policy-role-client-roles.tooltip=Especifica quais role(s) do *cliente* são permitidos por esta política.
-client-template.tooltip=Modelo de cliente do qual ete cliente herda as configurações.
-client-template.name.tooltip=Nome do modelo de cliente. Deve ser único neste Realm.
-client-template.description.tooltip=Descrição do modelo de cliente.
-client-template.protocol.tooltip=Qual configuração de protocolo SSO será provida por este modelo de cliente.
+client-scope.tooltip=Modelo de cliente do qual ete cliente herda as configurações.
+client-scope.name.tooltip=Nome do modelo de cliente. Deve ser único neste Realm.
+client-scope.description.tooltip=Descrição do modelo de cliente.
+client-scope.protocol.tooltip=Qual configuração de protocolo SSO será provida por este modelo de cliente.
console-display-name.tooltip=Nome de exibição do provedor quando linkado no console de administração.
priority.tooltip=Prioridade do provedor quando da busca de usuários. Valores mais baixos são utilizados primeiro.
periodic-full-sync.tooltip=Habilitar ou não a sincronização completa periódica dos usuários deste provedor.
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties
index 691b2e7..e67ff0a 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_ru.properties
@@ -602,8 +602,8 @@ allowed-client-templates.tooltip=Белый список шаблонов кли
max-clients.label=Максимальное количество клиентов для Realm
max-clients.tooltip=Не позволяет регистрировать клиентов больше установленного предельного значения.
-client-templates=Шаблоны клиентов
-client-templates.tooltip=Шаблоны клиентов позволяют вам определить основную конфигурацию, которая может быть общей между несколькими клиентами
+client-scopes=Шаблоны клиентов
+client-scopes.tooltip=Шаблоны клиентов позволяют вам определить основную конфигурацию, которая может быть общей между несколькими клиентами
groups=Группы
@@ -664,7 +664,7 @@ select-client-role=Выберите роль клиента
client-template=Шаблон клиента
client-template.tooltip=Шаблон клиента, определяющий наследование конфигурации этого клиента из
client-saml-endpoint=Конечная точка доступа SAML клиента
-add-client-template=Добавить шаблон клиента
+add-client-scope=Добавить шаблон клиента
manage=Управление
authentication=Аутентификация
@@ -676,9 +676,9 @@ configure=Конфигурация
select-realm=Выберите realm
add=Добавить
-client-template.name.tooltip=Наименование шаблона клиента. Должно быть уникально для realm
-client-template.description.tooltip=Описание шаблона клиента
-client-template.protocol.tooltip=Какая конфигурация протокола SSO будет поддержана шаблоном клиента
+client-scope.name.tooltip=Наименование шаблона клиента. Должно быть уникально для realm
+client-scope.description.tooltip=Описание шаблона клиента
+client-scope.protocol.tooltip=Какая конфигурация протокола SSO будет поддержана шаблоном клиента
add-user-federation-provider=Добавить службу федерации пользователей
add-user-storage-provider=Добавить службу хранилища пользователей
diff --git a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_zh_CN.properties b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_zh_CN.properties
index dc2addc..3d596b8 100644
--- a/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_zh_CN.properties
+++ b/themes/src/main/resources-community/theme/base/admin/messages/admin-messages_zh_CN.properties
@@ -571,8 +571,8 @@ allowed-client-templates.tooltip =客户端模板的白名单,可以在新注�
max-clients.label =每个领域的最大客户端
max-clients.tooltip =如果域中现有客户端的数量等于或大于配置的限制,将不允许注册新客户端。
-client-templates =客户端模板
-client-templates.tooltip =客户机模板允许您定义在多个客户机之间共享的公共配置
+client-scopes =客户端模板
+client-scopes.tooltip =客户机模板允许您定义在多个客户机之间共享的公共配置
groups =组
@@ -632,7 +632,7 @@ select-client-role =选择客户端角色
client-template =客户端模板
client-template.tooltip =此客户端继承配置的客户端模板
client-saml-endpoint =客户端SAML端点
-add-client-template =添加客户端模板
+add-client-scope =添加客户端模板
manage =管理
authentication =验证
@@ -644,9 +644,9 @@ configure =配置
select-realm =选择领域
add =添加
-client-template.name.tooltip =客户端模板的名称。在领域中必须是唯一的
-client-template.description.tooltip =客户端模板的描述
-client-template.protocol.tooltip =此客户端模板提供的SSO协议配置
+client-scope.name.tooltip =客户端模板的名称。在领域中必须是唯一的
+client-scope.description.tooltip =客户端模板的描述
+client-scope.protocol.tooltip =此客户端模板提供的SSO协议配置
add-user-federation-provider =添加用户联合提供程序
add-user-storage-provider =添加用户存储提供程序