KEYCLOAK-2846 export/import of clientTemplate scopes
This commit is contained in:
parent
919a3791ea
commit
afc8179cf8
9 changed files with 163 additions and 23 deletions
|
@ -264,13 +264,22 @@ public class RealmRepresentation {
|
|||
return scopeMappings;
|
||||
}
|
||||
|
||||
public ScopeMappingRepresentation scopeMapping(String username) {
|
||||
public ScopeMappingRepresentation clientScopeMapping(String clientName) {
|
||||
ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
|
||||
mapping.setClient(username);
|
||||
mapping.setClient(clientName);
|
||||
if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
|
||||
scopeMappings.add(mapping);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
public ScopeMappingRepresentation clientTemplateScopeMapping(String clientTemplateName) {
|
||||
ScopeMappingRepresentation mapping = new ScopeMappingRepresentation();
|
||||
mapping.setClientTemplate(clientTemplateName);
|
||||
if (scopeMappings == null) scopeMappings = new ArrayList<ScopeMappingRepresentation>();
|
||||
scopeMappings.add(mapping);
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Set<String> getRequiredCredentials() {
|
||||
return requiredCredentials;
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||
public class ScopeMappingRepresentation {
|
||||
protected String self; // link
|
||||
protected String client;
|
||||
protected String clientTemplate;
|
||||
protected Set<String> roles;
|
||||
|
||||
public String getSelf() {
|
||||
|
@ -45,6 +46,14 @@ public class ScopeMappingRepresentation {
|
|||
this.client = client;
|
||||
}
|
||||
|
||||
public String getClientTemplate() {
|
||||
return clientTemplate;
|
||||
}
|
||||
|
||||
public void setClientTemplate(String clientTemplate) {
|
||||
this.clientTemplate = clientTemplate;
|
||||
}
|
||||
|
||||
public Set<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
|
|
@ -632,5 +632,15 @@ public final class KeycloakModelUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static ClientTemplateModel getClientTemplateByName(RealmModel realm, String templateName) {
|
||||
for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) {
|
||||
if (templateName.equals(clientTemplate.getName())) {
|
||||
return clientTemplate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.keycloak.models.ProtocolMapperModel;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredActionProviderModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.ScopeContainerModel;
|
||||
import org.keycloak.models.UserConsentModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserCredentialValueModel;
|
||||
|
@ -250,16 +251,14 @@ public class RepresentationToModel {
|
|||
|
||||
if (rep.getScopeMappings() != null) {
|
||||
for (ScopeMappingRepresentation scope : rep.getScopeMappings()) {
|
||||
ClientModel client = newRealm.getClientByClientId(scope.getClient());
|
||||
if (client == null) {
|
||||
throw new RuntimeException("Unknown client specification in realm scope mappings");
|
||||
}
|
||||
ScopeContainerModel scopeContainer = getScopeContainerHavingScope(newRealm, scope);
|
||||
|
||||
for (String roleString : scope.getRoles()) {
|
||||
RoleModel role = newRealm.getRole(roleString.trim());
|
||||
if (role == null) {
|
||||
role = newRealm.addRole(roleString.trim());
|
||||
}
|
||||
client.addScopeMapping(role);
|
||||
scopeContainer.addScopeMapping(role);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1205,20 +1204,36 @@ public class RepresentationToModel {
|
|||
|
||||
public static void createClientScopeMappings(RealmModel realm, ClientModel clientModel, List<ScopeMappingRepresentation> mappings) {
|
||||
for (ScopeMappingRepresentation mapping : mappings) {
|
||||
ClientModel client = realm.getClientByClientId(mapping.getClient());
|
||||
if (client == null) {
|
||||
throw new RuntimeException("Unknown client specified in client scope mappings");
|
||||
}
|
||||
ScopeContainerModel scopeContainer = getScopeContainerHavingScope(realm, mapping);
|
||||
|
||||
for (String roleString : mapping.getRoles()) {
|
||||
RoleModel role = clientModel.getRole(roleString.trim());
|
||||
if (role == null) {
|
||||
role = clientModel.addRole(roleString.trim());
|
||||
}
|
||||
client.addScopeMapping(role);
|
||||
scopeContainer.addScopeMapping(role);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ScopeContainerModel getScopeContainerHavingScope(RealmModel realm, ScopeMappingRepresentation scope) {
|
||||
if (scope.getClient() != null) {
|
||||
ClientModel client = realm.getClientByClientId(scope.getClient());
|
||||
if (client == null) {
|
||||
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());
|
||||
if (clientTemplate == null) {
|
||||
throw new RuntimeException("Unknown clientTemplate specification in scope mappings: " + scope.getClientTemplate());
|
||||
}
|
||||
return clientTemplate;
|
||||
} else {
|
||||
throw new RuntimeException("Either client or clientTemplate needs to be specified in scope mappings");
|
||||
}
|
||||
}
|
||||
|
||||
// Users
|
||||
|
||||
public static UserModel createUser(KeycloakSession session, RealmModel newRealm, UserRepresentation userRep) {
|
||||
|
|
|
@ -88,13 +88,14 @@ public class ExportUtils {
|
|||
List<ClientModel> allClients = new ArrayList<>(clients);
|
||||
Map<String, List<ScopeMappingRepresentation>> clientScopeReps = new HashMap<>();
|
||||
|
||||
// Scopes of clients
|
||||
for (ClientModel client : allClients) {
|
||||
Set<RoleModel> clientScopes = client.getScopeMappings();
|
||||
ScopeMappingRepresentation scopeMappingRep = null;
|
||||
for (RoleModel scope : clientScopes) {
|
||||
if (scope.getContainer() instanceof RealmModel) {
|
||||
if (scopeMappingRep == null) {
|
||||
scopeMappingRep = rep.scopeMapping(client.getClientId());
|
||||
scopeMappingRep = rep.clientScopeMapping(client.getClientId());
|
||||
}
|
||||
scopeMappingRep.role(scope.getName());
|
||||
} else {
|
||||
|
@ -108,7 +109,7 @@ public class ExportUtils {
|
|||
|
||||
ScopeMappingRepresentation currentClientScope = null;
|
||||
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
|
||||
if (scopeMapping.getClient().equals(client.getClientId())) {
|
||||
if (client.getClientId().equals(scopeMapping.getClient())) {
|
||||
currentClientScope = scopeMapping;
|
||||
break;
|
||||
}
|
||||
|
@ -123,6 +124,42 @@ public class ExportUtils {
|
|||
}
|
||||
}
|
||||
|
||||
// Scopes of client templates
|
||||
for (ClientTemplateModel clientTemplate : realm.getClientTemplates()) {
|
||||
Set<RoleModel> clientScopes = clientTemplate.getScopeMappings();
|
||||
ScopeMappingRepresentation scopeMappingRep = null;
|
||||
for (RoleModel scope : clientScopes) {
|
||||
if (scope.getContainer() instanceof RealmModel) {
|
||||
if (scopeMappingRep == null) {
|
||||
scopeMappingRep = rep.clientTemplateScopeMapping(clientTemplate.getName());
|
||||
}
|
||||
scopeMappingRep.role(scope.getName());
|
||||
} else {
|
||||
ClientModel app = (ClientModel)scope.getContainer();
|
||||
String appName = app.getClientId();
|
||||
List<ScopeMappingRepresentation> currentAppScopes = clientScopeReps.get(appName);
|
||||
if (currentAppScopes == null) {
|
||||
currentAppScopes = new ArrayList<>();
|
||||
clientScopeReps.put(appName, currentAppScopes);
|
||||
}
|
||||
|
||||
ScopeMappingRepresentation currentClientTemplateScope = null;
|
||||
for (ScopeMappingRepresentation scopeMapping : currentAppScopes) {
|
||||
if (clientTemplate.getName().equals(scopeMapping.getClientTemplate())) {
|
||||
currentClientTemplateScope = scopeMapping;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currentClientTemplateScope == null) {
|
||||
currentClientTemplateScope = new ScopeMappingRepresentation();
|
||||
currentClientTemplateScope.setClientTemplate(clientTemplate.getName());
|
||||
currentAppScopes.add(currentClientTemplateScope);
|
||||
}
|
||||
currentClientTemplateScope.role(scope.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clientScopeReps.size() > 0) {
|
||||
rep.setClientScopeMappings(clientScopeReps);
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ public class ClientTemplateResource {
|
|||
@NoCache
|
||||
public Response deleteClientTemplate() {
|
||||
auth.requireManage();
|
||||
|
||||
|
||||
try {
|
||||
realm.removeClientTemplate(template.getId());
|
||||
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
|
||||
|
|
|
@ -58,7 +58,7 @@ public class CompositeRolesModelTest extends AbstractModelTest {
|
|||
RealmRepresentation rep = AbstractModelTest.loadJson("model/testrealm-noclient-id.json");
|
||||
rep.setId("TestNoClientID");
|
||||
expectedException.expect(RuntimeException.class);
|
||||
expectedException.expectMessage("Unknown client specified in client scope mappings");
|
||||
expectedException.expectMessage("Unknown client specification in scope mappings: some-client");
|
||||
manager.importRealm(rep);
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapper;
|
|||
import org.keycloak.federation.ldap.mappers.FullNameLDAPFederationMapperFactory;
|
||||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
@ -321,13 +322,32 @@ public class ImportTest extends AbstractModelTest {
|
|||
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);
|
||||
Assert.assertEquals(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, gssCredentialMapper.getName());
|
||||
Assert.assertEquals( OIDCLoginProtocol.LOGIN_PROTOCOL, gssCredentialMapper.getProtocol());
|
||||
Assert.assertEquals(UserSessionNoteMapper.PROVIDER_ID, gssCredentialMapper.getProtocolMapper());
|
||||
String includeInAccessToken = gssCredentialMapper.getConfig().get(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
|
||||
String includeInIdToken = gssCredentialMapper.getConfig().get(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
|
||||
Assert.assertTrue(includeInAccessToken.equalsIgnoreCase("true"));
|
||||
Assert.assertTrue(includeInIdToken == null || Boolean.parseBoolean(includeInIdToken) == false);
|
||||
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);
|
||||
|
@ -380,4 +400,14 @@ public class ImportTest extends AbstractModelTest {
|
|||
Assert.assertEquals(expectedType, requiredCreds.get(0).getType());
|
||||
}
|
||||
|
||||
private static void assertGssProtocolMapper(ProtocolMapperModel gssCredentialMapper) {
|
||||
Assert.assertEquals(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, gssCredentialMapper.getName());
|
||||
Assert.assertEquals( OIDCLoginProtocol.LOGIN_PROTOCOL, gssCredentialMapper.getProtocol());
|
||||
Assert.assertEquals(UserSessionNoteMapper.PROVIDER_ID, gssCredentialMapper.getProtocolMapper());
|
||||
String includeInAccessToken = gssCredentialMapper.getConfig().get(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN);
|
||||
String includeInIdToken = gssCredentialMapper.getConfig().get(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN);
|
||||
Assert.assertTrue(includeInAccessToken.equalsIgnoreCase("true"));
|
||||
Assert.assertTrue(includeInIdToken == null || Boolean.parseBoolean(includeInIdToken) == false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -195,6 +195,28 @@
|
|||
"secret": "clientpassword"
|
||||
}
|
||||
],
|
||||
"clientTemplates" : [
|
||||
{
|
||||
"name" : "foo-template",
|
||||
"description" : "foo-template-desc",
|
||||
"protocol" : "openid-connect",
|
||||
"protocolMappers" : [
|
||||
{
|
||||
"name" : "gss delegation credential",
|
||||
"protocol" : "openid-connect",
|
||||
"protocolMapper" : "oidc-usersessionmodel-note-mapper",
|
||||
"consentRequired" : true,
|
||||
"consentText" : "gss delegation credential",
|
||||
"config" : {
|
||||
"user.session.note" : "gss_delegation_credential",
|
||||
"access.token.claim" : "true",
|
||||
"claim.name" : "gss_delegation_credential",
|
||||
"Claim JSON Type" : "String"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
"realm" : [
|
||||
{
|
||||
|
@ -226,6 +248,10 @@
|
|||
{
|
||||
"client": "oauthclient",
|
||||
"roles": ["admin"]
|
||||
},
|
||||
{
|
||||
"clientTemplate": "foo-template",
|
||||
"roles": ["admin"]
|
||||
}
|
||||
],
|
||||
"applicationScopeMappings": {
|
||||
|
@ -233,6 +259,10 @@
|
|||
{
|
||||
"client": "oauthclient",
|
||||
"roles": ["app-user"]
|
||||
},
|
||||
{
|
||||
"clientTemplate": "foo-template",
|
||||
"roles": ["app-user", "app-admin" ]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in a new issue