KEYCLOAK-14846 Default roles processing

This commit is contained in:
vramik 2020-10-27 13:17:26 +01:00 committed by Hynek Mlnařík
parent 2aaceeab7e
commit 1402d021de
81 changed files with 894 additions and 563 deletions

View file

@ -40,6 +40,7 @@ public class ClientRepresentation {
protected String clientAuthenticatorType;
protected String secret;
protected String registrationAccessToken;
@Deprecated
protected String[] defaultRoles;
protected List<String> redirectUris;
protected List<String> webOrigins;
@ -200,10 +201,12 @@ public class ClientRepresentation {
this.webOrigins = webOrigins;
}
@Deprecated
public String[] getDefaultRoles() {
return defaultRoles;
}
@Deprecated
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}

View file

@ -98,7 +98,9 @@ public class RealmRepresentation {
protected String codeSecret;
protected RolesRepresentation roles;
protected List<GroupRepresentation> groups;
@Deprecated
protected List<String> defaultRoles;
protected RoleRepresentation defaultRole;
protected List<String> defaultGroups;
@Deprecated
protected Set<String> requiredCredentials;
@ -482,14 +484,24 @@ public class RealmRepresentation {
this.actionTokenGeneratedByUserLifespan = actionTokenGeneratedByUserLifespan;
}
@Deprecated
public List<String> getDefaultRoles() {
return defaultRoles;
}
@Deprecated
public void setDefaultRoles(List<String> defaultRoles) {
this.defaultRoles = defaultRoles;
}
public RoleRepresentation getDefaultRole() {
return defaultRole;
}
public void setDefaultRole(RoleRepresentation defaultRole) {
this.defaultRole = defaultRole;
}
public List<String> getDefaultGroups() {
return defaultGroups;
}

View file

@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
@ -158,4 +159,22 @@ public class RoleRepresentation {
attributes.put(name, Arrays.asList(value));
return this;
}
@Override
public int hashCode() {
int hash = 7;
hash = 29 * hash + Objects.hashCode(this.id);
hash = 29 * hash + Objects.hashCode(this.name);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || (!(obj instanceof RoleRepresentation))) {
return false;
}
final RoleRepresentation other = (RoleRepresentation) obj;
return Objects.equals(this.id, other.id) && Objects.equals(this.name, other.name);
}
}

View file

@ -52,7 +52,6 @@ import org.keycloak.models.UserManager;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.credential.PasswordCredentialModel;
import org.keycloak.models.utils.DefaultRoles;
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
import org.keycloak.policy.PasswordPolicyManagerProvider;
import org.keycloak.policy.PolicyError;
@ -291,7 +290,7 @@ public class LDAPStorageProvider implements UserStorageProvider,
// Add the user to the default groups and add default required actions
UserModel proxy = proxy(realm, user, ldapUser, true);
DefaultRoles.addDefaultRoles(realm, proxy);
proxy.grantRole(realm.getDefaultRole());
realm.getDefaultGroupsStream().forEach(proxy::joinGroup);

View file

@ -483,28 +483,28 @@ public class ClientAdapter implements ClientModel, CachedObject {
}
@Override
@Deprecated
public Stream<String> getDefaultRolesStream() {
if (isUpdated()) return updated.getDefaultRolesStream();
return cached.getDefaultRoles().stream();
return getRealm().getDefaultRole().getCompositesStream().filter(this::isClientRole).map(RoleModel::getName);
}
private boolean isClientRole(RoleModel role) {
return role.isClientRole() && Objects.equals(role.getContainerId(), this.getId());
}
@Override
@Deprecated
public void addDefaultRole(String name) {
getDelegateForUpdate();
updated.addDefaultRole(name);
}
@Override
public void updateDefaultRoles(String... defaultRoles) {
getDelegateForUpdate();
updated.updateDefaultRoles(defaultRoles);
}
@Override
@Deprecated
public void removeDefaultRoles(String... defaultRoles) {
getDelegateForUpdate();
updated.removeDefaultRoles(defaultRoles);
}
@Override

View file

@ -725,30 +725,37 @@ public class RealmAdapter implements CachedRealmModel {
}
@Override
@Deprecated
public Stream<String> getDefaultRolesStream() {
if (isUpdated()) return updated.getDefaultRolesStream();
return cached.getDefaultRoles().stream();
return getDefaultRole().getCompositesStream().filter(this::isRealmRole).map(RoleModel::getName);
}
private boolean isRealmRole(RoleModel role) {
return ! role.isClientRole();
}
@Override
@Deprecated
public void addDefaultRole(String name) {
getDelegateForUpdate();
updated.addDefaultRole(name);
}
@Override
public void updateDefaultRoles(String... defaultRoles) {
getDelegateForUpdate();
updated.updateDefaultRoles(defaultRoles);
}
@Override
@Deprecated
public void removeDefaultRoles(String... defaultRoles) {
getDelegateForUpdate();
updated.removeDefaultRoles(defaultRoles);
}
@Override
public void addToDefaultRoles(RoleModel role) {
getDelegateForUpdate();
updated.addToDefaultRoles(role);
}
@Override
public Stream<ClientModel> getClientsStream() {
return cacheSession.getClientsStream(this);
@ -1008,6 +1015,17 @@ public class RealmAdapter implements CachedRealmModel {
updated.setMasterAdminClient(client);
}
@Override
public void setDefaultRole(RoleModel role) {
getDelegateForUpdate();
updated.setDefaultRole(role);
}
@Override
public RoleModel getDefaultRole() {
return cached.getDefaultRoleId() == null ? null : cacheSession.getRoleById(this, cached.getDefaultRoleId());
}
@Override
public RoleModel getRole(String name) {
return cacheSession.getRealmRole(this, name);

View file

@ -61,7 +61,6 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
protected String managementUrl;
protected String rootUrl;
protected String baseUrl;
protected List<String> defaultRoles = new LinkedList<>();
protected boolean bearerOnly;
protected boolean consentRequired;
protected boolean standardFlowEnabled;
@ -99,7 +98,6 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
managementUrl = model.getManagementUrl();
rootUrl = model.getRootUrl();
baseUrl = model.getBaseUrl();
defaultRoles.addAll(model.getDefaultRolesStream().collect(Collectors.toList()));
bearerOnly = model.isBearerOnly();
consentRequired = model.isConsentRequired();
standardFlowEnabled = model.isStandardFlowEnabled();
@ -212,10 +210,6 @@ public class CachedClient extends AbstractRevisioned implements InRealm {
return baseUrl;
}
public List<String> getDefaultRoles() {
return defaultRoles;
}
public boolean isBearerOnly() {
return bearerOnly;
}

View file

@ -142,7 +142,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
protected boolean adminEventsEnabled;
protected Set<String> adminEnabledEventOperations = new HashSet<>();
protected boolean adminEventsDetailsEnabled;
protected List<String> defaultRoles;
protected String defaultRoleId;
private boolean allowUserManagedAccess;
public Set<IdentityProviderMapperModel> getIdentityProviderMapperSet() {
@ -251,7 +251,7 @@ public class CachedRealm extends AbstractExtendableRevisioned {
adminEventsEnabled = model.isAdminEventsEnabled();
adminEventsDetailsEnabled = model.isAdminEventsDetailsEnabled();
defaultRoles = model.getDefaultRolesStream().collect(Collectors.toList());
defaultRoleId = model.getDefaultRole().getId();
ClientModel masterAdminClient = model.getMasterAdminClient();
this.masterAdminClient = (masterAdminClient != null) ? masterAdminClient.getId() : null;
@ -318,6 +318,10 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return masterAdminClient;
}
public String getDefaultRoleId() {
return defaultRoleId;
}
public String getName() {
return name;
}
@ -330,10 +334,6 @@ public class CachedRealm extends AbstractExtendableRevisioned {
return displayNameHtml;
}
public List<String> getDefaultRoles() {
return defaultRoles;
}
public boolean isEnabled() {
return enabled;
}

View file

@ -0,0 +1,132 @@
/*
* Copyright 2020 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.connections.jpa.updater.liquibase.custom;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import liquibase.exception.CustomChangeException;
import liquibase.statement.core.InsertStatement;
import liquibase.statement.core.RawSqlStatement;
import liquibase.statement.core.UpdateStatement;
import liquibase.structure.core.Table;
import org.keycloak.models.Constants;
public class JpaUpdate13_0_0_MigrateDefaultRoles extends CustomKeycloakTask {
private final Set<String> realmIds = new HashSet<>();
@Override
protected void generateStatementsImpl() throws CustomChangeException {
extractRealmIds("SELECT ID FROM " + getTableName("REALM"));
String clientTable = getTableName("CLIENT");
String clientDefaultRolesTable = getTableName("CLIENT_DEFAULT_ROLES");
String compositeRoleTable = getTableName("COMPOSITE_ROLE");
for (String realmId : realmIds) {
String id = UUID.randomUUID().toString();
String roleName = determineDefaultRoleName(realmId);
statements.add(
// create new default role
new InsertStatement(null, null, database.correctObjectName("KEYCLOAK_ROLE", Table.class))
.addColumnValue("ID", id)
.addColumnValue("CLIENT_REALM_CONSTRAINT", realmId)
.addColumnValue("CLIENT_ROLE", Boolean.FALSE)
.addColumnValue("DESCRIPTION", "${role_" + roleName + "}")
.addColumnValue("NAME", roleName)
.addColumnValue("REALM_ID", realmId)
.addColumnValue("REALM", realmId)
);
statements.add(
// assign the role to the realm
new UpdateStatement(null, null, database.correctObjectName("REALM", Table.class))
.addNewColumnValue("DEFAULT_ROLE", id)
.setWhereClause("REALM.ID = '" + realmId + "'")
);
statements.add(
// copy data from REALM_DEFAULT_ROLES to COMPOSITE_ROLE
new RawSqlStatement("INSERT INTO " + compositeRoleTable + " (COMPOSITE, CHILD_ROLE) " +
"SELECT '" + id + "', ROLE_ID FROM " + getTableName("REALM_DEFAULT_ROLES") +
" WHERE REALM_ID = '" + realmId + "'")
);
statements.add(
// copy data from CLIENT_DEFAULT_ROLES to COMPOSITE_ROLE
new RawSqlStatement("INSERT INTO " + compositeRoleTable + " (COMPOSITE, CHILD_ROLE) " +
"SELECT '" + id + "', " + clientDefaultRolesTable + ".ROLE_ID FROM " +
clientDefaultRolesTable + " INNER JOIN " + clientTable + " ON " +
clientTable + ".ID = " + clientDefaultRolesTable + ".CLIENT_ID AND " +
clientTable + ".REALM_ID = '" + realmId + "'")
);
}
}
private void extractRealmIds(String sql) throws CustomChangeException {
try (PreparedStatement statement = jdbcConnection.prepareStatement(sql);
ResultSet rs = statement.executeQuery()) {
while (rs.next()) {
String realmId = rs.getString(1);
if (realmId == null || realmId.trim().isEmpty()) {
continue;
}
realmIds.add(realmId);
}
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when extracting data from previous version", e);
}
}
private String determineDefaultRoleName(String realmId) throws CustomChangeException {
String roleName = Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realmId.toLowerCase();
if (isRoleNameAvailable(realmId, roleName)) {
return roleName;
} else {
for (int i = 1; i < Integer.MAX_VALUE; i++) {
roleName = Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realmId.toLowerCase() + "-" + i;
if (isRoleNameAvailable(realmId, roleName)) return roleName;
}
}
throw new CustomChangeException(getTaskId() + ": Exception when extracting data from previous version. Unable to determine default role name.");
}
private boolean isRoleNameAvailable(String realmId, String roleName) throws CustomChangeException {
try (PreparedStatement statement = jdbcConnection.prepareStatement("SELECT ID FROM " + getTableName("KEYCLOAK_ROLE") +
" WHERE REALM_ID=? AND NAME=?")) {
statement.setString(1, realmId);
statement.setString(2, roleName);
try (ResultSet rs = statement.executeQuery()) {
return ! rs.next(); //name is available
}
} catch (Exception e) {
throw new CustomChangeException(getTaskId() + ": Exception when extracting data from previous version", e);
}
}
@Override
protected String getTaskId() {
return "Migrate Default roles (13.0.0)";
}
}

View file

@ -36,7 +36,6 @@ import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -44,7 +43,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -681,50 +679,35 @@ public class ClientAdapter implements ClientModel, JpaModel<ClientEntity> {
}
@Override
@Deprecated
public Stream<String> getDefaultRolesStream() {
return entity.getDefaultRolesIds().stream().map(this::getRoleNameById);
return realm.getDefaultRole().getCompositesStream().filter(this::isClientRole).map(RoleModel::getName);
}
private String getRoleNameById(String id) {
RoleModel roleById = session.roles().getRoleById(realm, id);
if (roleById == null) {
return null;
}
return roleById.getName();
private boolean isClientRole(RoleModel role) {
return role.isClientRole() && Objects.equals(role.getContainerId(), this.getId());
}
@Override
@Deprecated
public void addDefaultRole(String name) {
if (entity.getDefaultRolesIds().add(getOrAddRoleId(name))) {
em.flush();
}
realm.getDefaultRole().addCompositeRole(getOrAddRoleId(name));
}
private String getOrAddRoleId(String name) {
private RoleModel getOrAddRoleId(String name) {
RoleModel role = getRole(name);
if (role == null) {
role = addRole(name);
}
return role.getId();
}
@Override
public void updateDefaultRoles(String... defaultRoles) {
Set<String> newDefaultRolesIds = Arrays.stream(defaultRoles)
.map(this::getOrAddRoleId)
.collect(Collectors.toSet());
entity.getDefaultRolesIds().retainAll(newDefaultRolesIds);
entity.getDefaultRolesIds().addAll(newDefaultRolesIds);
em.flush();
return role;
}
@Override
@Deprecated
public void removeDefaultRoles(String... defaultRoles) {
Arrays.stream(defaultRoles)
.map(this::getRole)
.filter(Objects::nonNull)
.forEach(role -> entity.getDefaultRolesIds().remove(role.getId()));
em.flush();
for (String defaultRole : defaultRoles) {
realm.getDefaultRole().removeCompositeRole(getRole(defaultRole));
}
}
@Override

View file

@ -337,10 +337,6 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, GroupPro
throw new IllegalStateException("RoleModel's container isn not instance of either RealmModel or ClientModel");
}
session.users().preRemove(realm, role);
RoleContainerModel container = role.getContainer();
if (container.getDefaultRolesStream().anyMatch(r -> Objects.equals(r, role.getName()))) {
container.removeDefaultRoles(role.getName());
}
RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
if (roleEntity == null || !roleEntity.getRealmId().equals(realm.getId())) {
// Throw model exception to ensure transaction rollback and revert previous operations (removing default roles) as well

View file

@ -43,7 +43,6 @@ import org.keycloak.models.jpa.entities.UserConsentClientScopeEntity;
import org.keycloak.models.jpa.entities.UserConsentEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import org.keycloak.models.jpa.entities.UserGroupMembershipEntity;
import org.keycloak.models.utils.DefaultRoles;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
@ -126,7 +125,7 @@ public class JpaUserProvider implements UserProvider.Streams, UserCredentialStor
UserAdapter userModel = new UserAdapter(session, realm, em, entity);
if (addDefaultRoles) {
DefaultRoles.addDefaultRoles(realm, userModel);
userModel.grantRole(realm.getDefaultRole());
// No need to check if user has group as it's new user
realm.getDefaultGroupsStream().forEach(userModel::joinGroupImpl);

View file

@ -33,7 +33,6 @@ import javax.persistence.LockModeType;
import javax.persistence.TypedQuery;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.Objects.nonNull;
@ -707,47 +706,35 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
}
@Override
@Deprecated
public Stream<String> getDefaultRolesStream() {
return realm.getDefaultRolesIds().stream().map(this::getRoleNameById);
return getDefaultRole().getCompositesStream().filter(this::isRealmRole).map(RoleModel::getName);
}
private String getRoleNameById(String id) {
return getRoleById(id).getName();
private boolean isRealmRole(RoleModel role) {
return ! role.isClientRole();
}
@Override
@Deprecated
public void addDefaultRole(String name) {
if (realm.getDefaultRolesIds().add(getOrAddRoleId(name))) {
em.flush();
}
getDefaultRole().addCompositeRole(getOrAddRoleId(name));
}
private String getOrAddRoleId(String name) {
private RoleModel getOrAddRoleId(String name) {
RoleModel role = getRole(name);
if (role == null) {
role = addRole(name);
}
return role.getId();
}
@Override
public void updateDefaultRoles(String[] defaultRoles) {
Set<String> newDefaultRolesIds = Arrays.stream(defaultRoles)
.map(this::getOrAddRoleId)
.collect(Collectors.toSet());
realm.getDefaultRolesIds().retainAll(newDefaultRolesIds);
realm.getDefaultRolesIds().addAll(newDefaultRolesIds);
em.flush();
return role;
}
@Override
@Deprecated
public void removeDefaultRoles(String... defaultRoles) {
Arrays.stream(defaultRoles)
.map(this::getRole)
.filter(Objects::nonNull)
.map(RoleModel::getId)
.forEach(realm.getDefaultRolesIds()::remove);
em.flush();
for (String defaultRole : defaultRoles) {
getDefaultRole().removeCompositeRole(getRole(defaultRole));
}
}
@Override
@ -1204,6 +1191,19 @@ public class RealmAdapter implements RealmModel, JpaModel<RealmEntity> {
em.flush();
}
@Override
public void setDefaultRole(RoleModel role) {
realm.setDefaultRoleId(role.getId());
}
@Override
public RoleModel getDefaultRole() {
if (realm.getDefaultRoleId() == null) {
return null;
}
return session.roles().getRoleById(this, realm.getDefaultRoleId());
}
@Override
public Stream<IdentityProviderModel> getIdentityProvidersStream() {
return realm.getIdentityProviders().stream().map(this::entityToModel);

View file

@ -153,11 +153,6 @@ public class ClientEntity {
@Column(name="NODE_REREG_TIMEOUT")
private int nodeReRegistrationTimeout;
@ElementCollection
@Column(name="ROLE_ID")
@CollectionTable(name="CLIENT_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="CLIENT_ID")})
private Set<String> defaultRolesIds;
@ElementCollection
@Column(name="ROLE_ID")
@CollectionTable(name="SCOPE_MAPPING", joinColumns = { @JoinColumn(name="CLIENT_ID")})
@ -376,17 +371,6 @@ public class ClientEntity {
this.managementUrl = managementUrl;
}
public Set<String> getDefaultRolesIds() {
if (defaultRolesIds == null) {
defaultRolesIds = new HashSet<>();
}
return defaultRolesIds;
}
public void setDefaultRolesIds(Set<String> defaultRolesIds) {
this.defaultRolesIds = defaultRolesIds;
}
public boolean isBearerOnly() {
return bearerOnly;
}

View file

@ -27,7 +27,6 @@ import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.MapKey;
import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
@ -158,11 +157,6 @@ public class RealmEntity {
@CollectionTable(name="REALM_SMTP_CONFIG", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Map<String, String> smtpConfig;
@ElementCollection
@Column(name="ROLE_ID")
@CollectionTable(name="REALM_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="REALM_ID")})
protected Set<String> defaultRolesIds;
@ElementCollection
@Column(name="GROUP_ID")
@CollectionTable(name="REALM_DEFAULT_GROUPS", joinColumns={ @JoinColumn(name="REALM_ID") })
@ -192,6 +186,9 @@ public class RealmEntity {
@Column(name="MASTER_ADMIN_CLIENT")
protected String masterAdminClient;
@Column(name="DEFAULT_ROLE")
protected String defaultRoleId;
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
protected List<IdentityProviderEntity> identityProviders;
@ -459,17 +456,6 @@ public class RealmEntity {
this.smtpConfig = smtpConfig;
}
public Set<String> getDefaultRolesIds() {
if (defaultRolesIds == null) {
defaultRolesIds = new HashSet<>();
}
return defaultRolesIds;
}
public void setDefaultRolesIds(Set<String> defaultRolesIds) {
this.defaultRolesIds = defaultRolesIds;
}
public Set<String> getDefaultGroupIds() {
if (defaultGroupIds == null) {
defaultGroupIds = new HashSet<>();
@ -591,6 +577,14 @@ public class RealmEntity {
this.masterAdminClient = masterAdminClient;
}
public String getDefaultRoleId() {
return defaultRoleId;
}
public void setDefaultRoleId(String defaultRoleId) {
this.defaultRoleId = defaultRoleId;
}
public List<UserFederationProviderEntity> getUserFederationProviders() {
if (userFederationProviders == null) {
userFederationProviders = new LinkedList<>();

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
~ * Copyright 2020 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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="keycloak" id="default-roles">
<addColumn tableName="REALM">
<column name="DEFAULT_ROLE" type="VARCHAR(255)"/>
</addColumn>
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate13_0_0_MigrateDefaultRoles" />
</changeSet>
<changeSet author="keycloak" id="default-roles-cleanup">
<dropTable tableName="REALM_DEFAULT_ROLES" />
<dropTable tableName="CLIENT_DEFAULT_ROLES" />
</changeSet>
</databaseChangeLog>

View file

@ -68,5 +68,6 @@
<include file="META-INF/jpa-changelog-9.0.1.xml"/>
<include file="META-INF/jpa-changelog-11.0.0.xml"/>
<include file="META-INF/jpa-changelog-12.0.0.xml"/>
<include file="META-INF/jpa-changelog-13.0.0.xml"/>
</databaseChangeLog>

View file

@ -22,8 +22,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@ -62,7 +60,6 @@ public abstract class AbstractClientEntity<K> implements AbstractEntity<K> {
private Map<String, ProtocolMapperModel> protocolMappers = new HashMap<>();
private Map<String, Boolean> clientScopes = new HashMap<>();
private Set<String> scopeMappings = new LinkedHashSet<>();
private List<String> defaultRoles = new LinkedList<>();
private boolean surrogateAuthRequired;
private String managementUrl;
private String rootUrl;
@ -333,29 +330,6 @@ public abstract class AbstractClientEntity<K> implements AbstractEntity<K> {
this.baseUrl = baseUrl;
}
public List<String> getDefaultRoles() {
return defaultRoles;
}
public void setDefaultRoles(Collection<String> defaultRoles) {
this.updated |= ! Objects.equals(this.defaultRoles, defaultRoles);
this.defaultRoles.clear();
this.defaultRoles.addAll(defaultRoles);
}
public void addDefaultRole(String name) {
updated = true;
if (name != null) {
defaultRoles.add(name);
}
}
public void removeDefaultRoles(String... defaultRoles) {
for (String defaultRole : defaultRoles) {
updated |= this.defaultRoles.remove(defaultRole);
}
}
public boolean isBearerOnly() {
return bearerOnly;
}

View file

@ -454,22 +454,35 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
/*************** Default roles ****************/
@Override
@Deprecated
public Stream<String> getDefaultRolesStream() {
return entity.getDefaultRoles().stream();
return realm.getDefaultRole().getCompositesStream().filter(this::isClientRole).map(RoleModel::getName);
}
private boolean isClientRole(RoleModel role) {
return role.isClientRole() && Objects.equals(role.getContainerId(), this.getId());
}
@Override
@Deprecated
public void addDefaultRole(String name) {
realm.getDefaultRole().addCompositeRole(getOrAddRoleId(name));
}
private RoleModel getOrAddRoleId(String name) {
RoleModel role = getRole(name);
if (role == null) {
addRole(name);
role = addRole(name);
}
this.entity.addDefaultRole(name);
return role;
}
@Override
@Deprecated
public void removeDefaultRoles(String... defaultRoles) {
this.entity.removeDefaultRoles(defaultRoles);
for (String defaultRole : defaultRoles) {
realm.getDefaultRole().removeCompositeRole(getRole(defaultRole));
}
}
/*************** Protocol mappers ****************/

View file

@ -196,11 +196,6 @@ public class MapRoleProvider implements RoleProvider {
session.users().preRemove(realm, role);
RoleContainerModel container = role.getContainer();
if (container.getDefaultRolesStream().anyMatch(r -> Objects.equals(r, role.getName()))) {
container.removeDefaultRoles(role.getName());
}
//remove role from realm-roles composites
try (Stream<MapRoleEntity> baseStream = getNotRemovedUpdatedRolesStream(realm)
.filter(this::isRealmRole)

View file

@ -43,7 +43,6 @@ import org.keycloak.models.UserProvider;
import org.keycloak.models.map.common.Serialization;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.utils.DefaultRoles;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageProvider;
import org.keycloak.storage.client.ClientStorageProvider;
@ -352,7 +351,7 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
final UserModel userModel = entityToAdapterFunc(realm).apply(entity);
if (addDefaultRoles) {
DefaultRoles.addDefaultRoles(realm, userModel);
userModel.grantRole(realm.getDefaultRole());
// No need to check if user has group as it's new user
realm.getDefaultGroupsStream().forEach(userModel::joinGroup);

View file

@ -30,6 +30,6 @@ public interface AccountRoles {
String MANAGE_CONSENT = "manage-consent";
String DELETE_ACCOUNT = "delete-account";
String[] ALL = {VIEW_PROFILE, MANAGE_ACCOUNT};
String[] DEFAULT = {VIEW_PROFILE, MANAGE_ACCOUNT};
}

View file

@ -49,6 +49,7 @@ public final class Constants {
public static final String READ_TOKEN_ROLE = "read-token";
public static final String[] BROKER_SERVICE_ROLES = {READ_TOKEN_ROLE};
public static final String OFFLINE_ACCESS_ROLE = OAuth2Constants.OFFLINE_ACCESS;
public static final String DEFAULT_ROLES_ROLE_PREFIX = "default-roles";
public static final String AUTHZ_UMA_PROTECTION = "uma_protection";
public static final String AUTHZ_UMA_AUTHORIZATION = "uma_authorization";

View file

@ -339,13 +339,24 @@ public final class KeycloakModelUtils {
return str==null ? null : str.toLowerCase();
}
/**
* Creates default role for particular realm with the given name.
* @param realm Realm
* @param defaultRoleName Name of the newly created defaultRole
*/
public static void setupDefaultRole(RealmModel realm, String defaultRoleName) {
RoleModel defaultRole = realm.addRole(defaultRoleName);
defaultRole.setDescription("${role_default-roles}");
realm.setDefaultRole(defaultRole);
}
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);
realm.addToDefaultRoles(offlineRole);
}
return offlineRole;
@ -633,7 +644,7 @@ public final class KeycloakModelUtils {
if (realm.getRole(roleName) == null) {
RoleModel role = realm.addRole(roleName);
role.setDescription("${role_" + roleName + "}");
realm.addDefaultRole(roleName);
realm.addToDefaultRoles(role);
}
}
}

View file

@ -401,10 +401,8 @@ public class ModelToRepresentation {
if (realm.getClientAuthenticationFlow() != null) rep.setClientAuthenticationFlow(realm.getClientAuthenticationFlow().getAlias());
if (realm.getDockerAuthenticationFlow() != null) rep.setDockerAuthenticationFlow(realm.getDockerAuthenticationFlow().getAlias());
List<String> defaultRoles = realm.getDefaultRolesStream().collect(Collectors.toList());
if (!defaultRoles.isEmpty()) {
rep.setDefaultRoles(defaultRoles);
}
rep.setDefaultRole(toBriefRepresentation(realm.getDefaultRole()));
List<String> defaultGroups = realm.getDefaultGroupsStream()
.map(ModelToRepresentation::buildGroupPath).collect(Collectors.toList());
if (!defaultGroups.isEmpty()) {
@ -605,11 +603,6 @@ public class ModelToRepresentation {
rep.setWebOrigins(new LinkedList<>(webOrigins));
}
String[] defaultRoles = clientModel.getDefaultRolesStream().toArray(String[]::new);
if (defaultRoles.length > 0) {
rep.setDefaultRoles(defaultRoles);
}
if (!clientModel.getRegisteredNodes().isEmpty()) {
rep.setRegisteredNodes(new HashMap<>(clientModel.getRegisteredNodes()));
}

View file

@ -333,23 +333,7 @@ public class RepresentationToModel {
}
importRoles(rep.getRoles(), newRealm);
// Setup realm default roles
if (rep.getDefaultRoles() != null) {
for (String roleString : rep.getDefaultRoles()) {
newRealm.addDefaultRole(roleString.trim());
}
}
// Setup client default roles
if (rep.getClients() != null) {
for (ClientRepresentation resourceRep : rep.getClients()) {
if (resourceRep.getDefaultRoles() != null) {
ClientModel clientModel = createdClients.computeIfAbsent(resourceRep.getClientId(), k -> newRealm.getClientByClientId(resourceRep.getClientId()));
clientModel.updateDefaultRoles(resourceRep.getDefaultRoles());
createdClients.put(clientModel.getClientId(), clientModel);
}
}
}
convertDeprecatedDefaultRoles(rep, newRealm);
// Now that all possible roles and clients are created, create scope mappings
@ -628,9 +612,11 @@ public class RepresentationToModel {
if (realmRoles.getRealm() != null) { // realm roles
for (RoleRepresentation roleRep : realmRoles.getRealm()) {
if (! realm.getDefaultRole().getName().equals(roleRep.getName())) { // default role was already imported
createRole(realm, roleRep);
}
}
}
if (realmRoles.getClient() != null) {
for (Map.Entry<String, List<RoleRepresentation>> entry : realmRoles.getClient().entrySet()) {
ClientModel client = realm.getClientByClientId(entry.getKey());
@ -993,6 +979,47 @@ public class RepresentationToModel {
}
}
private static void convertDeprecatedDefaultRoles(RealmRepresentation rep, RealmModel newRealm) {
if (rep.getDefaultRole() == null) {
// Setup realm default roles
if (rep.getDefaultRoles() != null) {
rep.getDefaultRoles().stream()
.map(String::trim)
.map(name -> getOrAddRealmRole(newRealm, name))
.forEach(role -> newRealm.getDefaultRole().addCompositeRole(role));
}
// Setup client default roles
if (rep.getClients() != null) {
for (ClientRepresentation clientRep : rep.getClients()) {
if (clientRep.getDefaultRoles() != null) {
Arrays.stream(clientRep.getDefaultRoles())
.map(String::trim)
.map(name -> getOrAddClientRole(newRealm.getClientById(clientRep.getId()), name))
.forEach(role -> newRealm.getDefaultRole().addCompositeRole(role));
}
}
}
}
}
private static RoleModel getOrAddRealmRole(RealmModel realm, String name) {
RoleModel role = realm.getRole(name);
if (role == null) {
role = realm.addRole(name);
}
return role;
}
private static RoleModel getOrAddClientRole(ClientModel client, String name) {
RoleModel role = client.getRole(name);
if (role == null) {
role = client.addRole(name);
}
return role;
}
public static void renameRealm(RealmModel realm, String name) {
if (name.equals(realm.getName())) return;
@ -1130,10 +1157,6 @@ public class RepresentationToModel {
realm.setPasswordPolicy(PasswordPolicy.parse(session, rep.getPasswordPolicy()));
if (rep.getOtpPolicyType() != null) realm.setOTPPolicy(toPolicy(rep));
if (rep.getDefaultRoles() != null) {
realm.updateDefaultRoles(rep.getDefaultRoles().toArray(new String[rep.getDefaultRoles().size()]));
}
WebAuthnPolicy webAuthnPolicy = getWebAuthnPolicyTwoFactor(rep);
realm.setWebAuthnPolicy(webAuthnPolicy);
@ -1222,7 +1245,7 @@ public class RepresentationToModel {
// Roles
public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
public static RoleModel 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());
if (roleRep.getAttributes() != null) {
@ -1230,6 +1253,7 @@ public class RepresentationToModel {
role.setAttribute(attribute.getKey(), attribute.getValue());
}
}
return role;
}
private static void addComposites(RoleModel role, RoleRepresentation roleRep, RealmModel realm) {
@ -1264,7 +1288,7 @@ public class RepresentationToModel {
private static Map<String, ClientModel> createClients(KeycloakSession session, RealmRepresentation rep, RealmModel realm, Map<String, String> mappedFlows) {
Map<String, ClientModel> appMap = new HashMap<String, ClientModel>();
for (ClientRepresentation resourceRep : rep.getClients()) {
ClientModel app = createClient(session, realm, resourceRep, false, mappedFlows);
ClientModel app = createClient(session, realm, resourceRep, mappedFlows);
appMap.put(app.getClientId(), app);
ValidationUtil.validateClient(session, app, false, r -> {
@ -1281,11 +1305,11 @@ public class RepresentationToModel {
* @param resourceRep
* @return
*/
public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, boolean addDefaultRoles) {
return createClient(session, realm, resourceRep, addDefaultRoles, null);
public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep) {
return createClient(session, realm, resourceRep, null);
}
private static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, boolean addDefaultRoles, Map<String, String> mappedFlows) {
private static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation resourceRep, Map<String, String> mappedFlows) {
logger.debugv("Create client: {0}", resourceRep.getClientId());
ClientModel client = resourceRep.getId() != null ? realm.addClient(resourceRep.getId(), resourceRep.getClientId()) : realm.addClient(resourceRep.getClientId());
@ -1408,11 +1432,6 @@ public class RepresentationToModel {
}
}
if (addDefaultRoles && resourceRep.getDefaultRoles() != null) {
client.updateDefaultRoles(resourceRep.getDefaultRoles());
}
if (resourceRep.getProtocolMappers() != null) {
// first, remove all default/built in mappers
client.getProtocolMappersStream().collect(Collectors.toList()).forEach(client::removeProtocolMapper);
@ -1528,9 +1547,6 @@ public class RepresentationToModel {
if (rep.getNotBefore() != null) {
resource.setNotBefore(rep.getNotBefore());
}
if (rep.getDefaultRoles() != null) {
resource.updateDefaultRoles(rep.getDefaultRoles());
}
List<String> redirectUris = rep.getRedirectUris();
if (redirectUris != null) {

View file

@ -25,7 +25,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModelDefaultMethods;
import org.keycloak.models.utils.DefaultRoles;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException;
@ -76,7 +75,7 @@ public class InMemoryUserAdapter extends UserModelDefaultMethods.Streams {
}
public void addDefaults() {
DefaultRoles.addDefaultRoles(realm, this);
this.grantRole(realm.getDefaultRole());
realm.getDefaultGroupsStream().forEach(this::joinGroup);
}

View file

@ -668,6 +668,18 @@ public interface RealmModel extends RoleContainerModel {
void setMasterAdminClient(ClientModel client);
/**
* Returns default realm role. All both realm and client default roles are assigned as composite of this role.
* @return Default role of this realm
*/
RoleModel getDefaultRole();
/**
* Sets default role for this realm
* @param role to be set
*/
void setDefaultRole(RoleModel role);
boolean isIdentityFederationEnabled();
boolean isInternationalizationEnabled();
@ -788,4 +800,12 @@ public interface RealmModel extends RoleContainerModel {
}
Stream<ClientScopeModel> getDefaultClientScopesStream(boolean defaultScope);
/**
* Adds a role as a composite to default role of this realm.
* @param role to be added
*/
default void addToDefaultRoles(RoleModel role) {
getDefaultRole().addCompositeRole(role);
}
}

View file

@ -17,12 +17,12 @@
package org.keycloak.models;
import org.keycloak.provider.ProviderEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import org.keycloak.provider.ProviderEvent;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@ -71,9 +71,7 @@ public interface RoleContainerModel {
Stream<RoleModel> searchForRolesStream(String search, Integer first, Integer max);
/**
* Returns all the default role names of this object.
* @return List of the default role names of this object. Never returns {@code null}.
* @deprecated use the stream variant instead
* @deprecated Default roles are now managed by {@link org.keycloak.models.RealmModel#getDefaultRole()}. This method will be removed.
*/
@Deprecated
default List<String> getDefaultRoles() {
@ -81,25 +79,21 @@ public interface RoleContainerModel {
}
/**
* Returns all default role names of this object as a stream.
* @return stream of default role names of this object. Never returns {@code null}.
* @deprecated Default roles are now managed by {@link org.keycloak.models.RealmModel#getDefaultRole()}. This method will be removed.
*/
@Deprecated
Stream<String> getDefaultRolesStream();
/**
* Adds a role with given name to default roles of this object. If the role
* doesn't exist a new role is created.
* @param name of the role to be (created and ) added
* @deprecated Default roles are now managed by {@link org.keycloak.models.RealmModel#getDefaultRole()}. This method will be removed.
*/
@Deprecated
void addDefaultRole(String name);
/**
* Updates default roles of this object. It removes all default roles which
* are not specified by {@code defaultRoles} and adds all which weren't
* present in original default roles. In other words it's the same as calling
* {@code Set.retainAll} and {@code Set.addAll}.
* @param defaultRoles Array of realm roles to be updated
* @deprecated Default roles are now managed by {@link org.keycloak.models.RealmModel#getDefaultRole()}. This method will be removed.
*/
@Deprecated
default void updateDefaultRoles(String... defaultRoles) {
List<String> defaultRolesArray = Arrays.asList(defaultRoles);
Collection<String> entities = getDefaultRolesStream().collect(Collectors.toList());
@ -122,9 +116,9 @@ public interface RoleContainerModel {
}
/**
* Removes default roles from this object according to {@code defaultRoles}.
* @param defaultRoles Role names to be removed from default roles of this object.
* @deprecated Default roles are now managed by {@link org.keycloak.models.RealmModel#getDefaultRole()}. This method will be removed.
*/
@Deprecated
void removeDefaultRoles(String... defaultRoles);
}

View file

@ -1,43 +0,0 @@
/*
* 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.models.utils;
import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import java.util.stream.Stream;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class DefaultRoles {
public static Stream<RoleModel> getDefaultRoles(RealmModel realm) {
Stream<RoleModel> realmDefaultRoles = realm.getDefaultRolesStream().map(realm::getRole);
Stream<RoleModel> clientDefaultRoles = realm.getClientsStream().flatMap(DefaultRoles::toClientDefaultRoles);
return Stream.concat(realmDefaultRoles, clientDefaultRoles);
}
public static void addDefaultRoles(RealmModel realm, UserModel userModel) {
getDefaultRoles(realm).forEach(userModel::grantRole);
}
private static Stream<RoleModel> toClientDefaultRoles(ClientModel c) {
return c.getDefaultRolesStream().map(c::getRole);
}
}

View file

@ -25,7 +25,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModelDefaultMethods;
import org.keycloak.models.utils.DefaultRoles;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.ReadOnlyException;
import org.keycloak.storage.StorageId;
@ -175,7 +174,7 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
@Override
public Set<RoleModel> getRoleMappings() {
Set<RoleModel> set = new HashSet<>();
if (appendDefaultRolesToRoleMappings()) set.addAll(DefaultRoles.getDefaultRoles(realm).collect(Collectors.toSet()));
if (appendDefaultRolesToRoleMappings()) set.addAll(realm.getDefaultRole().getCompositesStream().collect(Collectors.toSet()));
set.addAll(getRoleMappingsInternal());
return set;
}
@ -457,7 +456,7 @@ public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
@Override
public Stream<RoleModel> getRoleMappingsStream() {
Stream<RoleModel> roleMappings = getRoleMappingsInternal().stream();
if (appendDefaultRolesToRoleMappings()) return Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm));
if (appendDefaultRolesToRoleMappings()) return Stream.concat(roleMappings, realm.getDefaultRole().getCompositesStream());
return roleMappings;
}

View file

@ -25,7 +25,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModelDefaultMethods;
import org.keycloak.models.utils.DefaultRoles;
import org.keycloak.models.utils.RoleUtils;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
@ -216,7 +215,7 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
@Override
public Set<RoleModel> getRoleMappings() {
Set<RoleModel> set = new HashSet<>(getFederatedRoleMappings());
if (appendDefaultRolesToRoleMappings()) set.addAll(DefaultRoles.getDefaultRoles(realm).collect(Collectors.toSet()));
if (appendDefaultRolesToRoleMappings()) set.addAll(realm.getDefaultRole().getCompositesStream().collect(Collectors.toSet()));
set.addAll(getRoleMappingsInternal());
return set;
}
@ -503,7 +502,7 @@ public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefau
@Override
public Stream<RoleModel> getRoleMappingsStream() {
Stream<RoleModel> roleMappings = getFederatedRoleMappings().stream();
if (appendDefaultRolesToRoleMappings()) roleMappings = Stream.concat(roleMappings, DefaultRoles.getDefaultRoles(realm));
if (appendDefaultRolesToRoleMappings()) roleMappings = Stream.concat(roleMappings, realm.getDefaultRole().getCompositesStream());
return Stream.concat(roleMappings, getRoleMappingsInternal().stream());
}

View file

@ -117,7 +117,7 @@ public class ClientsPartialImport extends AbstractPartialImport<ClientRepresenta
}
}
ClientModel client = RepresentationToModel.createClient(session, realm, clientRep, true);
ClientModel client = RepresentationToModel.createClient(session, realm, clientRep);
RepresentationToModel.importAuthorizationSettings(clientRep, client, session);
}

View file

@ -60,7 +60,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
try {
RealmModel realm = session.getContext().getRealm();
ClientModel clientModel = ClientManager.createClient(session, realm, client, true);
ClientModel clientModel = ClientManager.createClient(session, realm, client);
if (clientModel.isServiceAccountsEnabled()) {
new ClientManager(new RealmManager(session)).enableServiceAccount(clientModel);

View file

@ -75,8 +75,8 @@ public class ClientManager {
* @param addDefaultRoles
* @return
*/
public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation rep, boolean addDefaultRoles) {
ClientModel client = RepresentationToModel.createClient(session, realm, rep, addDefaultRoles);
public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation rep) {
ClientModel client = RepresentationToModel.createClient(session, realm, rep);
if (rep.getProtocol() != null) {
LoginProtocolFactory providerFactory = (LoginProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, rep.getProtocol());

View file

@ -110,6 +110,7 @@ public class RealmManager {
// setup defaults
setupRealmDefaults(realm);
KeycloakModelUtils.setupDefaultRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + name.toLowerCase());
setupMasterAdminManagement(realm);
setupRealmAdminManagement(realm);
setupAccountManagement(realm);
@ -427,10 +428,10 @@ public class RealmManager {
accountClient.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
for (String role : AccountRoles.ALL) {
accountClient.addDefaultRole(role);
RoleModel roleModel = accountClient.getRole(role);
for (String role : AccountRoles.DEFAULT) {
RoleModel roleModel = accountClient.addRole(role);
roleModel.setDescription("${role_" + role + "}");
realm.addToDefaultRoles(roleModel);
}
RoleModel manageAccountLinks = accountClient.addRole(AccountRoles.MANAGE_ACCOUNT_LINKS);
manageAccountLinks.setDescription("${role_" + AccountRoles.MANAGE_ACCOUNT_LINKS + "}");
@ -521,6 +522,12 @@ public class RealmManager {
setupRealmDefaults(realm);
if (rep.getDefaultRole() == null) {
KeycloakModelUtils.setupDefaultRole(realm, determineDefaultRoleName(rep));
} else {
realm.setDefaultRole(RepresentationToModel.createRole(realm, rep.getDefaultRole()));
}
boolean postponeMasterClientSetup = postponeMasterClientSetup(rep);
if (!postponeMasterClientSetup) {
setupMasterAdminManagement(realm);
@ -605,6 +612,21 @@ public class RealmManager {
return realm;
}
private String determineDefaultRoleName(RealmRepresentation rep) {
String defaultRoleName = Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + rep.getRealm().toLowerCase();
if (! hasRealmRole(rep, defaultRoleName)) {
return defaultRoleName;
} else {
for (int i = 1; i < Integer.MAX_VALUE; i++) {
defaultRoleName = Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + rep.getRealm().toLowerCase() + "-" + i;
if (! hasRealmRole(rep, defaultRoleName)) {
return defaultRoleName;
}
}
}
return null;
}
private boolean postponeMasterClientSetup(RealmRepresentation rep) {
if (!Config.getAdminRealm().equals(rep.getRealm())) {
return false;

View file

@ -176,7 +176,7 @@ public class ClientsResource {
}
try {
ClientModel clientModel = ClientManager.createClient(session, realm, rep, true);
ClientModel clientModel = ClientManager.createClient(session, realm, rep);
if (TRUE.equals(rep.isServiceAccountsEnabled())) {
UserModel serviceAccount = session.users().getServiceAccount(clientModel);

View file

@ -28,6 +28,8 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.representations.idm.ManagementPermissionReference;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions;
@ -42,8 +44,8 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
/**
@ -104,6 +106,13 @@ public class RoleByIdResource extends RoleResource {
@DELETE
@NoCache
public void deleteRole(final @PathParam("role-id") String id) {
if (realm.getDefaultRole() == null) {
logger.warnf("Default role for realm with id '%s' doesn't exist.", realm.getId());
} else if (realm.getDefaultRole().getId().equals(id)) {
throw new ErrorResponseException(ErrorResponse.error(realm.getDefaultRole().getName() + " is default role of the realm and cannot be removed.",
Response.Status.BAD_REQUEST));
}
RoleModel role = getRoleModel(id);
auth.roles().requireManage(role);
deleteRole(role);

View file

@ -180,7 +180,9 @@ public class RoleMapperResource {
Function<RoleModel, RoleRepresentation> toBriefRepresentation = briefRepresentation ?
ModelToRepresentation::toBriefRepresentation : ModelToRepresentation::toRepresentation;
return realm.getRolesStream().filter(roleMapper::hasRole).map(toBriefRepresentation);
return realm.getRolesStream()
.filter(roleMapper::hasRole)
.map(toBriefRepresentation);
}
/**

View file

@ -73,6 +73,7 @@ public abstract class RoleResource {
protected void addComposites(AdminPermissionEvaluator auth, AdminEventBuilder adminEvent, UriInfo uriInfo, List<RoleRepresentation> roles, RoleModel role) {
for (RoleRepresentation rep : roles) {
if (rep.getId() == null) throw new NotFoundException("Could not find composite role");
RoleModel composite = realm.getRoleById(rep.getId());
if (composite == null) {
throw new NotFoundException("Could not find composite role");

View file

@ -237,7 +237,7 @@ public class ScopeMappedResource {
for (RoleRepresentation role : roles) {
RoleModel roleModel = realm.getRoleById(role.getId());
if (roleModel == null) {
throw new NotFoundException("Client not found");
throw new NotFoundException("Role not found");
}
scopeContainer.deleteScopeMapping(roleModel);
}

View file

@ -97,7 +97,6 @@ public class DropAllServlet extends HttpServlet {
"alter table CLIENT nocheck constraint FK_P56CTINXXB9GSK57FO49F9TAC;\n" +
"_drop_table_ CLIENT_ATTRIBUTES _cascade_;\n" +
"_drop_table_ CLIENT_AUTH_FLOW_BINDINGS _cascade_;\n" +
"_drop_table_ CLIENT_DEFAULT_ROLES _cascade_;\n" +
"_drop_table_ CLIENT_INITIAL_ACCESS _cascade_;\n" +
"_drop_table_ CLIENT_NODE_REGISTRATIONS _cascade_;\n" +
"_drop_table_ CLIENT_SCOPE_ATTRIBUTES _cascade_;\n" +
@ -141,7 +140,6 @@ public class DropAllServlet extends HttpServlet {
"_drop_table_ REALM_ATTRIBUTE _cascade_;\n" +
"_drop_table_ REALM_DEFAULT_GROUPS _cascade_;\n" +
"_drop_table_ KEYCLOAK_GROUP _cascade_;\n" +
"_drop_table_ REALM_DEFAULT_ROLES _cascade_;\n" +
"_drop_table_ REALM_ENABLED_EVENT_TYPES _cascade_;\n" +
"_drop_table_ REALM_EVENTS_LISTENERS _cascade_;\n" +
"_drop_table_ REALM_REQUIRED_CREDENTIAL _cascade_;\n" +

View file

@ -122,9 +122,9 @@ public class AddUserTest extends AbstractKeycloakTest {
//--------------Roles-----------------------//
try {
List<RoleRepresentation> realmRoles = userResource.roles().realmLevel().listAll();
assertRoles(realmRoles, "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertRoles(userResource.roles().realmLevel().listAll(), "admin", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realmName);
assertRoles(userResource.roles().realmLevel().listEffective(), "create-realm", Constants.AUTHZ_UMA_AUTHORIZATION,
Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realmName, Constants.OFFLINE_ACCESS_ROLE, "admin");
List<ClientRepresentation> clients = realmResource.clients().findAll();
String accountId = null;
@ -134,8 +134,9 @@ public class AddUserTest extends AbstractKeycloakTest {
}
}
List<RoleRepresentation> accountRoles = userResource.roles().clientLevel(accountId).listAll();
assertRoles(accountRoles, "view-profile", "manage-account");
assertTrue(userResource.roles().clientLevel(accountId).listAll().isEmpty());
List<RoleRepresentation> accountRoles = userResource.roles().clientLevel(accountId).listEffective();
assertRoles(accountRoles, "view-profile", "manage-account", "manage-account-links");
} finally {
userResource.remove();
}

View file

@ -64,6 +64,8 @@ import java.util.stream.Collectors;
import static java.util.Arrays.asList;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
@ -331,19 +333,21 @@ public class ClientTest extends AbstractAdminTest {
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.clientRoleResourcePath(id, "test"), role, ResourceType.CLIENT_ROLE);
ClientRepresentation foundClientRep = realm.clients().get(id).toRepresentation();
foundClientRep.setDefaultRoles(new String[]{"test"});
realm.clients().get(id).update(foundClientRep);
role = realm.clients().get(id).roles().get("test").toRepresentation();
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, AdminEventPaths.clientResourcePath(id), rep, ResourceType.CLIENT);
realm.roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME).addComposites(Collections.singletonList(role));
assertArrayEquals(new String[]{"test"}, realm.clients().get(id).toRepresentation().getDefaultRoles());
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourceCompositesPath(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME), Collections.singletonList(role), ResourceType.REALM_ROLE);
assertThat(realm.roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME).getRoleComposites().stream().map(RoleRepresentation::getName).collect(Collectors.toSet()),
hasItem(role.getName()));
realm.clients().get(id).roles().deleteRole("test");
assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.clientRoleResourcePath(id, "test"), ResourceType.CLIENT_ROLE);
assertNull(realm.clients().get(id).toRepresentation().getDefaultRoles());
assertThat(realm.roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME).getRoleComposites().stream().map(RoleRepresentation::getName).collect(Collectors.toSet()),
not(hasItem(role)));
}
@Test
@ -555,7 +559,7 @@ public class ClientTest extends AbstractAdminTest {
Assert.assertNames(scopesResource.realmLevel().listAll(), "role1");
Assert.assertNames(scopesResource.realmLevel().listEffective(), "role1", "role2");
Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME);
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll(), AccountRoles.VIEW_PROFILE);
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listEffective(), AccountRoles.VIEW_PROFILE);
@ -573,7 +577,7 @@ public class ClientTest extends AbstractAdminTest {
Assert.assertNames(scopesResource.realmLevel().listAll());
Assert.assertNames(scopesResource.realmLevel().listEffective());
Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "role1", "role2");
Assert.assertNames(scopesResource.realmLevel().listAvailable(), "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "role1", "role2", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME);
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAll());
Assert.assertNames(scopesResource.clientLevel(accountMgmtId).listAvailable(), AccountRoles.VIEW_PROFILE, AccountRoles.MANAGE_ACCOUNT, AccountRoles.MANAGE_ACCOUNT_LINKS, AccountRoles.VIEW_APPLICATIONS, AccountRoles.VIEW_CONSENT, AccountRoles.MANAGE_CONSENT, AccountRoles.DELETE_ACCOUNT);

View file

@ -22,12 +22,14 @@ import org.junit.Test;
import org.keycloak.admin.client.resource.RoleByIdResource;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.AdminEventPaths;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.RoleBuilder;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
@ -191,4 +193,9 @@ public class RoleByIdResourceTest extends AbstractAdminTest {
Assert.assertRoleAttributes(attributes, roleAttributes);
}
}
@Test (expected = BadRequestException.class)
public void deleteDefaultRole() {
resource.deleteRole(adminClient.realm(REALM_NAME).roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME).toRepresentation().getId());
}
}

View file

@ -2009,7 +2009,8 @@ public class UserTest extends AbstractAdminTest {
assertAdminEvents.clear();
RoleMappingResource roles = realm.users().get(userId).roles();
assertNames(roles.realmLevel().listAll(), "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertNames(roles.realmLevel().listAll(), Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
assertNames(roles.realmLevel().listEffective(), "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
// Add realm roles
List<RoleRepresentation> l = new LinkedList<>();
@ -2028,9 +2029,9 @@ public class UserTest extends AbstractAdminTest {
assertAdminEvents.assertEvent("test", OperationType.CREATE, AdminEventPaths.userClientRoleMappingsPath(userId, clientUuid), ResourceType.CLIENT_ROLE_MAPPING);
// List realm roles
assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
assertNames(roles.realmLevel().listAvailable(), "admin", "customer-user-premium", "realm-composite-role", "sample-realm-role", "attribute-role");
assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
// List realm effective role with full representation
List<RoleRepresentation> realmRolesFullRepresentations = roles.realmLevel().listEffective(false);
@ -2051,17 +2052,16 @@ public class UserTest extends AbstractAdminTest {
// Get mapping representation
MappingsRepresentation all = roles.getAll();
assertNames(all.getRealmMappings(), "realm-role", "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertEquals(2, all.getClientMappings().size());
assertNames(all.getRealmMappings(), "realm-role", "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
assertEquals(1, all.getClientMappings().size());
assertNames(all.getClientMappings().get("myclient").getMappings(), "client-role", "client-composite");
assertNames(all.getClientMappings().get("account").getMappings(), "manage-account", "view-profile");
// Remove realm role
RoleRepresentation realmRoleRep = realm.roles().get("realm-role").toRepresentation();
roles.realmLevel().remove(Collections.singletonList(realmRoleRep));
assertAdminEvents.assertEvent("test", OperationType.DELETE, AdminEventPaths.userRealmRoleMappingsPath(userId), Collections.singletonList(realmRoleRep), ResourceType.REALM_ROLE_MAPPING);
assertNames(roles.realmLevel().listAll(), "realm-composite", "user", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION);
assertNames(roles.realmLevel().listAll(), "realm-composite", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
// Remove client role
RoleRepresentation clientRoleRep = realm.clients().get(clientUuid).roles().get("client-role").toRepresentation();

View file

@ -52,7 +52,7 @@ public class AuthorizationTest extends AbstractAuthorizationTest {
UserRepresentation serviceAccount = realm.users().search(ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + resourceServer.getClientId()).get(0);
Assert.assertNotNull(serviceAccount);
List<RoleRepresentation> serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listAll();
List<RoleRepresentation> serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listEffective();
Assert.assertTrue(serviceAccountRoles.stream().anyMatch(roleRepresentation -> "uma_protection".equals(roleRepresentation.getName())));
enableAuthorizationServices(false);
@ -61,7 +61,7 @@ public class AuthorizationTest extends AbstractAuthorizationTest {
serviceAccount = clientResource.getServiceAccountUser();
Assert.assertNotNull(serviceAccount);
realm = realmsResouce().realm(getRealmId());
serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listAll();
serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listEffective();
Assert.assertTrue(serviceAccountRoles.stream().anyMatch(roleRepresentation -> "uma_protection".equals(roleRepresentation.getName())));
JSPolicyRepresentation policy = new JSPolicyRepresentation();
@ -97,7 +97,7 @@ public class AuthorizationTest extends AbstractAuthorizationTest {
serviceAccount = clientResource.getServiceAccountUser();
Assert.assertNotNull(serviceAccount);
serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listAll();
serviceAccountRoles = realm.users().get(serviceAccount.getId()).roles().clientLevel(resourceServer.getId()).listEffective();
Assert.assertTrue(serviceAccountRoles.stream().anyMatch(roleRepresentation -> "uma_protection".equals(roleRepresentation.getName())));
}

View file

@ -33,6 +33,7 @@ import org.keycloak.representations.idm.authorization.ResourceRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.RoleBuilder;
/**
*
@ -119,12 +120,15 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
ClientResource clientResource = getClientResource();
AuthorizationResource authorizationResource = clientResource.authorization();
testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-1").defaultRoles("client-role").build()).close();
testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-2").defaultRoles("client-role").build()).close();
testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-1").build()).close();
testRealmResource().clients().create(ClientBuilder.create().clientId("test-client-2").build()).close();
ClientRepresentation client1 = getClientByClientId("test-client-1");
ClientRepresentation client2 = getClientByClientId("test-client-2");
testRealmResource().clients().get(client1.getId()).roles().create(RoleBuilder.create().name("client-role").build());
testRealmResource().clients().get(client2.getId()).roles().create(RoleBuilder.create().name("client-role").build());
RoleRepresentation role1 = testRealmResource().clients().get(client1.getId()).roles().get("client-role").toRepresentation();
RoleRepresentation role2 = testRealmResource().clients().get(client2.getId()).roles().get("client-role").toRepresentation();
@ -134,11 +138,8 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
Map<String, String> config = new HashMap<>();
config.put("roles", "[{\"id\":\"" + role1.getId() +"\"},{\"id\":\"" + role2.getId() +"\"}]");
policy.setConfig(config);
Response create = authorizationResource.policies().create(policy);
try {
try (Response create = authorizationResource.policies().create(policy)) {
Assert.assertEquals(Status.CREATED, create.getStatusInfo());
} finally {
create.close();
}
//export authorization settings

View file

@ -671,7 +671,7 @@ public class GroupTest extends AbstractGroupTest {
// List realm roles
assertNames(roles.realmLevel().listAll(), "realm-role", "realm-composite");
assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user", "customer-user-premium", "realm-composite-role", "sample-realm-role", "attribute-role");
assertNames(roles.realmLevel().listAvailable(), "admin", "offline_access", Constants.AUTHZ_UMA_AUTHORIZATION, "user", "customer-user-premium", "realm-composite-role", "sample-realm-role", "attribute-role", Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
assertNames(roles.realmLevel().listEffective(), "realm-role", "realm-composite", "realm-child");
// List client roles

View file

@ -48,9 +48,6 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertNull("Default groups are empty", rep.getDefaultGroups());
Assert.assertNull("Groups are empty", rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNull("Realm and client roles are empty", rep.getRoles());
Assert.assertNull("Clients are empty", rep.getClients());
@ -65,9 +62,6 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertNotNull("Groups not empty", rep.getGroups());
checkGroups(rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNotNull("Realm and client roles not empty", rep.getRoles());
Assert.assertNotNull("Realm roles not empty", rep.getRoles().getRealm());
checkRealmRoles(rep.getRoles().getRealm());
@ -86,8 +80,6 @@ public class PartialExportTest extends AbstractAdminTest {
checkServiceAccountRoles(rep.getUsers().get(0), false); // export but without roles
Assert.assertNull("Default groups are empty", rep.getDefaultGroups());
Assert.assertNull("Groups are empty", rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNull("Realm and client roles are empty", rep.getRoles());
Assert.assertNotNull("Clients not empty", rep.getClients());
@ -107,8 +99,6 @@ public class PartialExportTest extends AbstractAdminTest {
Assert.assertNotNull("Groups not empty", rep.getGroups());
checkGroups(rep.getGroups());
Assert.assertNotNull("Default roles not empty", rep.getDefaultRoles());
checkDefaultRoles(rep.getDefaultRoles());
Assert.assertNotNull("Realm and client roles not empty", rep.getRoles());
Assert.assertNotNull("Realm roles not empty", rep.getRoles().getRealm());

View file

@ -46,12 +46,16 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.ws.rs.ClientErrorException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -59,6 +63,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.keycloak.models.Constants;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -84,10 +89,10 @@ public class RealmRolesTest extends AbstractAdminTest {
ClientRepresentation clientRep = ClientBuilder.create().clientId("client-a").build();
Response response = adminClient.realm(REALM_NAME).clients().create(clientRep);
try (Response response = adminClient.realm(REALM_NAME).clients().create(clientRep)) {
clientUuid = ApiUtil.getCreatedId(response);
getCleanup().addClientUuid(clientUuid);
response.close();
}
RoleRepresentation roleC = RoleBuilder.create().name("role-c").description("Role C").build();
adminClient.realm(REALM_NAME).clients().get(clientUuid).roles().create(roleC);
@ -368,17 +373,17 @@ public class RealmRolesTest extends AbstractAdminTest {
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleName), role, ResourceType.REALM_ROLE);
}
String roleNameA = "abcdef";
String roleNameA = "abcdefg";
RoleRepresentation roleA = makeRole(roleNameA);
resource.create(roleA);
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleNameA), roleA, ResourceType.REALM_ROLE);
String roleNameB = "defghi";
String roleNameB = "defghij";
RoleRepresentation roleB = makeRole(roleNameB);
resource.create(roleB);
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath(roleNameB), roleB, ResourceType.REALM_ROLE);
List<RoleRepresentation> resultSearch = resource.list("def", -1, -1);
List<RoleRepresentation> resultSearch = resource.list("defg", -1, -1);
assertEquals(2,resultSearch.size());
List<RoleRepresentation> resultSearch2 = resource.list("testrole", -1, -1);
@ -485,4 +490,49 @@ public class RealmRolesTest extends AbstractAdminTest {
List<RoleRepresentation> roles = resource.list("attributesrolebrief", true);
assertNull(roles.get(0).getAttributes());
}
@Test
public void testDefaultRoles() {
RoleResource defaultRole = adminClient.realm(REALM_NAME).roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME);
UserRepresentation user = adminClient.realm(REALM_NAME).users().search("test-role-member").get(0);
UserResource userResource = adminClient.realm(REALM_NAME).users().get(user.getId());
assertThat(convertRolesToNames(userResource.roles().realmLevel().listAll()), hasItem(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME));
assertThat(convertRolesToNames(userResource.roles().realmLevel().listEffective()), allOf(
hasItem(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME),
hasItem(Constants.OFFLINE_ACCESS_ROLE),
hasItem(Constants.AUTHZ_UMA_AUTHORIZATION)
));
defaultRole.addComposites(Collections.singletonList(resource.get("role-a").toRepresentation()));
userResource = adminClient.realm(REALM_NAME).users().get(user.getId());
assertThat(convertRolesToNames(userResource.roles().realmLevel().listAll()), allOf(
hasItem(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME),
not(hasItem("role-a"))
));
assertThat(convertRolesToNames(userResource.roles().realmLevel().listEffective()), allOf(
hasItem(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME),
hasItem(Constants.OFFLINE_ACCESS_ROLE),
hasItem(Constants.AUTHZ_UMA_AUTHORIZATION),
hasItem("role-a")
));
assertThat(userResource.roles().clientLevel(clientUuid).listAll(), empty());
assertThat(userResource.roles().clientLevel(clientUuid).listEffective(), empty());
defaultRole.addComposites(Collections.singletonList(adminClient.realm(REALM_NAME).clients().get(clientUuid).roles().get("role-c").toRepresentation()));
userResource = adminClient.realm(REALM_NAME).users().get(user.getId());
assertThat(userResource.roles().clientLevel(clientUuid).listAll(), empty());
assertThat(convertRolesToNames(userResource.roles().clientLevel(clientUuid).listEffective()),
hasItem("role-c")
);
}
private List<String> convertRolesToNames(List<RoleRepresentation> roles) {
return roles.stream().map(RoleRepresentation::getName).collect(Collectors.toList());
}
}

View file

@ -240,13 +240,16 @@ public class RealmTest extends AbstractAdminTest {
@Test
public void createRealmFromJson() {
RealmRepresentation rep = loadJson(getClass().getResourceAsStream("/admin-test/testrealm.json"), RealmRepresentation.class);
try {
adminClient.realms().create(rep);
RealmRepresentation created = adminClient.realms().realm("admin-test-1").toRepresentation();
assertRealm(rep, created);
} finally {
adminClient.realms().realm("admin-test-1").remove();
}
}
//KEYCLOAK-6146
@Test
@ -531,14 +534,12 @@ public class RealmTest extends AbstractAdminTest {
realm.roles().create(role);
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourcePath("test"), role, ResourceType.REALM_ROLE);
assertNotNull(realm.roles().get("test").toRepresentation());
role = realm.roles().get("test").toRepresentation();
assertNotNull(role);
RealmRepresentation rep = realm.toRepresentation();
rep.setDefaultRoles(new LinkedList<String>());
rep.getDefaultRoles().add("test");
realm.roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME).addComposites(Collections.singletonList(role));
realm.update(rep);
assertAdminEvents.assertEvent(realmId, OperationType.UPDATE, Matchers.nullValue(String.class), rep, ResourceType.REALM);
assertAdminEvents.assertEvent(realmId, OperationType.CREATE, AdminEventPaths.roleResourceCompositesPath(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + REALM_NAME), Collections.singletonList(role), ResourceType.REALM_ROLE);
realm.roles().deleteRole("test");
assertAdminEvents.assertEvent(realmId, OperationType.DELETE, AdminEventPaths.roleResourcePath("test"), ResourceType.REALM_ROLE);
@ -642,13 +643,6 @@ public class RealmTest extends AbstractAdminTest {
if (realm.getPasswordPolicy() != null) assertEquals(realm.getPasswordPolicy(), storedRealm.getPasswordPolicy());
if (realm.getDefaultRoles() != null) {
assertNotNull(storedRealm.getDefaultRoles());
for (String role : realm.getDefaultRoles()) {
assertTrue(storedRealm.getDefaultRoles().contains(role));
}
}
if (realm.getSmtpServer() != null) {
assertEquals(realm.getSmtpServer(), storedRealm.getSmtpServer());
}

View file

@ -81,9 +81,11 @@ public abstract class AbstractIdentityProviderMapperTest extends AbstractBaseBro
user.setRealmRoles(realmRoles);
Map<String, List<String>> clientRoles = new HashMap<>();
if (roles.getClientMappings() != null) {
roles.getClientMappings().forEach((key, value) -> clientRoles.put(key, value.getMappings().stream()
.map(RoleRepresentation::getName)
.collect(Collectors.toList())));
}
user.setClientRoles(clientRoles);
return user;

View file

@ -13,6 +13,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static org.hamcrest.Matchers.equalTo;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
@ -69,7 +70,7 @@ public class KcAdmSessionTest extends AbstractAdmCliTest {
assertExitCodeAndStdErrSize(exe, 0, 0);
List<ObjectNode> roles = loadJson(exe.stdout(), LIST_OF_JSON);
Assert.assertTrue("expect two realm roles available", roles.size() == 2);
Assert.assertThat("expected three realm roles available", roles.size(), equalTo(3));
// create realm role
exe = execute("create roles --config '" + configFile.getName() + "' -s name=testrole -s 'description=Test role' -o");
@ -84,7 +85,7 @@ public class KcAdmSessionTest extends AbstractAdmCliTest {
assertExitCodeAndStdErrSize(exe, 0, 0);
roles = loadJson(exe.stdout(), LIST_OF_JSON);
Assert.assertTrue("expect three realm roles available", roles.size() == 3);
Assert.assertThat("expected four realm roles available", roles.size(), equalTo(4));
// create client
exe = execute("create clients --config '" + configFile.getName() + "' -s clientId=testclient -i");
@ -104,7 +105,7 @@ public class KcAdmSessionTest extends AbstractAdmCliTest {
assertExitCodeAndStdErrSize(exe, 0, 0);
roles = loadJson(exe.stdout(), LIST_OF_JSON);
Assert.assertTrue("expect one role", roles.size() == 1);
Assert.assertThat("expected one role", roles.size(), equalTo(1));
Assert.assertEquals("clientrole", roles.get(0).get("name").asText());
// add created role to user - we are realm admin so we can add role to ourself
@ -122,17 +123,13 @@ public class KcAdmSessionTest extends AbstractAdmCliTest {
List<String> realmMappings = StreamSupport.stream(node.get("realmMappings").spliterator(), false)
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
Assert.assertEquals(Arrays.asList("offline_access", "uma_authorization"), realmMappings);
Assert.assertEquals(Arrays.asList("default-roles-demorealm"), realmMappings);
ObjectNode clientRoles = (ObjectNode) node.get("clientMappings");
//List<String> fields = asSortedList(clientRoles.fieldNames());
List<String> fields = StreamSupport.stream(clientRoles.spliterator(), false)
.map(o -> o.get("client").asText()).sorted().collect(Collectors.toList());
Assert.assertEquals(Arrays.asList("account", "realm-management", "testclient"), fields);
realmMappings = StreamSupport.stream(clientRoles.get("account").get("mappings").spliterator(), false)
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
Assert.assertEquals(Arrays.asList("manage-account", "view-profile"), realmMappings);
Assert.assertEquals(Arrays.asList("realm-management", "testclient"), fields);
realmMappings = StreamSupport.stream(clientRoles.get("realm-management").get("mappings").spliterator(), false)
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
@ -159,7 +156,7 @@ public class KcAdmSessionTest extends AbstractAdmCliTest {
realmMappings = StreamSupport.stream(node.get("realmMappings").spliterator(), false)
.map(o -> o.get("name").asText()).sorted().collect(Collectors.toList());
Assert.assertEquals(Arrays.asList("offline_access", "testrole", "uma_authorization"), realmMappings);
Assert.assertEquals(Arrays.asList("default-roles-demorealm", "testrole"), realmMappings);
// create a group
exe = execute("create groups --config '" + configFile.getName() + "' -s name=TestUsers -i");

View file

@ -147,6 +147,8 @@ import org.keycloak.testsuite.util.OAuthClient;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.testsuite.util.RoleBuilder;
import org.keycloak.testsuite.util.ServerURLs;
import org.keycloak.util.JsonSerialization;
@ -396,9 +398,7 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
assertEquals(OIDCLoginProtocol.CLIENT_SECRET_BASIC, response.getTokenEndpointAuthMethod());
events.expect(EventType.CLIENT_INFO).client(clientId).user(Matchers.isEmptyOrNullString()).assertEvent();
updateClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles(Arrays.asList("sample-client-role").toArray(new String[1]));
});
adminClient.realm(REALM_NAME).clients().get(clientId).roles().create(RoleBuilder.create().name("sample-client-role").build());
successfulLoginAndLogoutWithPKCE(response.getClientId(), clientSecret, userName, userPassword);
}
@ -411,9 +411,7 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
assertEquals(OIDCLoginProtocol.CLIENT_SECRET_BASIC, clientRep.getTokenEndpointAuthMethod());
events.expect(EventType.CLIENT_REGISTER).client(clientId).user(Matchers.isEmptyOrNullString()).assertEvent();
events.expect(EventType.CLIENT_INFO).client(clientId).user(Matchers.isEmptyOrNullString()).assertEvent();
updateClientByAdmin(clientId, (ClientRepresentation cr) -> {
cr.setDefaultRoles((String[]) Arrays.asList("sample-client-role").toArray(new String[1]));
});
adminClient.realm(REALM_NAME).clients().get(clientId).roles().create(RoleBuilder.create().name("sample-client-role").build());
successfulLoginAndLogout(clientId, clientRep.getClientSecret());
@ -446,9 +444,9 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
String clientId = "Zahlungs-App";
String clientSecret = "secret";
String cid = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles((String[]) Arrays.asList("sample-client-role").toArray(new String[1]));
clientRep.setSecret(clientSecret);
});
adminClient.realm(REALM_NAME).clients().get(cid).roles().create(RoleBuilder.create().name("sample-client-role").build());
try {
successfulLoginAndLogout(clientId, clientSecret);
@ -497,10 +495,9 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
String clientId = "Zahlungs-App";
String clientSecret = "secret";
String cid = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
String[] defaultRoles = {"sample-client-role"};
clientRep.setDefaultRoles(defaultRoles);
clientRep.setSecret(clientSecret);
});
adminClient.realm(REALM_NAME).clients().get(cid).roles().create(RoleBuilder.create().name("sample-client-role").build());
try {
successfulLoginAndLogout(clientId, clientSecret);
@ -584,17 +581,21 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
String clientAlphaId = "Alpha-App";
String clientAlphaSecret = "secretAlpha";
String cAlphaId = createClientByAdmin(clientAlphaId, (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles((String[]) Arrays.asList("sample-client-role-alpha", "sample-client-role-common").toArray(new String[2]));
clientRep.setSecret(clientAlphaSecret);
clientRep.setClientAuthenticatorType(JWTClientSecretAuthenticator.PROVIDER_ID);
});
RolesResource rolesResourceAlpha = adminClient.realm(REALM_NAME).clients().get(cAlphaId).roles();
rolesResourceAlpha.create(RoleBuilder.create().name("sample-client-role-alpha").build());
rolesResourceAlpha.create(RoleBuilder.create().name("sample-client-role-common").build());
String clientBetaId = "Beta-App";
String clientBetaSecret = "secretBeta";
String cBetaId = createClientByAdmin(clientBetaId, (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles((String[]) Arrays.asList("sample-client-role-beta", "sample-client-role-common").toArray(new String[2]));
clientRep.setSecret(clientBetaSecret);
});
RolesResource rolesResourceBeta = adminClient.realm(REALM_NAME).clients().get(cBetaId).roles();
rolesResourceBeta.create(RoleBuilder.create().name("sample-client-role-beta").build());
rolesResourceBeta.create(RoleBuilder.create().name("sample-client-role-common").build());
try {
assertEquals(ClientIdAndSecretAuthenticator.PROVIDER_ID, getClientByAdmin(cAlphaId).getClientAuthenticatorType());
@ -649,13 +650,12 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
String clientId = "Zahlungs-App";
String clientSecret = "secret";
String cid = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
String[] defaultRoles = {"sample-client-role"};
clientRep.setDefaultRoles(defaultRoles);
clientRep.setSecret(clientSecret);
clientRep.setStandardFlowEnabled(Boolean.TRUE);
clientRep.setImplicitFlowEnabled(Boolean.TRUE);
clientRep.setPublicClient(Boolean.FALSE);
});
adminClient.realm(REALM_NAME).clients().get(cid).roles().create(RoleBuilder.create().name("sample-client-role").build());
try {
oauth.clientId(clientId);
@ -717,10 +717,9 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
String clientId = "Zahlungs-App";
String clientSecret = "secret";
String cid = createClientByAdmin(clientId, (ClientRepresentation clientRep) -> {
String[] defaultRoles = {"sample-client-role"};
clientRep.setDefaultRoles(defaultRoles);
clientRep.setSecret(clientSecret);
});
adminClient.realm(REALM_NAME).clients().get(cid).roles().create(RoleBuilder.create().name("sample-client-role").build());
try {
oauth.clientId(clientId);
@ -822,16 +821,16 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
String clientAlphaId = "Alpha-App";
String clientAlphaSecret = "secretAlpha";
String cAlphaId = createClientByAdmin(clientAlphaId, (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles((String[]) Arrays.asList("sample-client-role-alpha").toArray(new String[1]));
clientRep.setSecret(clientAlphaSecret);
});
adminClient.realm(REALM_NAME).clients().get(cAlphaId).roles().create(RoleBuilder.create().name("sample-client-role-alpha").build());
String clientBetaId = "Beta-App";
String clientBetaSecret = "secretBeta";
String cBetaId = createClientByAdmin(clientBetaId, (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles((String[]) Arrays.asList("sample-client-role-beta").toArray(new String[1]));
clientRep.setSecret(clientBetaSecret);
});
adminClient.realm(REALM_NAME).clients().get(cBetaId).roles().create(RoleBuilder.create().name("sample-client-role-beta").build());
try {
successfulLoginAndLogout(clientAlphaId, clientAlphaSecret);
@ -923,7 +922,6 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
// create by Admin REST API - fail
try {
createClientByAdmin("App-by-Admin", (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles((String[]) Arrays.asList("sample-client-role-beta").toArray(new String[1]));
clientRep.setSecret("secretBeta");
clientRep.setAttributes(new HashMap<>());
clientRep.getAttributes().put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, Algorithm.none.name());
@ -966,7 +964,6 @@ public class ClientPolicyBasicsTest extends AbstractKeycloakTest {
// create dynamically - fail
try {
createClientByAdmin("App-in-Dynamic", (ClientRepresentation clientRep) -> {
clientRep.setDefaultRoles((String[]) Arrays.asList("sample-client-role-beta").toArray(new String[1]));
clientRep.setSecret("secretBeta");
clientRep.setAttributes(new HashMap<>());
clientRep.getAttributes().put(OIDCConfigAttributes.USER_INFO_RESPONSE_SIGNATURE_ALG, Algorithm.RS384.name());

View file

@ -101,10 +101,6 @@ public class ExportImportUtil {
Assert.assertEquals(1, creds.size());
String cred = (String)creds.iterator().next();
Assert.assertEquals("password", cred);
Assert.assertEquals(4, realm.getDefaultRoles().size());
Assert.assertNotNull(RealmRepUtil.findDefaultRole(realm, "foo"));
Assert.assertNotNull(RealmRepUtil.findDefaultRole(realm, "bar"));
RealmResource realmRsc = adminClient.realm(realm.getRealm());

View file

@ -151,7 +151,6 @@ public class LDAPRoleMappingsTest extends AbstractLDAPTest {
ClientModel accountApp = appRealm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
ClientModel financeApp = appRealm.getClientByClientId("finance");
RoleModel manageAccountRole = accountApp.getRole(AccountRoles.MANAGE_ACCOUNT);
RoleModel financeRole1 = financeApp.getRole("financeRole1");
john.grantRole(financeRole1);
@ -163,7 +162,6 @@ public class LDAPRoleMappingsTest extends AbstractLDAPTest {
Assert.assertFalse(johnDbRoles.contains(realmRole2));
Assert.assertFalse(johnDbRoles.contains(realmRole3));
Assert.assertFalse(johnDbRoles.contains(financeRole1));
Assert.assertTrue(johnDbRoles.contains(manageAccountRole));
// 3 - Check that role mappings are in LDAP and hence available through federation
@ -172,17 +170,12 @@ public class LDAPRoleMappingsTest extends AbstractLDAPTest {
Assert.assertFalse(johnRoles.contains(realmRole2));
Assert.assertTrue(johnRoles.contains(realmRole3));
Assert.assertTrue(johnRoles.contains(financeRole1));
Assert.assertTrue(johnRoles.contains(manageAccountRole));
Set<RoleModel> johnRealmRoles = john.getRealmRoleMappingsStream().collect(Collectors.toSet());
Assert.assertEquals(2, johnRealmRoles.size());
Assert.assertTrue(johnRealmRoles.contains(realmRole1));
Assert.assertTrue(johnRealmRoles.contains(realmRole3));
// account roles are not mapped in LDAP. Those are in Keycloak DB
Set<RoleModel> johnAccountRoles = john.getClientRoleMappingsStream(accountApp).collect(Collectors.toSet());
Assert.assertTrue(johnAccountRoles.contains(manageAccountRole));
Set<RoleModel> johnFinanceRoles = john.getClientRoleMappingsStream(financeApp).collect(Collectors.toSet());
Assert.assertEquals(1, johnFinanceRoles.size());
Assert.assertTrue(johnFinanceRoles.contains(financeRole1));
@ -192,19 +185,16 @@ public class LDAPRoleMappingsTest extends AbstractLDAPTest {
john.deleteRoleMapping(realmRole3);
john.deleteRoleMapping(realmRole1);
john.deleteRoleMapping(financeRole1);
john.deleteRoleMapping(manageAccountRole);
johnRoles = john.getRoleMappingsStream().collect(Collectors.toSet());
Assert.assertFalse(johnRoles.contains(realmRole1));
Assert.assertFalse(johnRoles.contains(realmRole2));
Assert.assertFalse(johnRoles.contains(realmRole3));
Assert.assertFalse(johnRoles.contains(financeRole1));
Assert.assertFalse(johnRoles.contains(manageAccountRole));
// Cleanup
mary.deleteRoleMapping(realmRole2);
mary.deleteRoleMapping(realmRole3);
john.grantRole(manageAccountRole);
});
}
@ -490,9 +480,6 @@ public class LDAPRoleMappingsTest extends AbstractLDAPTest {
LDAPTestContext ctx = LDAPTestContext.init(session);
RealmModel appRealm = ctx.getRealm();
// Set a default role on the realm
appRealm.addDefaultRole("realmRole1");
UserModel david = session.users().addUser(appRealm, "davidkeycloak");
RoleModel defaultRole = appRealm.getRole("realmRole1");
@ -501,11 +488,18 @@ public class LDAPRoleMappingsTest extends AbstractLDAPTest {
Assert.assertNotNull(defaultRole);
Assert.assertNotNull(realmRole2);
// Set a default role on the realm
appRealm.addToDefaultRoles(defaultRole);
Set<RoleModel> davidRoles = david.getRealmRoleMappingsStream().collect(Collectors.toSet());
Assert.assertTrue(davidRoles.contains(defaultRole));
// default role is not assigned directly
Assert.assertFalse(davidRoles.contains(defaultRole));
Assert.assertFalse(davidRoles.contains(realmRole2));
// but david should have the role as effective
Assert.assertTrue(david.hasRole(defaultRole));
Assert.assertFalse(david.hasRole(realmRole2));
});
}
}

View file

@ -247,7 +247,6 @@ public class LDAPRoleMappingsNoImportTest extends AbstractLDAPTest {
RoleModel realmRole3 = appRealm.getRole("realmRole3");
ClientModel accountApp = appRealm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
ClientModel financeApp = appRealm.getClientByClientId("finance");
RoleModel manageAccountRole = accountApp.getRole(AccountRoles.MANAGE_ACCOUNT);
RoleModel financeRole1 = financeApp.getRole("financeRole1");
// 3 - Check that role mappings are in LDAP and hence available through federation
@ -257,17 +256,12 @@ public class LDAPRoleMappingsNoImportTest extends AbstractLDAPTest {
Assert.assertFalse(johnRoles.contains(realmRole2));
Assert.assertTrue(johnRoles.contains(realmRole3));
Assert.assertTrue(johnRoles.contains(financeRole1));
Assert.assertTrue(johnRoles.contains(manageAccountRole));
Set<RoleModel> johnRealmRoles = john.getRealmRoleMappingsStream().collect(Collectors.toSet());
Assert.assertEquals(2, johnRealmRoles.size());
Assert.assertTrue(johnRealmRoles.contains(realmRole1));
Assert.assertTrue(johnRealmRoles.contains(realmRole3));
// account roles are not mapped in LDAP. Those are in Keycloak DB
Set<RoleModel> johnAccountRoles = john.getClientRoleMappingsStream(accountApp).collect(Collectors.toSet());
Assert.assertTrue(johnAccountRoles.contains(manageAccountRole));
Set<RoleModel> johnFinanceRoles = john.getClientRoleMappingsStream(financeApp).collect(Collectors.toSet());
Assert.assertEquals(1, johnFinanceRoles.size());
Assert.assertTrue(johnFinanceRoles.contains(financeRole1));
@ -303,9 +297,6 @@ public class LDAPRoleMappingsNoImportTest extends AbstractLDAPTest {
LDAPTestUtils.addOrUpdateRoleLDAPMappers(appRealm, ctx.getLdapModel(), LDAPGroupMapperMode.LDAP_ONLY);
// Set a default role on the realm
appRealm.addDefaultRole("realmRole1");
UserModel david = session.users().addUser(appRealm, "davidkeycloak");
// make sure we are in no-import mode
@ -317,16 +308,23 @@ public class LDAPRoleMappingsNoImportTest extends AbstractLDAPTest {
Assert.assertNotNull(defaultRole);
Assert.assertNotNull(realmRole2);
// Set a default role on the realm
appRealm.addToDefaultRoles(defaultRole);
Set<RoleModel> davidRoles = david.getRealmRoleMappingsStream().collect(Collectors.toSet());
Assert.assertTrue(davidRoles.contains(defaultRole));
// default role is not assigned directly
Assert.assertFalse(davidRoles.contains(defaultRole));
Assert.assertFalse(davidRoles.contains(realmRole2));
// but david should have the role as effective
Assert.assertTrue(david.hasRole(defaultRole));
Assert.assertFalse(david.hasRole(realmRole2));
// Make sure john has not received the default role
UserModel john = session.users().getUserByUsername("johnkeycloak", appRealm);
Set<RoleModel> johnRoles = john.getRealmRoleMappingsStream().collect(Collectors.toSet());
Assert.assertFalse(johnRoles.contains(defaultRole));
Assert.assertFalse(john.hasRole(defaultRole));
});
}

View file

@ -46,4 +46,11 @@ public abstract class AbstractJsonFileImportMigrationTest extends AbstractMigrat
Assert.assertThat(migrationRealm.toRepresentation().getRealm(), is(equalTo("Migration")));
Assert.assertThat(migrationRealm2.toRepresentation().getRealm(), is(equalTo("Migration2")));
}
@Override
protected void testMigrationTo13_0_0() {
testDefaultRoles(migrationRealm);
testDefaultRolesNameWhenTaken();
}
}

View file

@ -64,11 +64,9 @@ 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.arquillian.migration.MigrationContext;
import org.keycloak.testsuite.exportimport.ExportImportUtil;
import org.keycloak.testsuite.runonserver.RunHelpers;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.WaitUtils;
import org.keycloak.util.TokenUtil;
import java.io.IOException;
@ -83,11 +81,15 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
@ -120,7 +122,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
}
protected void testMigratedMigrationData(boolean supportsAuthzService) {
assertNames(migrationRealm.roles().list(), "offline_access", "uma_authorization", "migration-test-realm-role");
assertNames(migrationRealm.roles().list(), "offline_access", "uma_authorization", "default-roles-migration", "migration-test-realm-role");
List<String> expectedClientIds = new ArrayList<>(Arrays.asList("account", "account-console", "admin-cli", "broker", "migration-test-client", "realm-management", "security-admin-console"));
if (supportsAuthzService) {
@ -136,7 +138,7 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
}
protected void testMigratedMasterData() {
assertNames(masterRealm.roles().list(), "offline_access", "uma_authorization", "create-realm", "master-test-realm-role", "admin");
assertNames(masterRealm.roles().list(), "offline_access", "uma_authorization", "default-roles-master", "create-realm", "master-test-realm-role", "admin");
assertNames(masterRealm.clients().findAll(), "admin-cli", "security-admin-console", "broker", "account", "account-console",
"master-realm", "master-test-client", "Migration-realm", "Migration2-realm");
String id = masterRealm.clients().findByClientId("master-test-client").get(0).getId();
@ -290,7 +292,14 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
testDeleteAccount(migrationRealm);
}
private void testDeleteAccount(RealmResource realm) {
protected void testMigrationTo13_0_0() {
testDefaultRoles(masterRealm);
testDefaultRoles(migrationRealm);
testDefaultRolesNameWhenTaken();
}
protected void testDeleteAccount(RealmResource realm) {
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
ClientResource accountResource = realm.clients().get(accountClient.getId());
@ -570,7 +579,8 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
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));
assertThat("role should be added to default roles for new users", realm.roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.toRepresentation().getRealm().toLowerCase()).getRoleComposites().stream()
.map(RoleRepresentation::getName).collect(Collectors.toSet()), hasItem(roleName));
}
//test admin roles - master admin client
List<ClientRepresentation> clients = realm.clients().findByClientId(realm.toRepresentation().getRealm() + "-realm");
@ -902,9 +912,9 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
protected void testMigrationTo9_x() {
testMigrationTo9_0_0();
}
protected void testMigrationTo12_x() {
testMigrationTo12_0_0();
testMigrationTo13_0_0();
}
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
@ -930,4 +940,19 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
Assert.assertFalse(clientRep.isAlwaysDisplayInConsole());
}
}
protected void testDefaultRoles(RealmResource realm) {
String realmName = realm.toRepresentation().getRealm().toLowerCase();
assertThat(realm.roles().get("default-roles-" + realmName).getRoleComposites().stream()
.map(RoleRepresentation::getName).collect(Collectors.toSet()),
allOf(
hasItem(realmName + "-test-realm-role"),
hasItem(realmName + "-test-client-role"))
);
}
protected void testDefaultRolesNameWhenTaken() {
// 'default-roles-migration2' name is used, we test that 'default-roles-migration2-1' is created instead
assertThat(migrationRealm2.toRepresentation().getDefaultRole().getName(), equalTo("default-roles-migration2-1"));
}
}

View file

@ -44,6 +44,8 @@ import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;
import org.keycloak.models.Constants;
import org.keycloak.models.RoleModel;
import static org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer.REMOTE;
/**
@ -248,6 +250,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
KeycloakSession currentSession = sesRealmRemoved1;
RealmModel realm = currentSession.realms().getRealm("test");
RealmModel fooRealm = currentSession.realms().createRealm("foo-realm");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName()));
fooRealm.addClient("foo-client");

View file

@ -79,9 +79,6 @@ public class ClientModelTest extends AbstractKeycloakTest {
assertThat(expected.getDescription(), is(actual.getDescription()));
assertThat(expected.getBaseUrl(), is(actual.getBaseUrl()));
assertThat(expected.getManagementUrl(), is(actual.getManagementUrl()));
assertThat(expected.getDefaultRolesStream().collect(Collectors.toSet()),
is(actual.getDefaultRolesStream().collect(Collectors.toSet())));
assertThat(expected.getRedirectUris().containsAll(actual.getRedirectUris()), is(true));
assertThat(expected.getWebOrigins().containsAll(actual.getWebOrigins()), is(true));
assertThat(expected.getRegisteredNodes(), is(actual.getRegisteredNodes()));
@ -99,8 +96,6 @@ public class ClientModelTest extends AbstractKeycloakTest {
client.addRole("role-1");
client.addRole("role-2");
client.addRole("role-3");
client.addDefaultRole("role-1");
client.addDefaultRole("role-2");
client.addRedirectUri("redirect-1");
client.addRedirectUri("redirect-2");
client.addWebOrigin("origin-1");
@ -288,7 +283,7 @@ public class ClientModelTest extends AbstractKeycloakTest {
}
realm = currentSession.realms().createRealm("copy");
ClientModel copyClient = RepresentationToModel.createClient(currentSession, realm, representation, true);
ClientModel copyClient = RepresentationToModel.createClient(currentSession, realm, representation);
assertEquals(client, copyClient);

View file

@ -39,6 +39,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import org.keycloak.models.Constants;
import org.keycloak.models.RoleModel;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
@ -68,6 +70,8 @@ public class ConcurrentTransactionsTest extends AbstractTestRealmKeycloakTest {
sessionSetup.users().addUser(realm, "user2").setEmail("user2@localhost");
realm = sessionSetup.realms().createRealm("original");
RoleModel defaultRole = sessionSetup.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName());
realm.setDefaultRole(defaultRole);
client[0] = sessionSetup.clients().addClient(realm, "client");
client[0].setSecret("old");
@ -195,6 +199,7 @@ public class ConcurrentTransactionsTest extends AbstractTestRealmKeycloakTest {
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionSet) -> {
RealmModel realm = sessionSet.realms().createRealm("original");
realm.setDefaultRole(sessionSet.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
UserModel john = sessionSet.users().addUser(realm, "john");
john.setSingleAttribute("foo", "val1");

View file

@ -33,6 +33,7 @@ import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.arquillian.annotation.ModelTest;
import java.util.concurrent.atomic.AtomicReference;
import org.keycloak.models.Constants;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
@ -70,6 +71,9 @@ public class MultipleRealmsTest extends AbstractTestRealmKeycloakTest {
RealmModel realm1 = currentSession.realms().createRealm("id1", "realm1");
RealmModel realm2 = currentSession.realms().createRealm("id2", "realm2");
realm1.setDefaultRole(currentSession.roles().addRealmRole(realm1, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm1.getName()));
realm2.setDefaultRole(currentSession.roles().addRealmRole(realm2, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm2.getName()));
createObjects(currentSession, realm1);
createObjects(currentSession, realm2);
@ -137,6 +141,9 @@ public class MultipleRealmsTest extends AbstractTestRealmKeycloakTest {
RealmModel realm1 = currentSession.realms().createRealm("id1", "realm1");
RealmModel realm2 = currentSession.realms().createRealm("id2", "realm2");
realm1.setDefaultRole(currentSession.roles().addRealmRole(realm1, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm1.getName()));
realm2.setDefaultRole(currentSession.roles().addRealmRole(realm2, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm2.getName()));
createObjects(currentSession, realm1);
createObjects(currentSession, realm2);

View file

@ -373,7 +373,6 @@ public class OwnerReplacementTest extends AbstractKeycloakTest {
((session, realm1) -> {
RoleModel role = session.getProvider(RoleProvider.class).addRealmRole(realm1, "foo");
realm1.addDefaultRole("foo");
return role.getId();
}),

View file

@ -29,6 +29,7 @@ import org.keycloak.testsuite.arquillian.annotation.ModelTest;
import org.keycloak.testsuite.runonserver.RunOnServerException;
import java.util.List;
import org.keycloak.models.Constants;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
@ -61,7 +62,8 @@ public class SimpleModelTest extends AbstractKeycloakTest {
// Transaction 1
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession session1) -> {
session1.realms().createRealm("foo");
RealmModel realm = session1.realms().createRealm("foo");
realm.setDefaultRole(session1.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
});

View file

@ -48,6 +48,7 @@ import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.keycloak.models.Constants;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
/**
@ -228,6 +229,7 @@ public class UserSessionPersisterProviderTest extends AbstractTestRealmKeycloakT
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR1) -> {
KeycloakSession currentSession = sessionRR1;
RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName()));
fooRealm.addClient("foo-app");
currentSession.users().addUser(fooRealm, "user3");
@ -275,6 +277,7 @@ public class UserSessionPersisterProviderTest extends AbstractTestRealmKeycloakT
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCR1) -> {
KeycloakSession currentSession = sessionCR1;
RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX));
fooRealm.addClient("foo-app");
fooRealm.addClient("bar-app");

View file

@ -53,6 +53,7 @@ import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.keycloak.models.Constants;
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
/**
@ -226,6 +227,7 @@ public class UserSessionProviderOfflineTest extends AbstractTestRealmKeycloakTes
currentSession = sessionRR1;
persister = currentSession.getProvider(UserSessionPersisterProvider.class);
RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName()));
fooRealm.addClient("foo-app");
currentSession.users().addUser(fooRealm, "user3");
@ -258,6 +260,7 @@ public class UserSessionProviderOfflineTest extends AbstractTestRealmKeycloakTes
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRR3) -> {
currentSession = sessionRR3;
RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName()));
fooRealm.addClient("foo-app");
currentSession.users().addUser(fooRealm, "user3");
@ -289,6 +292,7 @@ public class UserSessionProviderOfflineTest extends AbstractTestRealmKeycloakTes
sessionManager = new UserSessionManager(currentSession);
persister = currentSession.getProvider(UserSessionPersisterProvider.class);
RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName()));
fooRealm.addClient("foo-app");
fooRealm.addClient("bar-app");
@ -381,6 +385,7 @@ public class UserSessionProviderOfflineTest extends AbstractTestRealmKeycloakTes
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionUR1) -> {
currentSession = sessionUR1;
RealmModel fooRealm = currentSession.realms().createRealm("foo", "foo");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName()));
fooRealm.addClient("foo-app");
currentSession.users().addUser(fooRealm, "user3");

View file

@ -85,6 +85,7 @@ import static org.junit.Assert.assertThat;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
import org.keycloak.testsuite.util.RoleBuilder;
/**
* @author pedroigor
@ -177,13 +178,12 @@ public class UserInfoTest extends AbstractKeycloakTest {
// KEYCLOAK-8838
@Test
public void testSuccess_dotsInClientId() throws Exception {
// Create client with dot in the name and with some role
// Create client with dot in the name
ClientRepresentation clientRep = org.keycloak.testsuite.util.ClientBuilder.create()
.clientId("my.foo.client")
.addRedirectUri("http://foo.host")
.secret("password")
.directAccessGrants()
.defaultRoles("my.foo.role")
.build();
RealmResource realm = adminClient.realm("test");
@ -193,6 +193,9 @@ public class UserInfoTest extends AbstractKeycloakTest {
resp.close();
getCleanup().addClientUuid(clientUUID);
//Create role with dot in the name
realm.clients().get(clientUUID).roles().create(RoleBuilder.create().name("my.foo.role").build());
// Assign role to the user
RoleRepresentation fooRole = realm.clients().get(clientUUID).roles().get("my.foo.role").toRepresentation();
UserResource userResource = ApiUtil.findUserByUsernameId(realm, "test-user@localhost");

View file

@ -73,6 +73,7 @@ public class ClientBuilder {
return this;
}
@Deprecated
public ClientBuilder defaultRoles(String... roles) {
rep.setDefaultRoles(roles);
return this;

View file

@ -324,7 +324,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access" ],
"defaultRoles" : [ "offline_access", "master-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"passwordPolicy" : "hashIterations(20000)",
"otpPolicyType" : "totp",
@ -1012,6 +1012,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "83dadb00-0510-4cae-b0dc-1ce1a1969ae3",
"defaultRoles" : [ "master-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,
@ -1727,7 +1728,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access" ],
"defaultRoles" : [ "offline_access", "migration-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"passwordPolicy" : "hashIterations(20000)",
"otpPolicyType" : "totp",
@ -2126,6 +2127,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "secret",
"defaultRoles" : [ "migration-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,
@ -2815,6 +2817,12 @@
"description" : "${role_offline-access}",
"scopeParamRequired" : true,
"composite" : false
}, {
"id" : "a495da40-f44c-4e28-8f82-75bb5677e597",
"name" : "default-roles-migration2",
"description" : "${role_default-roles}",
"scopeParamRequired" : true,
"composite" : false
} ],
"client" : {
"realm-management" : [ {

View file

@ -466,7 +466,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access", "uma_authorization" ],
"defaultRoles" : [ "offline_access", "uma_authorization", "master-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"passwordPolicy" : "hashIterations(20000)",
"otpPolicyType" : "totp",
@ -1181,6 +1181,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "4f427905-9843-4986-9d6c-97a304055f92",
"defaultRoles" : [ "master-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,
@ -2076,7 +2077,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access", "uma_authorization" ],
"defaultRoles" : [ "offline_access", "uma_authorization", "migration-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"passwordPolicy" : "hashIterations(20000)",
"otpPolicyType" : "totp",
@ -2499,6 +2500,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "secret",
"defaultRoles" : [ "migration-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,
@ -3434,6 +3436,12 @@
"composite" : false,
"clientRole" : false,
"containerId" : "Migration2"
}, {
"id" : "a495da40-f44c-4e28-8f82-75bb5677e597",
"name" : "default-roles-migration2",
"description" : "${role_default-roles}",
"scopeParamRequired" : true,
"composite" : false
} ],
"client" : {
"realm-management" : [ {

View file

@ -287,7 +287,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access", "uma_authorization" ],
"defaultRoles" : [ "offline_access", "uma_authorization", "migration-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"otpPolicyType" : "totp",
"otpPolicyAlgorithm" : "HmacSHA1",
@ -708,6 +708,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "secret",
"defaultRoles" : [ "migration-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,
@ -1683,6 +1684,12 @@
"composite" : false,
"clientRole" : false,
"containerId" : "Migration2"
}, {
"id" : "a495da40-f44c-4e28-8f82-75bb5677e597",
"name" : "default-roles-migration2",
"description" : "${role_default-roles}",
"scopeParamRequired" : true,
"composite" : false
} ],
"client" : {
"realm-management" : [ {
@ -3553,7 +3560,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access", "uma_authorization" ],
"defaultRoles" : [ "offline_access", "uma_authorization", "master-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"otpPolicyType" : "totp",
"otpPolicyAlgorithm" : "HmacSHA1",
@ -4266,6 +4273,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "932dd4b7-42e8-44d6-9791-651bec2a757b",
"defaultRoles" : [ "master-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,

View file

@ -292,7 +292,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access", "uma_authorization" ],
"defaultRoles" : [ "offline_access", "uma_authorization", "migration-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"otpPolicyType" : "totp",
"otpPolicyAlgorithm" : "HmacSHA1",
@ -509,6 +509,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "secret",
"defaultRoles" : [ "migration-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,
@ -1584,6 +1585,12 @@
"clientRole" : false,
"containerId" : "Migration2",
"attributes" : { }
}, {
"id" : "a495da40-f44c-4e28-8f82-75bb5677e597",
"name" : "default-roles-migration2",
"description" : "${role_default-roles}",
"scopeParamRequired" : true,
"composite" : false
} ],
"client" : {
"realm-management" : [ {
@ -3508,7 +3515,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access", "uma_authorization" ],
"defaultRoles" : [ "offline_access", "uma_authorization", "master-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"otpPolicyType" : "totp",
"otpPolicyAlgorithm" : "HmacSHA1",
@ -3727,6 +3734,7 @@
"enabled" : true,
"clientAuthenticatorType" : "client-secret",
"secret" : "21e5b3ab-d978-4552-bbb5-dead19284b5d",
"defaultRoles" : [ "master-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,

View file

@ -320,7 +320,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "offline_access", "uma_authorization" ],
"defaultRoles" : [ "offline_access", "uma_authorization", "migration-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"otpPolicyType" : "totp",
"otpPolicyAlgorithm" : "HmacSHA1",
@ -534,6 +534,7 @@
"alwaysDisplayInConsole" : false,
"clientAuthenticatorType" : "client-secret",
"secret" : "ff12b4c2-abba-4b88-a76b-ffbdb4d725fd",
"defaultRoles" : [ "migration-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,
@ -1762,6 +1763,12 @@
"clientRole" : false,
"containerId" : "Migration2",
"attributes" : { }
}, {
"id" : "a495da40-f44c-4e28-8f82-75bb5677e597",
"name" : "default-roles-migration2",
"description" : "${role_default-roles}",
"scopeParamRequired" : true,
"composite" : false
} ],
"client" : {
"realm-management" : [ {
@ -3968,7 +3975,7 @@
"clientRoles" : { },
"subGroups" : [ ]
} ],
"defaultRoles" : [ "uma_authorization", "offline_access" ],
"defaultRoles" : [ "uma_authorization", "offline_access", "master-test-realm-role" ],
"requiredCredentials" : [ "password" ],
"otpPolicyType" : "totp",
"otpPolicyAlgorithm" : "HmacSHA1",
@ -4255,6 +4262,7 @@
"alwaysDisplayInConsole" : false,
"clientAuthenticatorType" : "client-secret",
"secret" : "a7064a25-7d2d-4a2d-a916-e7b033870803",
"defaultRoles" : [ "master-test-client-role" ],
"redirectUris" : [ ],
"webOrigins" : [ ],
"notBefore" : 0,

View file

@ -14,6 +14,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.keycloak.models.Constants;
import org.keycloak.testsuite.console.page.roles.DefaultRoles;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
import static org.keycloak.testsuite.util.WaitUtils.pause;
@ -30,6 +32,8 @@ public class RealmRolesTest extends AbstractRolesTest {
private CreateRole createRolePage;
@Page
private RoleDetails roleDetailsPage;
@Page
private DefaultRoles defaultRolesPage;
private RoleRepresentation testRole;
@ -123,6 +127,24 @@ public class RealmRolesTest extends AbstractRolesTest {
assertAlertDanger();
}
@Test
public void testDefaultRoleWithinRoleList() {
//test role name link leads to Default Roles tab
configure().roles();
realmRolesPage.table().clickRole(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
defaultRolesPage.assertCurrent();
//test role edit button leads to Default Roles tab
configure().roles();
realmRolesPage.table().editRole(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
defaultRolesPage.assertCurrent();
//test delete default role doesn't work
configure().roles();
realmRolesPage.table().deleteRole(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test");
assertTrue(realmRolesPage.table().containsRole(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-test"));
}
public void createTestRoles(String namePrefix, int count) {
Timer.DEFAULT.reset();
for (int i = 0; i < count; i++) {

View file

@ -17,6 +17,7 @@
package org.keycloak.testsuite.model;
import org.keycloak.component.ComponentModel;
import org.keycloak.models.Constants;
import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@ -67,6 +68,7 @@ public class UserModelTest extends KeycloakModelTest {
@Override
public void createEnvironment(KeycloakSession s) {
RealmModel realm = s.realms().createRealm("realm");
realm.setDefaultRole(s.roles().addRealmRole(realm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + realm.getName()));
this.realmId = realm.getId();
IntStream.range(0, NUM_GROUPS).forEach(i -> {

View file

@ -747,13 +747,12 @@ module.controller('RealmPasswordPolicyCtrl', function($scope, Realm, realm, $htt
};
});
module.controller('RealmDefaultRolesCtrl', function ($scope, $route, Realm, realm, roles, Notifications, ClientRole, Client) {
module.controller('RealmDefaultRolesCtrl', function ($scope, $route, realm, roles, Notifications, ClientRole, Client, RoleRealmComposites, RoleClientComposites, ComponentUtils, $http) {
console.log('RealmDefaultRolesCtrl');
$scope.realm = realm;
$scope.availableRealmRoles = [];
$scope.availableRealmRoles = angular.copy(roles);
$scope.selectedRealmRoles = [];
$scope.selectedRealmDefRoles = [];
@ -761,85 +760,106 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, $route, Realm, real
$scope.selectedClientRoles = [];
$scope.selectedClientDefRoles = [];
if (!$scope.realm.hasOwnProperty('defaultRoles') || $scope.realm.defaultRoles === null) {
$scope.realm.defaultRoles = [];
for (var j = 0; j < $scope.availableRealmRoles.length; j++) {
if ($scope.availableRealmRoles[j].id === realm.defaultRole.id) {
var realmRole = $scope.availableRealmRoles[j];
var idx = $scope.availableRealmRoles.indexOf(realmRole);
$scope.availableRealmRoles.splice(idx, 1);
break;
}
}
// Populate available roles. Available roles are neither already assigned
for (var i = 0; i < roles.length; i++) {
var item = roles[i].name;
if ($scope.realm.defaultRoles.indexOf(item) < 0) {
$scope.availableRealmRoles.push(item);
$scope.realmMappings = RoleRealmComposites.query({realm : realm.realm, role : realm.defaultRole.id}, function(){
for (var i = 0; i < $scope.realmMappings.length; i++) {
var role = $scope.realmMappings[i];
for (var j = 0; j < $scope.availableRealmRoles.length; j++) {
var realmRole = $scope.availableRealmRoles[j];
if (realmRole.id === role.id) {
var idx = $scope.availableRealmRoles.indexOf(realmRole);
if (idx !== -1) {
$scope.availableRealmRoles.splice(idx, 1);
break;
}
}
}
}
});
$scope.addRealmDefaultRole = function () {
$scope.selectedRealmRolesToAdd = JSON.parse('[' + $scope.selectedRealmRoles + ']');
$http.post(authUrl + '/admin/realms/' + realm.realm + '/roles-by-id/' + realm.defaultRole.id + '/composites',
$scope.selectedRealmRolesToAdd).then(function() {
// Remove selected roles from the Available roles and add them to realm default roles (move from left to right).
for (var i = 0; i < $scope.selectedRealmRoles.length; i++) {
var selectedRole = $scope.selectedRealmRoles[i];
$scope.realm.defaultRoles.push(selectedRole);
var index = $scope.availableRealmRoles.indexOf(selectedRole);
for (var i = 0; i < $scope.selectedRealmRolesToAdd.length; i++) {
var selectedRole = $scope.selectedRealmRolesToAdd[i];
var index = ComponentUtils.findIndexById($scope.availableRealmRoles, selectedRole.id);
if (index > -1) {
$scope.availableRealmRoles.splice(index, 1);
$scope.realmMappings.push(selectedRole);
}
}
$scope.selectedRealmRoles = [];
// Update/save the realm with new default roles.
Realm.update($scope.realm, function () {
Notifications.success("Realm default roles updated.");
$scope.selectedRealmRolesToAdd = [];
Notifications.success("Default roles updated.");
});
};
$scope.deleteRealmDefaultRole = function () {
$scope.selectedClientRolesToRemove = JSON.parse('[' + $scope.selectedRealmDefRoles + ']');
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/roles-by-id/' + realm.defaultRole.id + '/composites',
{data : $scope.selectedClientRolesToRemove, headers : {"content-type" : "application/json"}}).then(function() {
// Remove selected roles from the realm default roles and add them to available roles (move from right to left).
for (var i = 0; i < $scope.selectedRealmDefRoles.length; i++) {
$scope.availableRealmRoles.push($scope.selectedRealmDefRoles[i]);
var index = $scope.realm.defaultRoles.indexOf($scope.selectedRealmDefRoles[i]);
for (var i = 0; i < $scope.selectedClientRolesToRemove.length; i++) {
var selectedRole = $scope.selectedClientRolesToRemove[i];
var index = ComponentUtils.findIndexById($scope.realmMappings, selectedRole.id);
if (index > -1) {
$scope.realm.defaultRoles.splice(index, 1);
$scope.realmMappings.splice(index, 1);
$scope.availableRealmRoles.push(selectedRole);
}
}
$scope.selectedRealmDefRoles = [];
// Update/save the realm with new default roles.
//var realmCopy = angular.copy($scope.realm);
Realm.update($scope.realm, function () {
Notifications.success("Realm default roles updated.");
$scope.selectedClientRolesToRemove = [];
Notifications.success("Default roles updated.");
});
};
$scope.changeClient = function (client) {
$scope.selectedClient = client;
$scope.selectedClientRoles = [];
$scope.selectedClientDefRoles = [];
if (!client || !client.id) {
$scope.selectedClient = null;
return;
}
$scope.selectedClient = client;
$scope.selectedClientRoles = [];
$scope.selectedClientDefRoles = [];
// Populate available roles for selected client
if ($scope.selectedClient) {
ClientRole.query({realm: $scope.realm.realm, client: $scope.selectedClient.id}, function (appDefaultRoles) {
if (!$scope.selectedClient.hasOwnProperty('defaultRoles') || $scope.selectedClient.defaultRoles === null) {
$scope.selectedClient.defaultRoles = [];
$scope.availableClientRoles = ClientRole.query({realm: realm.realm, client: client.id}, function () {
$scope.clientMappings = RoleClientComposites.query({realm : realm.realm, role : realm.defaultRole.id, client : client.id}, function(){
for (var i = 0; i < $scope.clientMappings.length; i++) {
var role = $scope.clientMappings[i];
for (var j = 0; j < $scope.availableClientRoles.length; j++) {
var clientRole = $scope.availableClientRoles[j];
if (clientRole.id === role.id) {
var idx = $scope.availableClientRoles.indexOf(clientRole);
if (idx !== -1) {
$scope.availableClientRoles.splice(idx, 1);
break;
}
$scope.availableClientRoles = [];
console.log('default roles', appDefaultRoles);
for (var i = 0; i < appDefaultRoles.length; i++) {
var roleName = appDefaultRoles[i].name;
if ($scope.selectedClient.defaultRoles.indexOf(roleName) < 0) {
$scope.availableClientRoles.push(roleName);
}
}
}
});
for (var j = 0; j < $scope.availableClientRoles.length; j++) {
if ($scope.availableClientRoles[j] === realm.defaultRole.id) {
var clientRole = $scope.availableClientRoles[j];
var idx = $scope.availableClientRoles.indexof(clientRole);
$scope.availableClientRoles.splice(idx, 1);
break;
}
}
});
@ -850,66 +870,44 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, $route, Realm, real
$scope.addClientDefaultRole = function () {
$scope.selectedClientRolesToAdd = JSON.parse('[' + $scope.selectedClientRoles + ']');
$http.post(authUrl + '/admin/realms/' + realm.realm + '/roles-by-id/' + realm.defaultRole.id + '/composites',
$scope.selectedClientRolesToAdd).then(function() {
// Remove selected roles from the app available roles and add them to app default roles (move from left to right).
for (var i = 0; i < $scope.selectedClientRoles.length; i++) {
var role = $scope.selectedClientRoles[i];
for (var i = 0; i < $scope.selectedClientRolesToAdd.length; i++) {
var selectedRole = $scope.selectedClientRolesToAdd[i];
var idx = $scope.selectedClient.defaultRoles.indexOf(role);
if (idx < 0) {
$scope.selectedClient.defaultRoles.push(role);
}
idx = $scope.availableClientRoles.indexOf(role);
if (idx != -1) {
$scope.availableClientRoles.splice(idx, 1);
var index = ComponentUtils.findIndexById($scope.availableClientRoles, selectedRole.id);
if (index > -1) {
$scope.availableClientRoles.splice(index, 1);
$scope.clientMappings.push(selectedRole);
}
}
$scope.selectedClientRoles = [];
// Update/save the selected client with new default roles.
delete $scope.selectedClient.text;
Client.update({
realm: $scope.realm.realm,
client: $scope.selectedClient.id
}, $scope.selectedClient, function () {
Notifications.success("Your changes have been saved to the client.");
Client.get({realm: realm.realm, client: $scope.selectedClient.id}, function(response) {
response.text = response.clientId;
$scope.changeClient(response);
});
$scope.selectedClientRolesToAdd = [];
Notifications.success("Default roles updated.");
});
};
$scope.rmClientDefaultRole = function () {
// Remove selected roles from the app default roles and add them to app available roles (move from right to left).
for (var i = 0; i < $scope.selectedClientDefRoles.length; i++) {
var role = $scope.selectedClientDefRoles[i];
var idx = $scope.selectedClient.defaultRoles.indexOf(role);
if (idx != -1) {
$scope.selectedClient.defaultRoles.splice(idx, 1);
}
idx = $scope.availableClientRoles.indexOf(role);
if (idx < 0) {
$scope.availableClientRoles.push(role);
$scope.selectedClientRolesToRemove = JSON.parse('[' + $scope.selectedClientDefRoles + ']');
$http.delete(authUrl + '/admin/realms/' + realm.realm + '/roles-by-id/' + realm.defaultRole.id + '/composites',
{data : $scope.selectedClientRolesToRemove, headers : {"content-type" : "application/json"}}).then(function() {
// Remove selected roles from the realm default roles and add them to available roles (move from right to left).
for (var i = 0; i < $scope.selectedClientRolesToRemove.length; i++) {
var selectedRole = $scope.selectedClientRolesToRemove[i];
var index = ComponentUtils.findIndexById($scope.clientMappings, selectedRole.id);
if (index > -1) {
$scope.clientMappings.splice(index, 1);
$scope.availableClientRoles.push(selectedRole);
}
}
$scope.selectedClientDefRoles = [];
// Update/save the selected client with new default roles.
delete $scope.selectedClient.text;
Client.update({
realm: $scope.realm.realm,
client: $scope.selectedClient.id
}, $scope.selectedClient, function () {
Notifications.success("Your changes have been saved to the client.");
Client.get({realm: realm.realm, client: $scope.selectedClient.id}, function(response) {
response.text = response.clientId;
$scope.changeClient(response);
});
$scope.selectedClientRolesToRemove = [];
Notifications.success("Default roles updated.");
});
};
@ -1658,6 +1656,7 @@ module.controller('RoleTabCtrl', function(Dialog, $scope, Current, Notifications
module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications, realm, RoleList, RoleById, filterFilter) {
$scope.realm = realm;
$scope.roles = [];
$scope.defaultRoleName = realm.defaultRole.name;
$scope.query = {
realm: realm.realm,
@ -1701,7 +1700,13 @@ module.controller('RoleListCtrl', function($scope, $route, Dialog, Notifications
$scope.searchQuery();
$scope.determineEditLink = function(role) {
return role.name === $scope.defaultRoleName ? "/realms/" + $scope.realm.realm + "/default-roles" : "/realms/" + $scope.realm.realm + "/roles/" + role.id;
}
$scope.removeRole = function (role) {
if (role.name === $scope.defaultRoleName) return;
Dialog.confirmDelete(role.name, 'role', function () {
RoleById.remove({
realm: realm.realm,

View file

@ -17,8 +17,8 @@
<select id="available" class="form-control overflow-select" multiple size="5"
ng-multiple="true"
ng-model="selectedRealmRoles">
<option ng-repeat="r in availableRealmRoles | orderBy:'toString()'" value="{{r}}" title="{{r.toString()}}">
{{r.toString()}}
<option ng-repeat="r in availableRealmRoles | orderBy:'name'" value="{{r}}" title="{{r.name}}">
{{r.name}}
</option>
</select>
<button ng-disabled="selectedRealmRoles.length == 0" class="btn btn-default" type="submit" ng-click="addRealmDefaultRole()">
@ -31,8 +31,8 @@
<select id="assigned" class="form-control overflow-select" multiple size=5
ng-multiple="true"
ng-model="selectedRealmDefRoles">
<option ng-repeat="r in realm.defaultRoles | orderBy:'toString()'" value="{{r}}" title="{{r.toString()}}">
{{r.toString()}}
<option ng-repeat="r in realmMappings | orderBy:'name'" value="{{r}}" title="{{r.name}}">
{{r.name}}
</option>
</select>
<button ng-disabled="selectedRealmDefRoles.length == 0" class="btn btn-default" type="submit" ng-click="deleteRealmDefaultRole()">
@ -57,8 +57,8 @@
<select id="available-client" class="form-control overflow-select" multiple size="5"
ng-multiple="true"
ng-model="selectedClientRoles">
<option ng-repeat="r in availableClientRoles | orderBy:'toString()'" value="{{r}}" title="{{r.toString()}}">
{{r.toString()}}
<option ng-repeat="r in availableClientRoles | orderBy:'name'" value="{{r}}" title="{{r.name}}">
{{r.name}}
</option>
</select>
<button ng-disabled="selectedClientRoles.length == 0" class="btn btn-default" type="submit" ng-click="addClientDefaultRole()">
@ -71,8 +71,8 @@
<select id="assigned-client" class="form-control overflow-select" multiple size=5
ng-multiple="true"
ng-model="selectedClientDefRoles">
<option ng-repeat="r in selectedClient.defaultRoles | orderBy:'toString()'" value="{{r}}" title="{{r.toString()}}">
{{r.toString()}}
<option ng-repeat="r in clientMappings | orderBy:'name'" value="{{r}}" title="{{r.name}}">
{{r.name}}
</option>
</select>
<button ng-disabled="selectedClientDefRoles.length == 0" class="btn btn-default" type="submit" ng-click="rmClientDefaultRole()">

View file

@ -35,11 +35,11 @@
</thead>
<tbody>
<tr ng-repeat="role in roles">
<td><a href="#/realms/{{realm.realm}}/roles/{{role.id}}">{{role.name}}</a></td>
<td><a href="#{{determineEditLink(role)}}">{{role.name}}</a></td>
<td translate="{{role.composite}}"></td>
<td>{{role.description}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
<td class="kc-action-cell" kc-open="{{determineEditLink(role)}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-click="removeRole(role)" ng-class="{'kc-action-cell-disabled': role.name === defaultRoleName}">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="(roles | filter:{name: query.search}).length == 0">
<td class="text-muted" colspan="4" data-ng-show="searchLoaded && roles.length == 0 && lastSearch != null">{{:: 'no-results' | translate}}</td>

View file

@ -355,6 +355,13 @@ h1 i {
background-image: none;
}
.kc-action-cell-disabled {
background-color: #fafafa;
color: #8b8d8f;
background-image: none;
cursor: not-allowed;
}
.kc-sorter span {
margin-left: 10px;
}