keycloak-aplcache
Changes
services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java 77(+50 -27)
services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java 7(+7 -0)
services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java 4(+4 -0)
services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java 15(+11 -4)
services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java 15(+12 -3)
services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java 4(+4 -0)
themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html 8(+4 -4)
themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html 2(+1 -1)
themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html 2(+1 -1)
themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html 2(+1 -1)
Details
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
index 432aae1..0ade972 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AttackDetectionResource.java
@@ -80,7 +80,8 @@ public class AttackDetectionResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Object> bruteForceUserStatus(@PathParam("username") String username) {
- auth.hasView();
+ auth.requireView();
+
Map<String, Object> data = new HashMap<>();
data.put("disabled", false);
data.put("numFailures", 0);
@@ -110,6 +111,7 @@ public class AttackDetectionResource {
@DELETE
public void clearBruteForceForUser(@PathParam("username") String username) {
auth.requireManage();
+
UsernameLoginFailureModel model = session.sessions().getUserLoginFailure(realm, username.toLowerCase());
if (model != null) {
session.sessions().removeUserLoginFailure(realm, username);
@@ -127,6 +129,7 @@ public class AttackDetectionResource {
@DELETE
public void clearAllBruteForce() {
auth.requireManage();
+
session.sessions().removeAllUserLoginFailures(realm);
adminEvent.operation(OperationType.DELETE).success();
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 1e42aad..a0034e9 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -101,7 +101,8 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getFormProviders() {
- this.auth.requireView();
+ auth.requireView();
+
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAuthenticator.class);
return buildProviderMetadata(factories);
}
@@ -116,7 +117,8 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getAuthenticatorProviders() {
- this.auth.requireView();
+ auth.requireView();
+
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(Authenticator.class);
return buildProviderMetadata(factories);
}
@@ -131,7 +133,8 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getClientAuthenticatorProviders() {
- this.auth.requireView();
+ auth.requireAny();
+
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class);
return buildProviderMetadata(factories);
}
@@ -160,7 +163,8 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getFormActionProviders() {
- this.auth.requireView();
+ auth.requireView();
+
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAction.class);
return buildProviderMetadata(factories);
}
@@ -176,7 +180,8 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<AuthenticationFlowRepresentation> getFlows() {
- this.auth.requireView();
+ auth.requireAny();
+
List<AuthenticationFlowRepresentation> flows = new LinkedList<>();
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
if (flow.isTopLevel()) {
@@ -197,7 +202,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response createFlow(AuthenticationFlowRepresentation flow) {
- this.auth.requireManage();
+ auth.requireManage();
if (flow.getAlias() == null || flow.getAlias().isEmpty()) {
return ErrorResponse.exists("Failed to create flow with empty alias name");
@@ -222,7 +227,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public AuthenticationFlowRepresentation getFlow(@PathParam("id") String id) {
- this.auth.requireView();
+ auth.requireView();
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id);
if (flow == null) {
@@ -240,7 +245,7 @@ public class AuthenticationManagementResource {
@DELETE
@NoCache
public void deleteFlow(@PathParam("id") String id) {
- this.auth.requireView();
+ auth.requireManage();
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id);
if (flow == null) {
@@ -273,7 +278,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response copy(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
- this.auth.requireManage();
+ auth.requireManage();
String newName = data.get("newName");
if (realm.getFlowByAlias(newName) != null) {
@@ -329,7 +334,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void addExecutionFlow(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
- this.auth.requireManage();
+ auth.requireManage();
AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
if (parentFlow == null) {
@@ -377,7 +382,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void addExecution(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
- this.auth.requireManage();
+ auth.requireManage();
AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
if (parentFlow == null) {
@@ -419,7 +424,7 @@ public class AuthenticationManagementResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Response getExecutions(@PathParam("flowAlias") String flowAlias) {
- this.auth.requireView();
+ auth.requireView();
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
if (flow == null) {
@@ -495,7 +500,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void updateExecutions(@PathParam("flowAlias") String flowAlias, AuthenticationExecutionInfoRepresentation rep) {
- this.auth.requireManage();
+ auth.requireManage();
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
if (flow == null) {
@@ -525,7 +530,8 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response addExecution(AuthenticationExecutionRepresentation execution) {
- this.auth.requireManage();
+ auth.requireManage();
+
AuthenticationExecutionModel model = RepresentationToModel.toModel(realm, execution);
AuthenticationFlowModel parentFlow = getParentFlow(model);
if (parentFlow.isBuiltIn()) {
@@ -557,7 +563,7 @@ public class AuthenticationManagementResource {
@POST
@NoCache
public void raisePriority(@PathParam("executionId") String execution) {
- this.auth.requireManage();
+ auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -601,7 +607,7 @@ public class AuthenticationManagementResource {
@POST
@NoCache
public void lowerPriority(@PathParam("executionId") String execution) {
- this.auth.requireManage();
+ auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -639,7 +645,7 @@ public class AuthenticationManagementResource {
@DELETE
@NoCache
public void removeExecution(@PathParam("executionId") String execution) {
- this.auth.requireManage();
+ auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -673,7 +679,7 @@ public class AuthenticationManagementResource {
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response newExecutionConfig(@PathParam("executionId") String execution, AuthenticatorConfigRepresentation json) {
- this.auth.requireManage();
+ auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
@@ -699,7 +705,8 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id) {
- this.auth.requireView();
+ auth.requireView();
+
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
@@ -718,6 +725,8 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<Map<String, String>> getUnregisteredRequiredActions() {
+ auth.requireManage();
+
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class);
List<Map<String, String>> unregisteredList = new LinkedList<>();
for (ProviderFactory factory : factories) {
@@ -750,6 +759,8 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public void registereRequiredAction(Map<String, String> data) {
+ auth.requireManage();
+
String providerId = data.get("providerId");
String name = data.get("name");
RequiredActionProviderModel requiredAction = new RequiredActionProviderModel();
@@ -772,6 +783,8 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RequiredActionProviderRepresentation> getRequiredActions() {
+ auth.requireAny();
+
List<RequiredActionProviderRepresentation> list = new LinkedList<>();
for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
RequiredActionProviderRepresentation rep = toRepresentation(model);
@@ -799,6 +812,8 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public RequiredActionProviderRepresentation getRequiredAction(@PathParam("alias") String alias) {
+ auth.requireView();
+
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action");
@@ -817,7 +832,8 @@ public class AuthenticationManagementResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateRequiredAction(@PathParam("alias") String alias, RequiredActionProviderRepresentation rep) {
- this.auth.requireManage();
+ auth.requireManage();
+
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action");
@@ -840,7 +856,8 @@ public class AuthenticationManagementResource {
@Path("required-actions/{alias}")
@DELETE
public void updateRequiredAction(@PathParam("alias") String alias) {
- this.auth.requireManage();
+ auth.requireManage();
+
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action.");
@@ -856,7 +873,8 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigInfoRepresentation getAuthenticatorConfigDescription(@PathParam("providerId") String providerId) {
- this.auth.requireView();
+ auth.requireView();
+
ConfigurableAuthenticatorFactory factory = CredentialHelper.getConfigurableAuthenticatorFactory(session, providerId);
if (factory == null) {
throw new NotFoundException("Could not find authenticator provider");
@@ -892,7 +910,8 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public Map<String, List<ConfigPropertyRepresentation>> getPerClientConfigDescription() {
- this.auth.requireView();
+ auth.requireAny();
+
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(ClientAuthenticator.class);
Map<String, List<ConfigPropertyRepresentation>> toReturn = new HashMap<>();
@@ -921,7 +940,8 @@ public class AuthenticationManagementResource {
@POST
@NoCache
public Response createAuthenticatorConfig(AuthenticatorConfigRepresentation rep) {
- this.auth.requireManage();
+ auth.requireManage();
+
AuthenticatorConfigModel config = realm.addAuthenticatorConfig(RepresentationToModel.toModel(rep));
return Response.created(uriInfo.getAbsolutePathBuilder().path(config.getId()).build()).build();
}
@@ -935,7 +955,8 @@ public class AuthenticationManagementResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigRepresentation getAuthenticatorConfig(@PathParam("id") String id) {
- this.auth.requireView();
+ auth.requireView();
+
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
@@ -952,7 +973,8 @@ public class AuthenticationManagementResource {
@DELETE
@NoCache
public void removeAuthenticatorConfig(@PathParam("id") String id) {
- this.auth.requireManage();
+ auth.requireManage();
+
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
@@ -981,7 +1003,8 @@ public class AuthenticationManagementResource {
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public void updateAuthenticatorConfig(@PathParam("id") String id, AuthenticatorConfigRepresentation rep) {
- this.auth.requireManage();
+ auth.requireManage();
+
AuthenticatorConfigModel exists = realm.getAuthenticatorConfigById(id);
if (exists == null) {
throw new NotFoundException("Could not find authenticator config");
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
index bea79a0..09186f1 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientAttributeCertificateResource.java
@@ -91,6 +91,8 @@ public class ClientAttributeCertificateResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation getKeyInfo() {
+ auth.requireView();
+
CertificateRepresentation info = new CertificateRepresentation();
info.setCertificate(client.getAttribute(certificateAttribute));
info.setPrivateKey(client.getAttribute(privateAttribute));
@@ -132,6 +134,8 @@ public class ClientAttributeCertificateResource {
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation uploadJks(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+ auth.requireManage();
+
CertificateRepresentation info = getCertFromRequest(uriInfo, input);
if (info.getPrivateKey() != null) {
@@ -163,6 +167,8 @@ public class ClientAttributeCertificateResource {
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public CertificateRepresentation uploadJksCertificate(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+ auth.requireManage();
+
CertificateRepresentation info = getCertFromRequest(uriInfo, input);
if (info.getCertificate() != null) {
@@ -239,6 +245,7 @@ public class ClientAttributeCertificateResource {
@Consumes(MediaType.APPLICATION_JSON)
public byte[] getKeystore(final KeyStoreConfig config) {
auth.requireView();
+
if (config.getFormat() != null && !config.getFormat().equals("JKS") && !config.getFormat().equals("PKCS12")) {
throw new NotAcceptableException("Only support jks or pkcs12 format.");
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
index 3be39f8..ff766d6 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
@@ -91,6 +91,8 @@ public class ClientInitialAccessResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<ClientInitialAccessPresentation> list() {
+ auth.requireView();
+
List<ClientInitialAccessModel> models = session.sessions().listClientInitialAccess(realm);
List<ClientInitialAccessPresentation> reps = new LinkedList<>();
for (ClientInitialAccessModel m : models) {
@@ -103,6 +105,8 @@ public class ClientInitialAccessResource {
@DELETE
@Path("{id}")
public void delete(final @PathParam("id") String id) {
+ auth.requireManage();
+
session.sessions().removeClientInitialAccessModel(realm, id);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
index 09c3dee..2d83e84 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java
@@ -147,6 +147,7 @@ public class ClientResource {
@Produces(MediaType.APPLICATION_JSON)
public ClientRepresentation getClient() {
auth.requireView();
+
return ModelToRepresentation.toRepresentation(client);
}
@@ -165,6 +166,8 @@ public class ClientResource {
@NoCache
@Path("installation/providers/{providerId}")
public Response getInstallationProvider(@PathParam("providerId") String providerId) {
+ auth.requireView();
+
ClientInstallationProvider provider = session.getProvider(ClientInstallationProvider.class, providerId);
if (provider == null) throw new NotFoundException("Unknown Provider");
return provider.generateInstallation(session, realm, client, keycloak.getBaseUri(uriInfo));
@@ -178,6 +181,7 @@ public class ClientResource {
@NoCache
public void deleteClient() {
auth.requireManage();
+
new ClientManager(new RealmManager(session)).removeClient(realm, client);
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
}
@@ -290,6 +294,7 @@ public class ClientResource {
@POST
public GlobalRequestResult pushRevocation() {
auth.requireManage();
+
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
return new ResourceAdminManager(session).pushClientRevocationPolicy(uriInfo.getRequestUri(), realm, client);
@@ -312,6 +317,7 @@ public class ClientResource {
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Long> getApplicationSessionCount() {
auth.requireView();
+
Map<String, Long> map = new HashMap<>();
map.put("count", session.sessions().getActiveUserSessions(client.getRealm(), client));
return map;
@@ -332,6 +338,7 @@ public class ClientResource {
@Produces(MediaType.APPLICATION_JSON)
public List<UserSessionRepresentation> getUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
auth.requireView();
+
firstResult = firstResult != null ? firstResult : -1;
maxResults = maxResults != null ? maxResults : -1;
List<UserSessionRepresentation> sessions = new ArrayList<UserSessionRepresentation>();
@@ -359,6 +366,7 @@ public class ClientResource {
@Produces(MediaType.APPLICATION_JSON)
public Map<String, Long> getOfflineSessionCount() {
auth.requireView();
+
Map<String, Long> map = new HashMap<>();
map.put("count", session.sessions().getOfflineSessionsCount(client.getRealm(), client));
return map;
@@ -379,6 +387,7 @@ public class ClientResource {
@Produces(MediaType.APPLICATION_JSON)
public List<UserSessionRepresentation> getOfflineUserSessions(@QueryParam("first") Integer firstResult, @QueryParam("max") Integer maxResults) {
auth.requireView();
+
firstResult = firstResult != null ? firstResult : -1;
maxResults = maxResults != null ? maxResults : -1;
List<UserSessionRepresentation> sessions = new ArrayList<UserSessionRepresentation>();
@@ -412,6 +421,7 @@ public class ClientResource {
@Consumes(MediaType.APPLICATION_JSON)
public void registerNode(Map<String, String> formParams) {
auth.requireManage();
+
String node = formParams.get("node");
if (node == null) {
throw new BadRequestException("Node not found in params");
@@ -431,6 +441,7 @@ public class ClientResource {
@NoCache
public void unregisterNode(final @PathParam("node") String node) {
auth.requireManage();
+
if (logger.isDebugEnabled()) logger.debug("Unregister node: " + node);
Integer time = client.getRegisteredNodes().get(node);
@@ -453,6 +464,7 @@ public class ClientResource {
@NoCache
public GlobalRequestResult testNodesAvailable() {
auth.requireManage();
+
logger.debug("Test availability of cluster nodes");
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
return new ResourceAdminManager(session).testNodesAvailability(uriInfo.getRequestUri(), realm, client);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
index 9acca03..768bf1a 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplateResource.java
@@ -123,6 +123,7 @@ public class ClientTemplateResource {
@Produces(MediaType.APPLICATION_JSON)
public ClientTemplateRepresentation getClient() {
auth.requireView();
+
return ModelToRepresentation.toRepresentation(template);
}
@@ -134,6 +135,7 @@ public class ClientTemplateResource {
@NoCache
public void deleteClientTemplate() {
auth.requireManage();
+
realm.removeClientTemplate(template.getId());
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java
index 429ba5e..36e0179 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientTemplatesResource.java
@@ -75,7 +75,7 @@ public class ClientTemplatesResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<ClientTemplateRepresentation> getClientTemplates() {
- auth.requireAny();
+ auth.requireView();
List<ClientTemplateRepresentation> rep = new ArrayList<>();
List<ClientTemplateModel> clientModels = realm.getClientTemplates();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
index fb0b104..39e3d4a 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java
@@ -69,7 +69,8 @@ public class GroupsResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<GroupRepresentation> getGroups() {
- this.auth.requireView();
+ auth.requireView();
+
return ModelToRepresentation.toGroupHierarchy(realm, false);
}
@@ -81,7 +82,8 @@ public class GroupsResource {
*/
@Path("{id}")
public GroupResource getGroupById(@PathParam("id") String id) {
- this.auth.requireView();
+ auth.requireView();
+
GroupModel group = realm.getGroupById(id);
if (group == null) {
throw new NotFoundException("Could not find group by id");
@@ -101,7 +103,8 @@ public class GroupsResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response addTopLevelGroup(GroupRepresentation rep) {
- this.auth.requireManage();
+ auth.requireManage();
+
GroupModel child = null;
Response.ResponseBuilder builder = Response.status(204);
if (rep.getId() != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
index b673d83..9e2470b 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ProtocolMappersResource.java
@@ -28,6 +28,7 @@ import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.services.ErrorResponse;
import org.keycloak.services.ServicesLogger;
+import org.keycloak.services.resources.admin.RealmAuth.Resource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
@@ -71,7 +72,7 @@ public class ProtocolMappersResource {
this.client = client;
this.adminEvent = adminEvent;
- auth.init(RealmAuth.Resource.USER);
+ auth.init(Resource.CLIENT);
}
/**
@@ -85,7 +86,8 @@ public class ProtocolMappersResource {
@Path("protocol/{protocol}")
@Produces(MediaType.APPLICATION_JSON)
public List<ProtocolMapperRepresentation> getMappersPerProtocol(@PathParam("protocol") String protocol) {
- auth.requireView();
+ auth.requireAny();
+
List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>();
for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
if (mapper.getProtocol().equals(protocol)) mappers.add(ModelToRepresentation.toRepresentation(mapper));
@@ -127,6 +129,7 @@ public class ProtocolMappersResource {
@Consumes(MediaType.APPLICATION_JSON)
public void createMapper(List<ProtocolMapperRepresentation> reps) {
auth.requireManage();
+
ProtocolMapperModel model = null;
for (ProtocolMapperRepresentation rep : reps) {
model = RepresentationToModel.toModel(rep);
@@ -145,7 +148,8 @@ public class ProtocolMappersResource {
@Path("models")
@Produces(MediaType.APPLICATION_JSON)
public List<ProtocolMapperRepresentation> getMappers() {
- auth.requireView();
+ auth.requireAny();
+
List<ProtocolMapperRepresentation> mappers = new LinkedList<ProtocolMapperRepresentation>();
for (ProtocolMapperModel mapper : client.getProtocolMappers()) {
mappers.add(ModelToRepresentation.toRepresentation(mapper));
@@ -164,7 +168,8 @@ public class ProtocolMappersResource {
@Path("models/{id}")
@Produces(MediaType.APPLICATION_JSON)
public ProtocolMapperRepresentation getMapperById(@PathParam("id") String id) {
- auth.requireView();
+ auth.requireAny();
+
ProtocolMapperModel model = client.getProtocolMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
return ModelToRepresentation.toRepresentation(model);
@@ -182,6 +187,7 @@ public class ProtocolMappersResource {
@Consumes(MediaType.APPLICATION_JSON)
public void update(@PathParam("id") String id, ProtocolMapperRepresentation rep) {
auth.requireManage();
+
ProtocolMapperModel model = client.getProtocolMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
model = RepresentationToModel.toModel(rep);
@@ -199,6 +205,7 @@ public class ProtocolMappersResource {
@Path("models/{id}")
public void delete(@PathParam("id") String id) {
auth.requireManage();
+
ProtocolMapperModel model = client.getProtocolMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
client.removeProtocolMapper(model);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 058b6e4..67bd7a3 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -54,6 +54,7 @@ import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.PartialImportRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
@@ -64,6 +65,7 @@ import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.ServicesLogger;
import org.keycloak.services.managers.UsersSyncManager;
import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.resources.admin.RealmAuth.Resource;
import org.keycloak.timer.TimerProvider;
import javax.ws.rs.Consumes;
@@ -127,6 +129,7 @@ public class RealmAdminResource {
this.adminEvent = adminEvent.realm(realm);
auth.init(RealmAuth.Resource.REALM);
+ auth.requireAny();
}
/**
@@ -139,6 +142,8 @@ public class RealmAdminResource {
@POST
@Produces(MediaType.APPLICATION_JSON)
public ClientRepresentation convertClientDescription(String description) {
+ auth.requireManage();
+
for (ProviderFactory<ClientDescriptionConverter> factory : session.getKeycloakSessionFactory().getProviderFactories(ClientDescriptionConverter.class)) {
if (((ClientDescriptionConverterFactory) factory).isSupported(description)) {
return factory.create(session).convertToInternal(description);
@@ -224,6 +229,13 @@ public class RealmAdminResource {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realm.getName());
+
+ if (auth.init(Resource.IDENTITY_PROVIDER).hasView()) {
+ RealmRepresentation r = ModelToRepresentation.toRepresentation(realm, false);
+ rep.setIdentityProviders(r.getIdentityProviders());
+ rep.setIdentityProviderMappers(r.getIdentityProviderMappers());
+ }
+
return rep;
}
}
@@ -337,6 +349,7 @@ public class RealmAdminResource {
@POST
public GlobalRequestResult pushRevocation() {
auth.requireManage();
+
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
return new ResourceAdminManager(session).pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
}
@@ -350,6 +363,7 @@ public class RealmAdminResource {
@POST
public GlobalRequestResult logoutAll() {
auth.init(RealmAuth.Resource.USER).requireManage();
+
session.sessions().removeUserSessions(realm);
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
return new ResourceAdminManager(session).logoutAll(uriInfo.getRequestUri(), realm);
@@ -365,6 +379,7 @@ public class RealmAdminResource {
@DELETE
public void deleteSession(@PathParam("session") String sessionId) {
auth.init(RealmAuth.Resource.USER).requireManage();
+
UserSessionModel userSession = session.sessions().getUserSession(realm, sessionId);
if (userSession == null) throw new NotFoundException("Sesssion not found");
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
@@ -386,6 +401,7 @@ public class RealmAdminResource {
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, String>> getClientSessionStats() {
auth.requireView();
+
List<Map<String, String>> data = new LinkedList<Map<String, String>>();
for (ClientModel client : realm.getClients()) {
long size = session.sessions().getActiveUserSessions(client.getRealm(), client);
@@ -691,7 +707,8 @@ public class RealmAdminResource {
@Produces(MediaType.APPLICATION_JSON)
@Path("default-groups")
public List<GroupRepresentation> getDefaultGroups() {
- this.auth.requireView();
+ auth.requireView();
+
List<GroupRepresentation> defaults = new LinkedList<>();
for (GroupModel group : realm.getDefaultGroups()) {
defaults.add(ModelToRepresentation.toRepresentation(group, false));
@@ -702,7 +719,8 @@ public class RealmAdminResource {
@NoCache
@Path("default-groups/{groupId}")
public void addDefaultGroup(@PathParam("groupId") String groupId) {
- this.auth.requireManage();
+ auth.requireManage();
+
GroupModel group = realm.getGroupById(groupId);
if (group == null) {
throw new NotFoundException("Group not found");
@@ -714,7 +732,8 @@ public class RealmAdminResource {
@NoCache
@Path("default-groups/{groupId}")
public void removeDefaultGroup(@PathParam("groupId") String groupId) {
- this.auth.requireManage();
+ auth.requireManage();
+
GroupModel group = realm.getGroupById(groupId);
if (group == null) {
throw new NotFoundException("Group not found");
@@ -736,7 +755,8 @@ public class RealmAdminResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public GroupRepresentation getGroupByPath(@PathParam("path") String path) {
- this.auth.requireView();
+ auth.requireView();
+
GroupModel found = KeycloakModelUtils.findGroupByPath(realm, path);
if (found == null) {
throw new NotFoundException("Group path does not exist");
@@ -756,6 +776,7 @@ public class RealmAdminResource {
@Consumes(MediaType.APPLICATION_JSON)
public Response partialImport(PartialImportRepresentation rep) {
auth.requireManage();
+
PartialImportManager partialImport = new PartialImportManager(rep, session, realm, adminEvent);
return partialImport.saveResources();
}
@@ -768,6 +789,7 @@ public class RealmAdminResource {
@POST
public void clearRealmCache() {
auth.requireManage();
+
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
CacheRealmProvider cache = session.getProvider(CacheRealmProvider.class);
if (cache != null) {
@@ -783,6 +805,7 @@ public class RealmAdminResource {
@POST
public void clearUserCache() {
auth.requireManage();
+
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
CacheUserProvider cache = session.getProvider(CacheUserProvider.class);
if (cache != null) {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
index 07efd05..26b1a4a 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleByIdResource.java
@@ -79,8 +79,9 @@ public class RoleByIdResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public RoleRepresentation getRole(final @PathParam("role-id") String id) {
+ auth.requireAny();
+
RoleModel roleModel = getRoleModel(id);
- auth.requireView();
return getRole(roleModel);
}
@@ -111,8 +112,9 @@ public class RoleByIdResource extends RoleResource {
@DELETE
@NoCache
public void deleteRole(final @PathParam("role-id") String id) {
- RoleModel role = getRoleModel(id);
auth.requireManage();
+
+ RoleModel role = getRoleModel(id);
deleteRole(role);
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
}
@@ -127,8 +129,9 @@ public class RoleByIdResource extends RoleResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateRole(final @PathParam("role-id") String id, final RoleRepresentation rep) {
- RoleModel role = getRoleModel(id);
auth.requireManage();
+
+ RoleModel role = getRoleModel(id);
updateRole(rep, role);
adminEvent.operation(OperationType.UPDATE).resourcePath(uriInfo).representation(rep).success();
}
@@ -143,8 +146,9 @@ public class RoleByIdResource extends RoleResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
public void addComposites(final @PathParam("role-id") String id, List<RoleRepresentation> roles) {
- RoleModel role = getRoleModel(id);
auth.requireManage();
+
+ RoleModel role = getRoleModel(id);
addComposites(adminEvent, uriInfo, roles, role);
}
@@ -161,6 +165,7 @@ public class RoleByIdResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getRoleComposites(final @PathParam("role-id") String id) {
+ auth.requireAny();
if (logger.isDebugEnabled()) logger.debug("*** getRoleComposites: '" + id + "'");
RoleModel role = getRoleModel(id);
@@ -179,8 +184,9 @@ public class RoleByIdResource extends RoleResource {
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getRealmRoleComposites(final @PathParam("role-id") String id) {
+ auth.requireAny();
+
RoleModel role = getRoleModel(id);
- auth.requireView();
return getRealmRoleComposites(role);
}
@@ -197,8 +203,9 @@ public class RoleByIdResource extends RoleResource {
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getClientRoleComposites(final @PathParam("role-id") String id,
final @PathParam("client") String client) {
+ auth.requireAny();
+
RoleModel role = getRoleModel(id);
- auth.requireView();
ClientModel clientModel = realm.getClientById(client);
if (clientModel == null) {
throw new NotFoundException("Could not find client");
@@ -219,8 +226,9 @@ public class RoleByIdResource extends RoleResource {
@Produces(MediaType.APPLICATION_JSON)
public Set<RoleRepresentation> getClientByIdRoleComposites(final @PathParam("role-id") String role,
final @PathParam("client") String client) {
+ auth.requireAny();
+
RoleModel roleModel = getRoleModel(role);
- auth.requireView();
ClientModel clientModel = realm.getClientById(client);
if (clientModel == null) {
throw new NotFoundException("Could not find client");
@@ -239,8 +247,9 @@ public class RoleByIdResource extends RoleResource {
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void deleteComposites(final @PathParam("role-id") String id, List<RoleRepresentation> roles) {
- RoleModel role = getRoleModel(id);
auth.requireManage();
+
+ RoleModel role = getRoleModel(id);
deleteComposites(roles, role);
adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).representation(roles).success();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
index 29b4ae6..ecfc307 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProviderResource.java
@@ -100,6 +100,7 @@ public class UserFederationProviderResource {
@Consumes(MediaType.APPLICATION_JSON)
public void updateProviderInstance(UserFederationProviderRepresentation rep) {
auth.requireManage();
+
String displayName = rep.getDisplayName();
if (displayName != null && displayName.trim().equals("")) {
displayName = null;
@@ -129,6 +130,7 @@ public class UserFederationProviderResource {
@Produces(MediaType.APPLICATION_JSON)
public UserFederationProviderRepresentation getProviderInstance() {
auth.requireView();
+
return ModelToRepresentation.toRepresentation(this.federationProviderModel);
}
@@ -157,9 +159,10 @@ public class UserFederationProviderResource {
@Path("sync")
@NoCache
public UserFederationSyncResult syncUsers(@QueryParam("action") String action) {
- logger.debug("Syncing users");
auth.requireManage();
+ logger.debug("Syncing users");
+
UsersSyncManager syncManager = new UsersSyncManager();
UserFederationSyncResult syncResult;
if ("triggerFullSync".equals(action)) {
@@ -183,7 +186,8 @@ public class UserFederationProviderResource {
@Path("mapper-types")
@NoCache
public Map<String, UserFederationMapperTypeRepresentation> getMapperTypes() {
- this.auth.requireView();
+ auth.requireView();
+
KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory();
Map<String, UserFederationMapperTypeRepresentation> types = new HashMap<>();
List<ProviderFactory> factories = sessionFactory.getProviderFactories(UserFederationMapper.class);
@@ -226,7 +230,8 @@ public class UserFederationProviderResource {
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<UserFederationMapperRepresentation> getMappers() {
- this.auth.requireView();
+ auth.requireView();
+
List<UserFederationMapperRepresentation> mappers = new LinkedList<>();
for (UserFederationMapperModel model : realm.getUserFederationMappersByFederationProvider(this.federationProviderModel.getId())) {
mappers.add(ModelToRepresentation.toRepresentation(realm, model));
@@ -265,6 +270,7 @@ public class UserFederationProviderResource {
@Consumes(MediaType.APPLICATION_JSON)
public Response addMapper(UserFederationMapperRepresentation mapper) {
auth.requireManage();
+
UserFederationMapperModel model = RepresentationToModel.toModel(realm, mapper);
validateModel(model);
@@ -290,6 +296,7 @@ public class UserFederationProviderResource {
@Produces(MediaType.APPLICATION_JSON)
public UserFederationMapperRepresentation getMapperById(@PathParam("id") String id) {
auth.requireView();
+
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
return ModelToRepresentation.toRepresentation(realm, model);
@@ -307,6 +314,7 @@ public class UserFederationProviderResource {
@Consumes(MediaType.APPLICATION_JSON)
public void update(@PathParam("id") String id, UserFederationMapperRepresentation rep) {
auth.requireManage();
+
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
model = RepresentationToModel.toModel(realm, rep);
@@ -328,6 +336,7 @@ public class UserFederationProviderResource {
@Path("mappers/{id}")
public void delete(@PathParam("id") String id) {
auth.requireManage();
+
UserFederationMapperModel model = realm.getUserFederationMapperById(id);
if (model == null) throw new NotFoundException("Model not found");
realm.removeUserFederationMapper(model);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java
index 9c9ac51..d7e66b1 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UserFederationProvidersResource.java
@@ -134,6 +134,7 @@ public class UserFederationProvidersResource {
@Produces(MediaType.APPLICATION_JSON)
public List<UserFederationProviderFactoryRepresentation> getProviders() {
auth.requireView();
+
List<UserFederationProviderFactoryRepresentation> providers = new LinkedList<UserFederationProviderFactoryRepresentation>();
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(UserFederationProvider.class)) {
UserFederationProviderFactoryRepresentation rep = new UserFederationProviderFactoryRepresentation();
@@ -155,6 +156,7 @@ public class UserFederationProvidersResource {
@Produces(MediaType.APPLICATION_JSON)
public UserFederationProviderFactoryRepresentation getProvider(@PathParam("id") String id) {
auth.requireView();
+
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(UserFederationProvider.class)) {
if (!factory.getId().equals(id)) {
continue;
@@ -192,6 +194,7 @@ public class UserFederationProvidersResource {
@Consumes(MediaType.APPLICATION_JSON)
public Response createProviderInstance(UserFederationProviderRepresentation rep) {
auth.requireManage();
+
String displayName = rep.getDisplayName();
if (displayName != null && displayName.trim().equals("")) {
displayName = null;
@@ -225,6 +228,7 @@ public class UserFederationProvidersResource {
@NoCache
public List<UserFederationProviderRepresentation> getUserFederationInstances() {
auth.requireManage();
+
List<UserFederationProviderRepresentation> reps = new LinkedList<UserFederationProviderRepresentation>();
for (UserFederationProviderModel model : realm.getUserFederationProviders()) {
UserFederationProviderRepresentation rep = ModelToRepresentation.toRepresentation(model);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 5427a1a..169eef7 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -313,6 +313,7 @@ public class UsersResource {
public Map<String, Object> impersonate(final @PathParam("id") String id) {
auth.init(RealmAuth.Resource.IMPERSONATION);
auth.requireManage();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -357,6 +358,7 @@ public class UsersResource {
@Produces(MediaType.APPLICATION_JSON)
public List<UserSessionRepresentation> getSessions(final @PathParam("id") String id) {
auth.requireView();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -382,6 +384,7 @@ public class UsersResource {
@Produces(MediaType.APPLICATION_JSON)
public List<UserSessionRepresentation> getSessions(final @PathParam("id") String id, final @PathParam("clientId") String clientId) {
auth.requireView();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -420,6 +423,7 @@ public class UsersResource {
@Produces(MediaType.APPLICATION_JSON)
public List<FederatedIdentityRepresentation> getFederatedIdentity(final @PathParam("id") String id) {
auth.requireView();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -456,6 +460,7 @@ public class UsersResource {
@NoCache
public Response addFederatedIdentity(final @PathParam("id") String id, final @PathParam("provider") String provider, FederatedIdentityRepresentation rep) {
auth.requireManage();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -481,6 +486,7 @@ public class UsersResource {
@NoCache
public void removeFederatedIdentity(final @PathParam("id") String id, final @PathParam("provider") String provider) {
auth.requireManage();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -503,6 +509,7 @@ public class UsersResource {
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, Object>> getConsents(final @PathParam("id") String id) {
auth.requireView();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -555,6 +562,7 @@ public class UsersResource {
@NoCache
public void revokeConsent(final @PathParam("id") String id, final @PathParam("client") String clientId) {
auth.requireManage();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
@@ -586,6 +594,7 @@ public class UsersResource {
@POST
public void logout(final @PathParam("id") String id) {
auth.requireManage();
+
UserModel user = session.users().getUserById(id, realm);
if (user == null) {
throw new NotFoundException("User not found");
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java
index aebc6c1..35f3849 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java
@@ -72,6 +72,9 @@ public class ImpersonationTest {
UserModel masterImpersonator = manager.getSession().users().addUser(adminstrationRealm, "master-impersonator");
masterImpersonator.setEnabled(true);
ClientModel adminRealmClient = adminstrationRealm.getClientByClientId(KeycloakModelUtils.getMasterRealmAdminApplicationClientId(appRealm.getName()));
+ RoleModel masterManageUsersRole = adminRealmClient.getRole(AdminRoles.MANAGE_USERS);
+ masterImpersonator.grantRole(masterManageUsersRole);
+
RoleModel masterImpersonatorRole = adminRealmClient.getRole(ImpersonationConstants.IMPERSONATION_ROLE);
masterImpersonator.grantRole(masterImpersonatorRole);
}
@@ -84,6 +87,10 @@ public class ImpersonationTest {
UserModel impersonator = manager.getSession().users().addUser(appRealm, "impersonator");
impersonator.setEnabled(true);
ClientModel appRealmClient = appRealm.getClientByClientId(Constants.REALM_MANAGEMENT_CLIENT_ID);
+
+ RoleModel masterManageUsersRole = appRealmClient.getRole(AdminRoles.MANAGE_USERS);
+ impersonator.grantRole(masterManageUsersRole);
+
RoleModel realmImpersonatorRole = appRealmClient.getRole(ImpersonationConstants.IMPERSONATION_ROLE);
impersonator.grantRole(realmImpersonatorRole);
}
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 8bac7bf..eb215e0 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -40,6 +40,10 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
return getAccess('view-events') || getAccess('manage-events') || this.manageClients;
},
+ get viewIdentityProviders() {
+ return getAccess('view-identity-providers') || getAccess('manage-identity-providers') || this.manageIdentityProviders;
+ },
+
get manageRealm() {
return getAccess('manage-realm');
},
@@ -55,6 +59,11 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location
get manageEvents() {
return getAccess('manage-events');
},
+
+ get manageIdentityProviders() {
+ return getAccess('manage-identity-providers');
+ },
+
get impersonation() {
return getAccess('impersonation');
}
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html
index 97c1fbb..11b3dcf 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-initial-access.html
@@ -15,7 +15,7 @@
</div>
</div>
- <div class="pull-right" data-ng-show="access.manageRealm">
+ <div class="pull-right" data-ng-show="access.manageClients">
<a id="createClient" class="btn btn-default" href="#/realms/{{realm.realm}}/client-initial-access/create">{{:: 'create' | translate}}</a>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
index 9347274..7f796e9 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
@@ -42,7 +42,7 @@
</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'edit' | translate}}</td>
<td class="kc-action-cell" data-ng-click="exportClient(client)">{{:: 'export' | translate}}</td>
- <td class="kc-action-cell" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="(clients | filter:search).length == 0">
<td class="text-muted" colspan="3" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
index c07e732..ba9b9bf 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
@@ -55,7 +55,7 @@
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/mappers/{{mapper.id}}">{{:: 'edit' | translate}}</td>
- <td class="kc-action-cell" data-ng-click="removeMapper(mapper)">{{:: 'delete' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeMapper(mapper)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="mappers.length == 0">
<td>{{:: 'no-mappers-available' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
index 37dde5c..38560e8 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
@@ -7,7 +7,7 @@
<kc-tabs-client></kc-tabs-client>
- <form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients">
<fieldset class="border-top">
<div class="form-group">
<label class="col-md-2 control-label" for="notBefore">{{:: 'not-before' | translate}}</label>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
index 52154d7..da4d49d 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
@@ -29,7 +29,7 @@
<td translate="{{role.composite}}"></td>
<td>{{role.description}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{:: 'edit' | translate}}</td>
- <td class="kc-action-cell" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="access.manageClients" data-ng-click="removeRole(role)">{{:: 'delete' | translate}}</td>
</tr>
<tr data-ng-show="!roles || roles.length == 0">
<td>{{:: 'no-client-roles-available' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html
index 5852634..cd2964d 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html
@@ -7,11 +7,11 @@
<li class="active" data-ng-hide="create">{{mapper.name|capitalize}}</li>
</ol>
- <h1 data-ng-hide="create">{{mapper.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageRealm"
+ <h1 data-ng-hide="create">{{mapper.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageIdentityProviders"
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
<h1 data-ng-show="create">{{:: 'add-identity-provider-mapper' | translate}}</h1>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageIdentityProviders">
<fieldset>
<div class="form-group clearfix" data-ng-show="!create">
<label class="col-md-2 control-label" for="mapperId">{{:: 'id' | translate}} </label>
@@ -48,14 +48,14 @@
<kc-provider-config config="mapper.config" properties="mapperType.properties" realm="realm"></kc-provider-config>
</fieldset>
- <div class="form-group" data-ng-show="create && access.manageRealm">
+ <div class="form-group" data-ng-show="create && access.manageIdentityProviders">
<div class="col-md-10 col-md-offset-2">
<button kc-save>{{:: 'save' | translate}}</button>
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
- <div class="form-group" data-ng-show="!create && access.manageRealm">
+ <div class="form-group" data-ng-show="!create && access.manageIdentityProviders">
<div class="col-md-10 col-md-offset-2">
<button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html b/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html
index 7950d6f..a2f7eff 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html
@@ -19,7 +19,7 @@
</div>
</div>
</div>
- <div class="pull-right">
+ <div class="pull-right" data-ng-show="access.manageIdentityProviders">
<a class="btn btn-primary" href="#/create/identity-provider-mappers/{{realm.realm}}/{{identityProvider.alias}}">{{:: 'create' | translate}}</a>
</div>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
index 29aa688..fe781c2 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
@@ -1,8 +1,8 @@
<h1 data-ng-show="model.create">{{:: 'create-protocol-mapper' | translate}}</h1>
- <h1 data-ng-hide="model.create">{{model.mapper.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!model.create && access.manageRealm"
+ <h1 data-ng-hide="model.create">{{model.mapper.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!model.create && access.manageClients"
data-ng-hide="model.changed" data-ng-click="remove()"></i></h1>
- <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients">
<fieldset>
<div class="form-group clearfix">
@@ -64,14 +64,14 @@
</fieldset>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="model.create && access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="model.create && access.manageClients">
<button kc-save>{{:: 'save' | translate}}</button>
<button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
<div class="form-group">
- <div class="col-md-10 col-md-offset-2" data-ng-show="!model.create && access.manageRealm">
+ <div class="col-md-10 col-md-offset-2" data-ng-show="!model.create && access.manageClients">
<button kc-save data-ng-disabled="!model.changed">{{:: 'save' | translate}}</button>
<button kc-reset data-ng-disabled="!model.changed">{{:: 'cancel' | translate}}</button>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
index 71a8c5a..1b1ae96 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
@@ -9,7 +9,7 @@
<thead>
<tr>
<th colspan="6" class="kc-table-actions">
- <div class="dropdown pull-right">
+ <div class="dropdown pull-right" data-ng-show="access.manageIdentityProviders">
<select class="form-control" ng-model="provider"
ng-options="p.name group by p.groupName for p in allProviders track by p.id"
data-ng-change="addProvider(provider); provider = null">
@@ -35,7 +35,7 @@
<td translate="{{identityProvider.enabled}}"></td>
<td>{{identityProvider.config.guiOrder}}</td>
<td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{:: 'edit' | translate}}</td>
- <td class="kc-action-cell" data-ng-click="removeIdentityProvider(identityProvider)">{{:: 'delete' | translate}}</td>
+ <td class="kc-action-cell" data-ng-show="access.manageIdentityProviders" data-ng-click="removeIdentityProvider(identityProvider)">{{:: 'delete' | translate}}</td>
</tr>
</tbody>
</table>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
index 86c0102..1db4c6a 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
@@ -6,7 +6,7 @@
<kc-tabs-identity-provider></kc-tabs-identity-provider>
- <form class="form-horizontal" name="realmForm" novalidate>
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageIdentityProviders">
<input type="text" readonly value="this is not a login form" style="display: none;">
<input type="password" readonly value="this is not a login form" style="display: none;">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
index 2abfeb8..aa94559 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
@@ -6,7 +6,7 @@
<kc-tabs-identity-provider></kc-tabs-identity-provider>
- <form class="form-horizontal" name="realmForm" novalidate>
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageIdentityProviders">
<fieldset>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="redirectUri">{{:: 'redirect-uri' | translate}}</label>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
index 29e85af..9599d98 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
@@ -6,7 +6,7 @@
<kc-tabs-identity-provider></kc-tabs-identity-provider>
- <form class="form-horizontal" name="realmForm" novalidate>
+ <form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageIdentityProviders">
<input type="text" readonly value="this is not a login form" style="display: none;">
<input type="password" readonly value="this is not a login form" style="display: none;">
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
index d585672..6939b26 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-detail.html
@@ -113,7 +113,7 @@
</div>
</div>
- <div class="form-group clearfix" data-ng-hide="create">
+ <div class="form-group clearfix" data-ng-hide="create || !access.impersonation">
<label class="col-md-2 control-label" for="impersonate">{{:: 'impersonate-user' | translate}}</label>
<div class="col-md-6">
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
index 0b34e7f..e766bcb 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
@@ -32,8 +32,8 @@
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'clients' || path[1] == 'client' || path[3] == 'clients') && 'active'"><a href="#/realms/{{realm.realm}}/clients"><i class="fa fa-cube"></i> {{:: 'clients' | translate}}</a></li>
<li data-ng-show="access.viewClients" data-ng-class="(path[2] == 'client-templates' || path[1] == 'client-template' || path[3] == 'client-templates') && 'active'"><a href="#/realms/{{realm.realm}}/client-templates"><i class="fa fa-cubes"></i> {{:: 'client-templates' | translate}}</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
- <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
- <li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'user-federation' || path[2] == 'user-federation') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation"><i class="fa fa-database"></i> {{:: 'user-federation' | translate}}</a></li>
+ <li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
+ <li data-ng-show="access.viewUsers" data-ng-class="(path[1] == 'user-federation' || path[2] == 'user-federation') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation"><i class="fa fa-database"></i> {{:: 'user-federation' | translate}}</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'authentication' || path[2] == 'authentication') && 'active'"><a href="#/realms/{{realm.realm}}/authentication/flows"><i class="fa fa-lock"></i> {{:: 'authentication' | translate}}</a></li>
</ul>
</div>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group-list.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group-list.html
index 6542f21..5b67035 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group-list.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-group-list.html
@@ -5,7 +5,7 @@
<ul class="nav nav-tabs">
<li ng-class="{active: path[2] == 'groups'}"><a href="#/realms/{{realm.realm}}/groups">{{:: 'groups' | translate}}</a></li>
- <li ng-class="{active: path[2] == 'default-groups'}"><a href="#/realms/{{realm.realm}}/default-groups">{{:: 'default-groups' | translate}}</a><kc-tooltip>{{:: 'groups.default-groups.tooltip' | translate}}</kc-tooltip>
+ <li data-ng-show="access.viewRealm" ng-class="{active: path[2] == 'default-groups'}"><a href="#/realms/{{realm.realm}}/default-groups">{{:: 'default-groups' | translate}}</a><kc-tooltip>{{:: 'groups.default-groups.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
</div>
\ No newline at end of file
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-realm.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-realm.html
index 5d14503..7f10767 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-realm.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-realm.html
@@ -13,7 +13,7 @@
<li ng-class="{active: path[2] == 'theme-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/theme-settings">{{:: 'realm-tab-themes' | translate}}</a></li>
<li ng-class="{active: path[2] == 'cache-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/cache-settings">{{:: 'realm-tab-cache' | translate}}</a></li>
<li ng-class="{active: path[2] == 'token-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/token-settings">{{:: 'realm-tab-tokens' | translate}}</a></li>
- <li ng-class="{active: path[2] == 'client-initial-access'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/client-initial-access">{{:: 'realm-tab-client-initial-access' | translate}}</a></li>
+ <li ng-class="{active: path[2] == 'client-initial-access'}" data-ng-show="access.viewClients"><a href="#/realms/{{realm.realm}}/client-initial-access">{{:: 'realm-tab-client-initial-access' | translate}}</a></li>
<li ng-class="{active: path[2] == 'defense'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/defense/headers">{{:: 'realm-tab-security-defenses' | translate}}</a></li>
</ul>
</div>
\ No newline at end of file