diff --git a/model/api/src/main/java/org/keycloak/models/AdminRoles.java b/model/api/src/main/java/org/keycloak/models/AdminRoles.java index e21a57a318..33e94cdc74 100644 --- a/model/api/src/main/java/org/keycloak/models/AdminRoles.java +++ b/model/api/src/main/java/org/keycloak/models/AdminRoles.java @@ -25,8 +25,4 @@ public class AdminRoles { public static String[] ALL_REALM_ROLES = {VIEW_REALM, VIEW_USERS, VIEW_APPLICATIONS, VIEW_CLIENTS, VIEW_AUDIT, MANAGE_REALM, MANAGE_USERS, MANAGE_APPLICATIONS, MANAGE_CLIENTS, MANAGE_AUDIT}; - public static String getAdminApp(RealmModel realm) { - return realm.getName() + APP_SUFFIX; - } - } diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java index eda8e30d97..14d531ee4d 100755 --- a/model/api/src/main/java/org/keycloak/models/RealmModel.java +++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java @@ -233,4 +233,9 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa Set getAuditListeners(); void setAuditListeners(Set listeners); + + ApplicationModel getAdminApp(); + + void setAdminApp(ApplicationModel app); + } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java index 9881e3c52e..69600014f1 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ApplicationAdapter.java @@ -260,4 +260,8 @@ public class ApplicationAdapter extends ClientAdapter implements ApplicationMode public String toString() { return getName(); } + + ApplicationEntity getJpaEntity() { + return applicationEntity; + } } 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 2833c64601..4cbda3ce8b 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 @@ -1276,4 +1276,16 @@ public class RealmAdapter implements RealmModel { realm.setAuditListeners(listeners); em.flush(); } + + @Override + public ApplicationModel getAdminApp() { + return new ApplicationAdapter(this, em, realm.getAdminApp()); + } + + @Override + public void setAdminApp(ApplicationModel app) { + realm.setAdminApp(((ApplicationAdapter) app).getJpaEntity()); + em.flush(); + } + } 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 556065f41d..904a3e48a1 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 @@ -1,6 +1,8 @@ package org.keycloak.models.jpa.entities; +import org.keycloak.models.ApplicationModel; + import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; @@ -13,6 +15,7 @@ import javax.persistence.MapKeyColumn; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; +import javax.persistence.OneToOne; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -114,6 +117,9 @@ public class RealmEntity { @ElementCollection private Set auditListeners= new HashSet(); + @OneToOne + private ApplicationEntity adminApp; + public String getId() { return id; } @@ -432,5 +438,14 @@ public class RealmEntity { public void setAuditListeners(Set auditListeners) { this.auditListeners = auditListeners; } + + public ApplicationEntity getAdminApp() { + return adminApp; + } + + public void setAdminApp(ApplicationEntity adminApp) { + this.adminApp = adminApp; + } + } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java index d3c2075db6..b5c6ddabdc 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java @@ -207,4 +207,5 @@ public class ApplicationAdapter extends ClientAdapter impleme getMongoEntity().setDefaultRoles(roleNames); updateMongoEntity(); } + } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java index 8c6fb48566..181b8123f3 100644 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientAdapter.java @@ -15,7 +15,7 @@ import org.keycloak.models.mongo.keycloak.entities.ClientEntity; */ public class ClientAdapter extends AbstractMongoAdapter implements ClientModel { - private final T clientEntity; + protected final T clientEntity; private final RealmModel realm; public ClientAdapter(RealmModel realm, T clientEntity, MongoStoreInvocationContext invContext) { @@ -157,4 +157,5 @@ public class ClientAdapter extends AbstractMongoAdapter< clientEntity.setNotBefore(notBefore); updateMongoEntity(); } + } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java index 2052967b53..8da896fa18 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java @@ -642,7 +642,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R .and("name").is(name) .get(); ApplicationEntity appEntity = getMongoStore().loadSingleEntity(ApplicationEntity.class, query, invocationContext); - return appEntity==null ? null : new ApplicationAdapter(this, appEntity, invocationContext); + return appEntity == null ? null : new ApplicationAdapter(this, appEntity, invocationContext); } @Override @@ -697,7 +697,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R @Override public void grantRole(UserModel user, RoleModel role) { - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); getMongoStore().pushItemToList(userEntity, "roleIds", role.getId(), true, invocationContext); } @@ -724,7 +724,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user? Set realmRoles = new HashSet(); for (RoleModel role : allRoles) { - RoleEntity roleEntity = ((RoleAdapter)role).getRole(); + RoleEntity roleEntity = ((RoleAdapter) role).getRole(); if (getId().equals(roleEntity.getRealmId())) { realmRoles.add(role); @@ -737,7 +737,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R public void deleteRoleMapping(UserModel user, RoleModel role) { if (user == null || role == null) return; - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); getMongoStore().pullItemFromList(userEntity, "roleIds", role.getId(), invocationContext); } @@ -764,7 +764,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user? Set realmRoles = new HashSet(); for (RoleModel role : allScopes) { - RoleEntity roleEntity = ((RoleAdapter)role).getRole(); + RoleEntity roleEntity = ((RoleAdapter) role).getRole(); if (getId().equals(roleEntity.getRealmId())) { realmRoles.add(role); @@ -787,12 +787,12 @@ public class RealmAdapter extends AbstractMongoAdapter implements R @Override public void addScopeMapping(ClientModel client, RoleModel role) { - getMongoStore().pushItemToList(((AbstractMongoAdapter)client).getMongoEntity(), "scopeIds", role.getId(), true, invocationContext); + getMongoStore().pushItemToList(((AbstractMongoAdapter) client).getMongoEntity(), "scopeIds", role.getId(), true, invocationContext); } @Override public void deleteScopeMapping(ClientModel client, RoleModel role) { - getMongoStore().pullItemFromList(((AbstractMongoAdapter)client).getMongoEntity(), "scopeIds", role.getId(), invocationContext); + getMongoStore().pullItemFromList(((AbstractMongoAdapter) client).getMongoEntity(), "scopeIds", role.getId(), invocationContext); } @Override @@ -911,7 +911,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R @Override public boolean validatePassword(UserModel user, String password) { - for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) { + for (CredentialEntity cred : ((UserAdapter) user).getUser().getCredentials()) { if (cred.getType().equals(UserCredentialModel.PASSWORD)) { return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue()); } @@ -922,7 +922,7 @@ public class RealmAdapter extends AbstractMongoAdapter implements R @Override public boolean validateTOTP(UserModel user, String password, String token) { if (!validatePassword(user, password)) return false; - for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) { + for (CredentialEntity cred : ((UserAdapter) user).getUser().getCredentials()) { if (cred.getType().equals(UserCredentialModel.TOTP)) { return new TimeBasedOTP().validate(token, cred.getValue().getBytes()); } @@ -967,12 +967,12 @@ public class RealmAdapter extends AbstractMongoAdapter implements R .and("realmId").is(getId()) .get(); UserEntity userEntity = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext); - return userEntity==null ? null : new UserAdapter(userEntity, invocationContext); + return userEntity == null ? null : new UserAdapter(userEntity, invocationContext); } @Override public Set getSocialLinks(UserModel user) { - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); List linkEntities = userEntity.getSocialLinks(); if (linkEntities == null) { @@ -990,12 +990,12 @@ public class RealmAdapter extends AbstractMongoAdapter implements R @Override public SocialLinkModel getSocialLink(UserModel user, String socialProvider) { SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider); - return socialLinkEntity!=null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()) : null; + return socialLinkEntity != null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()) : null; } @Override public void addSocialLink(UserModel user, SocialLinkModel socialLink) { - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); SocialLinkEntity socialLinkEntity = new SocialLinkEntity(); socialLinkEntity.setSocialProvider(socialLink.getSocialProvider()); socialLinkEntity.setSocialUserId(socialLink.getSocialUserId()); @@ -1005,18 +1005,18 @@ public class RealmAdapter extends AbstractMongoAdapter implements R } @Override - public boolean removeSocialLink(UserModel user,String socialProvider) { + public boolean removeSocialLink(UserModel user, String socialProvider) { SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider); if (socialLinkEntity == null) { return false; } - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); return getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext); } private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) { - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); List linkEntities = userEntity.getSocialLinks(); if (linkEntities == null) { return null; @@ -1032,19 +1032,19 @@ public class RealmAdapter extends AbstractMongoAdapter implements R @Override public AuthenticationLinkModel getAuthenticationLink(UserModel user) { - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); AuthenticationLinkEntity authLinkEntity = userEntity.getAuthenticationLink(); if (authLinkEntity == null) { return null; - } else { + } else { return new AuthenticationLinkModel(authLinkEntity.getAuthProvider(), authLinkEntity.getAuthUserId()); } } @Override public void setAuthenticationLink(UserModel user, AuthenticationLinkModel authenticationLink) { - UserEntity userEntity = ((UserAdapter)user).getUser(); + UserEntity userEntity = ((UserAdapter) user).getUser(); AuthenticationLinkEntity authLinkEntity = new AuthenticationLinkEntity(); authLinkEntity.setAuthProvider(authenticationLink.getAuthProvider()); authLinkEntity.setAuthUserId(authenticationLink.getAuthUserId()); @@ -1086,8 +1086,8 @@ public class RealmAdapter extends AbstractMongoAdapter implements R if (spaceInd != -1) { String firstName = search.substring(0, spaceInd); String lastName = search.substring(spaceInd + 1); - Pattern firstNamePattern = Pattern.compile("(?i:" + firstName + "$)"); - Pattern lastNamePattern = Pattern.compile("(?i:^" + lastName + ")"); + Pattern firstNamePattern = Pattern.compile("(?i:" + firstName + "$)"); + Pattern lastNamePattern = Pattern.compile("(?i:^" + lastName + ")"); nameBuilder = new QueryBuilder().and( new QueryBuilder().put("firstName").regex(firstNamePattern).get(), new QueryBuilder().put("lastName").regex(lastNamePattern).get() @@ -1240,6 +1240,16 @@ public class RealmAdapter extends AbstractMongoAdapter implements R updateRealm(); } + @Override + public ApplicationModel getAdminApp() { + return new ApplicationAdapter(this, realm.getAdminApp(), invocationContext); + } + + @Override + public void setAdminApp(ApplicationModel app) { + realm.setAdminApp(((ApplicationAdapter) app).getMongoEntity()); + } + @Override public RealmEntity getMongoEntity() { return realm; diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java index fc0ee56a82..0fed4a7945 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RealmEntity.java @@ -2,6 +2,7 @@ package org.keycloak.models.mongo.keycloak.entities; import com.mongodb.DBObject; import com.mongodb.QueryBuilder; +import org.keycloak.models.ApplicationModel; import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity; import org.keycloak.models.mongo.api.MongoCollection; import org.keycloak.models.mongo.api.MongoEntity; @@ -69,6 +70,8 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong private long auditExpiration; private List auditListeners = new ArrayList(); + private ApplicationEntity adminApp; + @MongoField public String getName() { return name; @@ -393,6 +396,15 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong this.auditListeners = auditListeners; } + @MongoField + public ApplicationEntity getAdminApp() { + return adminApp; + } + + public void setAdminApp(ApplicationEntity adminApp) { + this.adminApp = adminApp; + } + @Override public void afterRemove(MongoStoreInvocationContext context) { DBObject query = new QueryBuilder() diff --git a/services/src/main/java/org/keycloak/services/managers/Auth.java b/services/src/main/java/org/keycloak/services/managers/Auth.java index 0718a61205..1966f437ab 100755 --- a/services/src/main/java/org/keycloak/services/managers/Auth.java +++ b/services/src/main/java/org/keycloak/services/managers/Auth.java @@ -1,5 +1,6 @@ package org.keycloak.services.managers; +import org.keycloak.models.ApplicationModel; import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; @@ -72,16 +73,16 @@ public class Auth { return false; } - public boolean hasAppRole(String app, String role) { + public boolean hasAppRole(ApplicationModel app, String role) { if (cookie) { - return realm.hasRole(user, realm.getApplicationByName(app).getRole(role)); + return realm.hasRole(user, app.getRole(role)); } else { - AccessToken.Access access = token.getResourceAccess(app); + AccessToken.Access access = token.getResourceAccess(app.getName()); return access != null && access.isUserInRole(role); } } - public boolean hasOneOfAppRole(String app, String... roles) { + public boolean hasOneOfAppRole(ApplicationModel app, String... roles) { for (String r : roles) { if (hasAppRole(app, r)) { return true; 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 f6e9bebf8e..14c22d3c2d 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -108,7 +108,7 @@ public class RealmManager { RealmModel adminRealm = getKeycloakAdminstrationRealm(); RoleModel adminRole = adminRealm.getRole(AdminRoles.ADMIN); - ApplicationModel realmAdminApp = adminRealm.getApplicationByName(AdminRoles.getAdminApp(realm)); + ApplicationModel realmAdminApp = realm.getAdminApp(); for (RoleModel r : realmAdminApp.getRoles()) { adminRole.removeCompositeRole(r); } @@ -214,7 +214,9 @@ public class RealmManager { } ApplicationManager applicationManager = new ApplicationManager(new RealmManager(identitySession)); - ApplicationModel realmAdminApp = applicationManager.createApplication(adminRealm, AdminRoles.getAdminApp(realm)); + + ApplicationModel realmAdminApp = applicationManager.createApplication(adminRealm, realm.getName() + "-realm"); + realm.setAdminApp(realmAdminApp); for (String r : AdminRoles.ALL_REALM_ROLES) { RoleModel role = realmAdminApp.addRole(r); diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java index 4d44d8587d..28f3df0372 100755 --- a/services/src/main/java/org/keycloak/services/resources/AccountService.java +++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java @@ -539,7 +539,7 @@ public class AccountService { throw new ForbiddenException(); } - if (!auth.hasAppRole(application.getName(), role)) { + if (!auth.hasAppRole(application, role)) { throw new ForbiddenException(); } } @@ -549,7 +549,7 @@ public class AccountService { throw new ForbiddenException(); } - if (!auth.hasOneOfAppRole(application.getName(), roles)) { + if (!auth.hasOneOfAppRole(application, roles)) { throw new ForbiddenException(); } } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java index e6ea9a1625..fc0d174cd6 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAuth.java @@ -1,6 +1,7 @@ package org.keycloak.services.resources.admin; import org.keycloak.models.AdminRoles; +import org.keycloak.models.ApplicationModel; import org.keycloak.services.ForbiddenException; import org.keycloak.services.managers.Auth; @@ -19,9 +20,9 @@ public class RealmAuth { } private Auth auth; - private String realmAdminApp; + private ApplicationModel realmAdminApp; - public RealmAuth(Auth auth, String realmAdminApp) { + public RealmAuth(Auth auth, ApplicationModel realmAdminApp) { this.auth = auth; this.realmAdminApp = realmAdminApp; } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java index a20d149603..33cd49b39b 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java @@ -69,11 +69,9 @@ public class RealmsAdminResource { List realms = session.getRealms(); List reps = new ArrayList(); for (RealmModel realm : realms) { - String realmAdminApp = AdminRoles.getAdminApp(realm); - - if (auth.hasAppRole(realmAdminApp, AdminRoles.MANAGE_REALM)) { + if (auth.hasAppRole(realm.getAdminApp(), AdminRoles.MANAGE_REALM)) { reps.add(ModelToRepresentation.toRepresentation(realm)); - } else if (auth.hasOneOfAppRole(realmAdminApp, AdminRoles.ALL_REALM_ROLES)) { + } else if (auth.hasOneOfAppRole(realm.getAdminApp(), AdminRoles.ALL_REALM_ROLES)) { RealmRepresentation rep = new RealmRepresentation(); rep.setRealm(realm.getName()); reps.add(rep); @@ -144,7 +142,7 @@ public class RealmsAdminResource { } RealmModel adminRealm = new RealmManager(session).getKeycloakAdminstrationRealm(); - ApplicationModel realmAdminApp = adminRealm.getApplicationByName(AdminRoles.getAdminApp(realm)); + ApplicationModel realmAdminApp = realm.getAdminApp(); for (String r : AdminRoles.ALL_REALM_ROLES) { RoleModel role = realmAdminApp.getRole(r); adminRealm.grantRole(auth.getUser(), role); @@ -159,7 +157,7 @@ public class RealmsAdminResource { RealmModel realm = realmManager.getRealmByName(name); if (realm == null) throw new NotFoundException("{realm} = " + name); - RealmAuth realmAuth = new RealmAuth(auth, AdminRoles.getAdminApp(realm)); + RealmAuth realmAuth = new RealmAuth(auth, realm.getAdminApp()); RealmAdminResource adminResource = new RealmAdminResource(realmAuth, realm, tokenManager); ResteasyProviderFactory.getInstance().injectProperties(adminResource);