Merge pull request #4084 from pedroigor/KEYCLOAK-4755
[KEYCLOAK-4755] - Client UI Tests
This commit is contained in:
commit
b78cc63f0d
27 changed files with 965 additions and 158 deletions
|
@ -1,6 +1,6 @@
|
|||
package org.keycloak.authorization.policy.provider.client;
|
||||
|
||||
import static org.keycloak.authorization.policy.provider.client.ClientPolicyProviderFactory.getClients;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
|
@ -9,24 +9,29 @@ import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
|||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
|
||||
public class ClientPolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Function<Policy, ClientPolicyRepresentation> representationFunction;
|
||||
|
||||
public ClientPolicyProvider(Function<Policy, ClientPolicyRepresentation> representationFunction) {
|
||||
this.representationFunction = representationFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
Policy policy = evaluation.getPolicy();
|
||||
EvaluationContext context = evaluation.getContext();
|
||||
String[] clients = getClients(policy);
|
||||
ClientPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
|
||||
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
|
||||
RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
|
||||
EvaluationContext context = evaluation.getContext();
|
||||
|
||||
if (clients.length > 0) {
|
||||
for (String client : clients) {
|
||||
ClientModel clientModel = realm.getClientById(client);
|
||||
if (context.getAttributes().containsValue("kc.client.id", clientModel.getClientId())) {
|
||||
evaluation.grant();
|
||||
return;
|
||||
}
|
||||
for (String client : representation.getClients()) {
|
||||
ClientModel clientModel = realm.getClientById(client);
|
||||
|
||||
if (context.getAttributes().containsValue("kc.client.id", clientModel.getClientId())) {
|
||||
evaluation.grant();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,17 @@ package org.keycloak.authorization.policy.provider.client;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
|
@ -17,12 +20,15 @@ import org.keycloak.authorization.store.StoreFactory;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmModel.ClientRemovedEvent;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
||||
public class ClientPolicyProviderFactory implements PolicyProviderFactory<ClientPolicyRepresentation> {
|
||||
|
||||
private ClientPolicyProvider provider = new ClientPolicyProvider();
|
||||
private ClientPolicyProvider provider = new ClientPolicyProvider(policy -> toRepresentation(policy, new ClientPolicyRepresentation()));
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -40,8 +46,29 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
|
||||
return null;
|
||||
public ClientPolicyRepresentation toRepresentation(Policy policy, ClientPolicyRepresentation representation) {
|
||||
representation.setClients(new HashSet<>(Arrays.asList(getClients(policy))));
|
||||
return representation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<ClientPolicyRepresentation> getRepresentationType() {
|
||||
return ClientPolicyRepresentation.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Policy policy, ClientPolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||
updateClients(policy, representation.getClients(), authorization);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Policy policy, ClientPolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||
updateClients(policy, representation.getClients(), authorization);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImport(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) {
|
||||
updateClients(policy, new HashSet<>(Arrays.asList(getClients(policy))), authorization);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -101,7 +128,41 @@ public class ClientPolicyProviderFactory implements PolicyProviderFactory {
|
|||
return "client";
|
||||
}
|
||||
|
||||
static String[] getClients(Policy policy) {
|
||||
private void updateClients(Policy policy, Set<String> clients, AuthorizationProvider authorization) {
|
||||
RealmModel realm = authorization.getKeycloakSession().getContext().getRealm();
|
||||
|
||||
if (clients == null || clients.isEmpty()) {
|
||||
throw new RuntimeException("No client provided.");
|
||||
}
|
||||
|
||||
Set<String> updatedClients = new HashSet<>();
|
||||
|
||||
for (String id : clients) {
|
||||
ClientModel client = realm.getClientByClientId(id);
|
||||
|
||||
if (client == null) {
|
||||
client = realm.getClientById(id);
|
||||
}
|
||||
|
||||
if (client == null) {
|
||||
throw new RuntimeException("Error while updating policy [" + policy.getName() + "]. Client [" + id + "] could not be found.");
|
||||
}
|
||||
|
||||
updatedClients.add(client.getId());
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> config = policy.getConfig();
|
||||
|
||||
config.put("clients", JsonSerialization.writeValueAsString(updatedClients));
|
||||
|
||||
policy.setConfig(config);
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to serialize clients", cause);
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getClients(Policy policy) {
|
||||
String clients = policy.getConfig().get("clients");
|
||||
|
||||
if (clients != null) {
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.role;
|
||||
|
||||
import static org.keycloak.authorization.policy.provider.role.RolePolicyProviderFactory.getRoles;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.identity.Identity;
|
||||
|
@ -29,43 +29,43 @@ import org.keycloak.authorization.policy.provider.PolicyProvider;
|
|||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class RolePolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Function<Policy, RolePolicyRepresentation> representationFunction;
|
||||
|
||||
public RolePolicyProvider(Function<Policy, RolePolicyRepresentation> representationFunction) {
|
||||
this.representationFunction = representationFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
Policy policy = evaluation.getPolicy();
|
||||
Map<String, Object>[] roleIds = getRoles(policy);
|
||||
Set<RolePolicyRepresentation.RoleDefinition> roleIds = representationFunction.apply(policy).getRoles();
|
||||
AuthorizationProvider authorizationProvider = evaluation.getAuthorizationProvider();
|
||||
RealmModel realm = authorizationProvider.getKeycloakSession().getContext().getRealm();
|
||||
Identity identity = evaluation.getContext().getIdentity();
|
||||
|
||||
if (roleIds.length > 0) {
|
||||
Identity identity = evaluation.getContext().getIdentity();
|
||||
for (RolePolicyRepresentation.RoleDefinition roleDefinition : roleIds) {
|
||||
RoleModel role = realm.getRoleById(roleDefinition.getId());
|
||||
|
||||
for (Map<String, Object> current : roleIds) {
|
||||
RoleModel role = realm.getRoleById((String) current.get("id"));
|
||||
if (role != null) {
|
||||
boolean hasRole = hasRole(identity, role, realm);
|
||||
|
||||
if (role != null) {
|
||||
boolean hasRole = hasRole(identity, role, realm);
|
||||
|
||||
if (!hasRole && Boolean.valueOf(isRequired(current))) {
|
||||
evaluation.deny();
|
||||
return;
|
||||
} else if (hasRole) {
|
||||
evaluation.grant();
|
||||
}
|
||||
if (!hasRole && roleDefinition.isRequired()) {
|
||||
evaluation.deny();
|
||||
return;
|
||||
} else if (hasRole) {
|
||||
evaluation.grant();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRequired(Map<String, Object> current) {
|
||||
return (boolean) current.getOrDefault("required", false);
|
||||
}
|
||||
|
||||
private boolean hasRole(Identity identity, RoleModel role, RealmModel realm) {
|
||||
String roleName = role.getName();
|
||||
if (role.isClientRole()) {
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.keycloak.authorization.AuthorizationProvider;
|
|||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.model.ResourceServer;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderAdminService;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProviderFactory;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceServerStore;
|
||||
|
@ -53,7 +52,7 @@ import java.util.Set;
|
|||
*/
|
||||
public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePolicyRepresentation> {
|
||||
|
||||
private RolePolicyProvider provider = new RolePolicyProvider();
|
||||
private RolePolicyProvider provider = new RolePolicyProvider(policy -> toRepresentation(policy, new RolePolicyRepresentation()));
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -70,20 +69,15 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
|
|||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolicyProviderAdminService getAdminResource(ResourceServer resourceServer, AuthorizationProvider authorization) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolicyProvider create(KeycloakSession session) {
|
||||
return new RolePolicyProvider();
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RolePolicyRepresentation toRepresentation(Policy policy, RolePolicyRepresentation representation) {
|
||||
try {
|
||||
representation.setRoles(JsonSerialization.readValue(policy.getConfig().get("roles"), Set.class));
|
||||
representation.setRoles(new HashSet<>(Arrays.asList(JsonSerialization.readValue(policy.getConfig().get("roles"), RolePolicyRepresentation.RoleDefinition[].class))));
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to deserialize roles", cause);
|
||||
}
|
||||
|
@ -119,65 +113,63 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
|
|||
}
|
||||
|
||||
private void updateRoles(Policy policy, AuthorizationProvider authorization, Set<RolePolicyRepresentation.RoleDefinition> roles) {
|
||||
try {
|
||||
RealmModel realm = authorization.getRealm();
|
||||
Set<RolePolicyRepresentation.RoleDefinition> updatedRoles = new HashSet<>();
|
||||
RealmModel realm = authorization.getRealm();
|
||||
Set<RolePolicyRepresentation.RoleDefinition> updatedRoles = new HashSet<>();
|
||||
|
||||
if (roles != null) {
|
||||
for (RolePolicyRepresentation.RoleDefinition definition : roles) {
|
||||
String roleName = definition.getId();
|
||||
String clientId = null;
|
||||
int clientIdSeparator = roleName.indexOf("/");
|
||||
if (roles != null) {
|
||||
for (RolePolicyRepresentation.RoleDefinition definition : roles) {
|
||||
String roleName = definition.getId();
|
||||
String clientId = null;
|
||||
int clientIdSeparator = roleName.indexOf("/");
|
||||
|
||||
if (clientIdSeparator != -1) {
|
||||
clientId = roleName.substring(0, clientIdSeparator);
|
||||
roleName = roleName.substring(clientIdSeparator + 1);
|
||||
}
|
||||
if (clientIdSeparator != -1) {
|
||||
clientId = roleName.substring(0, clientIdSeparator);
|
||||
roleName = roleName.substring(clientIdSeparator + 1);
|
||||
}
|
||||
|
||||
RoleModel role;
|
||||
RoleModel role;
|
||||
|
||||
if (clientId == null) {
|
||||
role = realm.getRole(roleName);
|
||||
|
||||
if (role == null) {
|
||||
role = realm.getRoleById(roleName);
|
||||
}
|
||||
} else {
|
||||
ClientModel client = realm.getClientByClientId(clientId);
|
||||
|
||||
if (client == null) {
|
||||
throw new RuntimeException("Client with id [" + clientId + "] not found.");
|
||||
}
|
||||
|
||||
role = client.getRole(roleName);
|
||||
}
|
||||
|
||||
// fallback to find any client role with the given name
|
||||
if (role == null) {
|
||||
String finalRoleName = roleName;
|
||||
role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
if (clientId == null) {
|
||||
role = realm.getRole(roleName);
|
||||
|
||||
if (role == null) {
|
||||
throw new RuntimeException("Error while importing configuration. Role [" + roleName + "] could not be found.");
|
||||
role = realm.getRoleById(roleName);
|
||||
}
|
||||
} else {
|
||||
ClientModel client = realm.getClientByClientId(clientId);
|
||||
|
||||
if (client == null) {
|
||||
throw new RuntimeException("Client with id [" + clientId + "] not found.");
|
||||
}
|
||||
|
||||
definition.setId(role.getId());
|
||||
role = client.getRole(roleName);
|
||||
}
|
||||
|
||||
updatedRoles.add(definition);
|
||||
// fallback to find any client role with the given name
|
||||
if (role == null) {
|
||||
String finalRoleName = roleName;
|
||||
role = realm.getClients().stream().map(clientModel -> clientModel.getRole(finalRoleName)).filter(roleModel -> roleModel != null)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
try {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
|
||||
|
||||
if (role == null) {
|
||||
throw new RuntimeException("Error while updating policy [" + policy.getName() + "]. Role [" + roleName + "] could not be found.");
|
||||
}
|
||||
|
||||
definition.setId(role.getId());
|
||||
|
||||
updatedRoles.add(definition);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> config = policy.getConfig();
|
||||
|
||||
config.put("roles", JsonSerialization.writeValueAsString(updatedRoles));
|
||||
|
||||
policy.setConfig(config);
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to deserialize roles", cause);
|
||||
throw new RuntimeException("Failed to serialize roles", cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,7 +245,7 @@ public class RolePolicyProviderFactory implements PolicyProviderFactory<RolePoli
|
|||
return "role";
|
||||
}
|
||||
|
||||
static Map<String, Object>[] getRoles(Policy policy) {
|
||||
private Map<String, Object>[] getRoles(Policy policy) {
|
||||
String roles = policy.getConfig().get("roles");
|
||||
|
||||
if (roles != null) {
|
||||
|
|
|
@ -17,30 +17,34 @@
|
|||
*/
|
||||
package org.keycloak.authorization.policy.provider.user;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.keycloak.authorization.model.Policy;
|
||||
import org.keycloak.authorization.policy.evaluation.Evaluation;
|
||||
import org.keycloak.authorization.policy.evaluation.EvaluationContext;
|
||||
import org.keycloak.authorization.policy.provider.PolicyProvider;
|
||||
|
||||
import static org.keycloak.authorization.policy.provider.user.UserPolicyProviderFactory.getUsers;
|
||||
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UserPolicyProvider implements PolicyProvider {
|
||||
|
||||
private final Function<Policy, UserPolicyRepresentation> representationFunction;
|
||||
|
||||
public UserPolicyProvider(Function<Policy, UserPolicyRepresentation> representationFunction) {
|
||||
this.representationFunction = representationFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evaluate(Evaluation evaluation) {
|
||||
Policy policy = evaluation.getPolicy();
|
||||
EvaluationContext context = evaluation.getContext();
|
||||
String[] userIds = getUsers(policy);
|
||||
UserPolicyRepresentation representation = representationFunction.apply(evaluation.getPolicy());
|
||||
|
||||
if (userIds.length > 0) {
|
||||
for (String userId : userIds) {
|
||||
if (context.getIdentity().getId().equals(userId)) {
|
||||
evaluation.grant();
|
||||
break;
|
||||
}
|
||||
for (String userId : representation.getUsers()) {
|
||||
if (context.getIdentity().getId().equals(userId)) {
|
||||
evaluation.grant();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
|
@ -49,7 +50,7 @@ import org.keycloak.util.JsonSerialization;
|
|||
*/
|
||||
public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPolicyRepresentation> {
|
||||
|
||||
private UserPolicyProvider provider = new UserPolicyProvider();
|
||||
private UserPolicyProvider provider = new UserPolicyProvider((Function<Policy, UserPolicyRepresentation>) policy -> toRepresentation(policy, new UserPolicyRepresentation()));
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
|
@ -110,42 +111,40 @@ public class UserPolicyProviderFactory implements PolicyProviderFactory<UserPoli
|
|||
}
|
||||
|
||||
private void updateUsers(Policy policy, AuthorizationProvider authorization, Set<String> users) {
|
||||
try {
|
||||
KeycloakSession session = authorization.getKeycloakSession();
|
||||
RealmModel realm = authorization.getRealm();
|
||||
UserProvider userProvider = session.users();
|
||||
Set<String> updatedUsers = new HashSet<>();
|
||||
KeycloakSession session = authorization.getKeycloakSession();
|
||||
RealmModel realm = authorization.getRealm();
|
||||
UserProvider userProvider = session.users();
|
||||
Set<String> updatedUsers = new HashSet<>();
|
||||
|
||||
if (users != null) {
|
||||
for (String userId : users) {
|
||||
UserModel user = null;
|
||||
|
||||
if (users != null) {
|
||||
try {
|
||||
for (String userId : users) {
|
||||
UserModel user = null;
|
||||
|
||||
try {
|
||||
user = userProvider.getUserByUsername(userId, realm);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
user = userProvider.getUserById(userId, realm);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new RuntimeException("Error while importing configuration. User [" + userId + "] could not be found.");
|
||||
}
|
||||
|
||||
updatedUsers.add(user.getId());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error while updating policy [" + policy.getName() + "].", e);
|
||||
user = userProvider.getUserByUsername(userId, realm);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
user = userProvider.getUserById(userId, realm);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new RuntimeException("Error while updating policy [" + policy.getName() + "]. User [" + userId + "] could not be found.");
|
||||
}
|
||||
|
||||
updatedUsers.add(user.getId());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, String> config = policy.getConfig();
|
||||
|
||||
config.put("users", JsonSerialization.writeValueAsString(updatedUsers));
|
||||
|
||||
policy.setConfig(config);
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to deserialize roles", cause);
|
||||
throw new RuntimeException("Failed to serialize users", cause);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.representations.idm.authorization;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ClientPolicyRepresentation extends AbstractPolicyRepresentation {
|
||||
|
||||
private Set<String> clients;
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "client";
|
||||
}
|
||||
|
||||
public Set<String> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public void setClients(Set<String> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
public void addClient(String... id) {
|
||||
if (this.clients == null) {
|
||||
this.clients = new HashSet<>();
|
||||
}
|
||||
this.clients.addAll(Arrays.asList(id));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.admin.client.resource;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public interface ClientPoliciesResource {
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
Response create(ClientPolicyRepresentation representation);
|
||||
|
||||
@Path("{id}")
|
||||
ClientPolicyResource findById(@PathParam("id") String id);
|
||||
|
||||
@Path("/search")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
ClientPolicyRepresentation findByName(@QueryParam("name") String name);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.admin.client.resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public interface ClientPolicyResource {
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
ClientPolicyRepresentation toRepresentation();
|
||||
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
void update(ClientPolicyRepresentation representation);
|
||||
|
||||
@DELETE
|
||||
void remove();
|
||||
|
||||
@Path("/associatedPolicies")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
List<PolicyRepresentation> associatedPolicies();
|
||||
|
||||
@Path("/dependentPolicies")
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
List<PolicyRepresentation> dependentPolicies();
|
||||
|
||||
@Path("/resources")
|
||||
@GET
|
||||
@Produces("application/json")
|
||||
@NoCache
|
||||
List<ResourceRepresentation> resources();
|
||||
|
||||
}
|
|
@ -70,10 +70,10 @@ public interface PoliciesResource {
|
|||
PolicyEvaluationResponse evaluate(PolicyEvaluationRequest evaluationRequest);
|
||||
|
||||
@Path("role")
|
||||
RolePoliciesResource roles();
|
||||
RolePoliciesResource role();
|
||||
|
||||
@Path("user")
|
||||
UserPoliciesResource users();
|
||||
UserPoliciesResource user();
|
||||
|
||||
@Path("js")
|
||||
JSPoliciesResource js();
|
||||
|
@ -86,4 +86,7 @@ public interface PoliciesResource {
|
|||
|
||||
@Path("rules")
|
||||
RulePoliciesResource rule();
|
||||
|
||||
@Path("client")
|
||||
ClientPoliciesResource client();
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
|
|||
return getClientResource(RESOURCE_SERVER_ID).authorization();
|
||||
}
|
||||
|
||||
private ClientResource getClientResource(String clientId) {
|
||||
protected ClientResource getClientResource(String clientId) {
|
||||
ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
|
||||
ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
|
||||
return clients.get(resourceServer.getId());
|
||||
|
@ -199,7 +199,7 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
|
|||
|
||||
assertFalse(policy.getUsers().isEmpty());
|
||||
|
||||
getAuthorizationResource().policies().users().create(policy);
|
||||
getAuthorizationResource().policies().user().create(policy);
|
||||
}
|
||||
|
||||
protected interface ExceptionRunnable {
|
||||
|
|
|
@ -26,15 +26,19 @@ import java.util.List;
|
|||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.admin.client.resource.ClientPoliciesResource;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.admin.client.resource.ResourcesResource;
|
||||
import org.keycloak.admin.client.resource.RolePoliciesResource;
|
||||
import org.keycloak.admin.client.resource.RoleScopeResource;
|
||||
import org.keycloak.admin.client.resource.RolesResource;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
|
||||
/**
|
||||
|
@ -205,4 +209,99 @@ public abstract class AbstractServletAuthzFunctionalAdapterTest extends Abstract
|
|||
assertTrue(hasText("This is public resource that should be accessible without login."));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredRole() throws Exception {
|
||||
performTests(() -> {
|
||||
login("jdoe", "jdoe");
|
||||
navigateToUserPremiumPage();
|
||||
assertFalse(wasDenied());
|
||||
|
||||
RolesResource rolesResource = getClientResource(RESOURCE_SERVER_ID).roles();
|
||||
|
||||
rolesResource.create(new RoleRepresentation("required-role", "", false));
|
||||
|
||||
RolePolicyRepresentation policy = new RolePolicyRepresentation();
|
||||
|
||||
policy.setName("Required Role Policy");
|
||||
policy.addRole("user_premium", false);
|
||||
policy.addRole("required-role", false);
|
||||
|
||||
RolePoliciesResource rolePolicy = getAuthorizationResource().policies().role();
|
||||
|
||||
rolePolicy.create(policy);
|
||||
policy = rolePolicy.findByName(policy.getName());
|
||||
|
||||
updatePermissionPolicies("Premium Resource Permission", policy.getName());
|
||||
|
||||
login("jdoe", "jdoe");
|
||||
navigateToUserPremiumPage();
|
||||
assertFalse(wasDenied());
|
||||
|
||||
policy.getRoles().clear();
|
||||
policy.addRole("user_premium", false);
|
||||
policy.addRole("required-role", true);
|
||||
|
||||
rolePolicy.findById(policy.getId()).update(policy);
|
||||
|
||||
login("jdoe", "jdoe");
|
||||
navigateToUserPremiumPage();
|
||||
assertTrue(wasDenied());
|
||||
|
||||
UsersResource users = realmsResouce().realm(REALM_NAME).users();
|
||||
UserRepresentation user = users.search("jdoe").get(0);
|
||||
|
||||
RoleScopeResource roleScopeResource = users.get(user.getId()).roles().clientLevel(getClientResource(RESOURCE_SERVER_ID).toRepresentation().getId());
|
||||
RoleRepresentation requiredRole = rolesResource.get("required-role").toRepresentation();
|
||||
roleScopeResource.add(Arrays.asList(requiredRole));
|
||||
|
||||
login("jdoe", "jdoe");
|
||||
navigateToUserPremiumPage();
|
||||
assertFalse(wasDenied());
|
||||
|
||||
policy.getRoles().clear();
|
||||
policy.addRole("user_premium", false);
|
||||
policy.addRole("required-role", false);
|
||||
|
||||
rolePolicy.findById(policy.getId()).update(policy);
|
||||
|
||||
login("jdoe", "jdoe");
|
||||
navigateToUserPremiumPage();
|
||||
assertFalse(wasDenied());
|
||||
|
||||
roleScopeResource.remove(Arrays.asList(requiredRole));
|
||||
|
||||
login("jdoe", "jdoe");
|
||||
navigateToUserPremiumPage();
|
||||
assertFalse(wasDenied());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlySpecificClient() throws Exception {
|
||||
performTests(() -> {
|
||||
login("jdoe", "jdoe");
|
||||
assertFalse(wasDenied());
|
||||
|
||||
ClientPolicyRepresentation policy = new ClientPolicyRepresentation();
|
||||
|
||||
policy.setName("Only Client Policy");
|
||||
policy.addClient("admin-cli");
|
||||
|
||||
ClientPoliciesResource policyResource = getAuthorizationResource().policies().client();
|
||||
policyResource.create(policy);
|
||||
policy = policyResource.findByName(policy.getName());
|
||||
|
||||
updatePermissionPolicies("Protected Resource Permission", policy.getName());
|
||||
|
||||
login("jdoe", "jdoe");
|
||||
assertTrue(wasDenied());
|
||||
|
||||
policy.addClient("servlet-authz-app");
|
||||
policyResource.findById(policy.getId()).update(policy);
|
||||
|
||||
login("jdoe", "jdoe");
|
||||
assertFalse(wasDenied());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ public abstract class AbstractPolicyManagementTest extends AbstractKeycloakTest
|
|||
representation.setName(name);
|
||||
representation.addUser(userId);
|
||||
|
||||
client.authorization().policies().users().create(representation);
|
||||
client.authorization().policies().user().create(representation);
|
||||
}
|
||||
|
||||
protected ClientResource getClient() {
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.admin.client.authorization;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientPoliciesResource;
|
||||
import org.keycloak.admin.client.resource.ClientPolicyResource;
|
||||
import org.keycloak.admin.client.resource.PolicyResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.DecisionStrategy;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ClientPolicyManagementTest extends AbstractPolicyManagementTest {
|
||||
|
||||
@Override
|
||||
protected RealmBuilder createTestRealm() {
|
||||
return super.createTestRealm()
|
||||
.client(ClientBuilder.create().clientId("Client A"))
|
||||
.client(ClientBuilder.create().clientId("Client B"))
|
||||
.client(ClientBuilder.create().clientId("Client C"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
AuthorizationResource authorization = getClient().authorization();
|
||||
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
|
||||
|
||||
representation.setName("Realm Client Policy");
|
||||
representation.setDescription("description");
|
||||
representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
|
||||
representation.setLogic(Logic.NEGATIVE);
|
||||
representation.addClient("Client A");
|
||||
representation.addClient("Client B");
|
||||
|
||||
assertCreated(authorization, representation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
AuthorizationResource authorization = getClient().authorization();
|
||||
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
|
||||
|
||||
representation.setName("Update Test Client Policy");
|
||||
representation.setDescription("description");
|
||||
representation.setDecisionStrategy(DecisionStrategy.CONSENSUS);
|
||||
representation.setLogic(Logic.NEGATIVE);
|
||||
representation.addClient("Client A");
|
||||
representation.addClient("Client B");
|
||||
representation.addClient("Client C");
|
||||
|
||||
assertCreated(authorization, representation);
|
||||
|
||||
representation.setName("changed");
|
||||
representation.setDescription("changed");
|
||||
representation.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE);
|
||||
representation.setLogic(Logic.POSITIVE);
|
||||
representation.setClients(representation.getClients().stream().filter(userName -> !userName.equals("Client A")).collect(Collectors.toSet()));
|
||||
|
||||
ClientPoliciesResource policies = authorization.policies().client();
|
||||
ClientPolicyResource permission = policies.findById(representation.getId());
|
||||
|
||||
permission.update(representation);
|
||||
assertRepresentation(representation, permission);
|
||||
|
||||
representation.setClients(representation.getClients().stream().filter(userName -> !userName.equals("Client C")).collect(Collectors.toSet()));
|
||||
|
||||
permission.update(representation);
|
||||
assertRepresentation(representation, permission);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete() {
|
||||
AuthorizationResource authorization = getClient().authorization();
|
||||
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
|
||||
|
||||
representation.setName("Test Delete Permission");
|
||||
representation.addClient("Client A");
|
||||
|
||||
ClientPoliciesResource policies = authorization.policies().client();
|
||||
Response response = policies.create(representation);
|
||||
ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
|
||||
|
||||
policies.findById(created.getId()).remove();
|
||||
|
||||
ClientPolicyResource removed = policies.findById(created.getId());
|
||||
|
||||
try {
|
||||
removed.toRepresentation();
|
||||
fail("Permission not removed");
|
||||
} catch (NotFoundException ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericConfig() {
|
||||
AuthorizationResource authorization = getClient().authorization();
|
||||
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
|
||||
|
||||
representation.setName("Test Generic Config Permission");
|
||||
representation.addClient("Client A");
|
||||
|
||||
ClientPoliciesResource policies = authorization.policies().client();
|
||||
Response response = policies.create(representation);
|
||||
ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
|
||||
|
||||
PolicyResource policy = authorization.policies().policy(created.getId());
|
||||
PolicyRepresentation genericConfig = policy.toRepresentation();
|
||||
|
||||
assertNotNull(genericConfig.getConfig());
|
||||
assertNotNull(genericConfig.getConfig().get("clients"));
|
||||
|
||||
ClientRepresentation user = getRealm().clients().findByClientId("Client A").get(0);
|
||||
|
||||
assertTrue(genericConfig.getConfig().get("clients").contains(user.getId()));
|
||||
}
|
||||
|
||||
private void assertCreated(AuthorizationResource authorization, ClientPolicyRepresentation representation) {
|
||||
ClientPoliciesResource permissions = authorization.policies().client();
|
||||
Response response = permissions.create(representation);
|
||||
ClientPolicyRepresentation created = response.readEntity(ClientPolicyRepresentation.class);
|
||||
ClientPolicyResource permission = permissions.findById(created.getId());
|
||||
assertRepresentation(representation, permission);
|
||||
}
|
||||
|
||||
private void assertRepresentation(ClientPolicyRepresentation representation, ClientPolicyResource permission) {
|
||||
ClientPolicyRepresentation actual = permission.toRepresentation();
|
||||
assertRepresentation(representation, actual, () -> permission.resources(), () -> Collections.emptyList(), () -> permission.associatedPolicies());
|
||||
assertEquals(representation.getClients().size(), actual.getClients().size());
|
||||
assertEquals(0, actual.getClients().stream().filter(clientId -> !representation.getClients().stream()
|
||||
.filter(userName -> getClientName(clientId).equalsIgnoreCase(userName))
|
||||
.findFirst().isPresent())
|
||||
.count());
|
||||
}
|
||||
|
||||
private String getClientName(String id) {
|
||||
return getRealm().clients().get(id).toRepresentation().getClientId();
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ import org.keycloak.admin.client.resource.ClientResource;
|
|||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +44,8 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
|
|||
RoleRepresentation role = new RoleRepresentation();
|
||||
role.setName("admin");
|
||||
clientResource.roles().create(role);
|
||||
|
||||
testRealmResource().users().create(UserBuilder.create().username("alice").build());
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -72,6 +75,6 @@ public class ImportAuthorizationSettingsTest extends AbstractAuthorizationTest {
|
|||
|
||||
authorizationResource.importSettings(toImport);
|
||||
|
||||
assertEquals(13, authorizationResource.policies().policies().size());
|
||||
assertEquals(15, authorizationResource.policies().policies().size());
|
||||
}
|
||||
}
|
|
@ -119,7 +119,7 @@ public class RolePolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
representation.setLogic(Logic.POSITIVE);
|
||||
representation.setRoles(representation.getRoles().stream().filter(roleDefinition -> !roleDefinition.getId().equals("Resource A")).collect(Collectors.toSet()));
|
||||
|
||||
RolePoliciesResource policies = authorization.policies().roles();
|
||||
RolePoliciesResource policies = authorization.policies().role();
|
||||
RolePolicyResource permission = policies.findById(representation.getId());
|
||||
|
||||
permission.update(representation);
|
||||
|
@ -146,7 +146,7 @@ public class RolePolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
representation.setName("Test Delete Permission");
|
||||
representation.addRole("Role A", false);
|
||||
|
||||
RolePoliciesResource policies = authorization.policies().roles();
|
||||
RolePoliciesResource policies = authorization.policies().role();
|
||||
Response response = policies.create(representation);
|
||||
RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
|
||||
|
||||
|
@ -170,7 +170,7 @@ public class RolePolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
representation.setName("Test Generic Config Permission");
|
||||
representation.addRole("Role A", false);
|
||||
|
||||
RolePoliciesResource policies = authorization.policies().roles();
|
||||
RolePoliciesResource policies = authorization.policies().role();
|
||||
Response response = policies.create(representation);
|
||||
RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
|
||||
|
||||
|
@ -186,7 +186,7 @@ public class RolePolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
}
|
||||
|
||||
private void assertCreated(AuthorizationResource authorization, RolePolicyRepresentation representation) {
|
||||
RolePoliciesResource permissions = authorization.policies().roles();
|
||||
RolePoliciesResource permissions = authorization.policies().role();
|
||||
Response response = permissions.create(representation);
|
||||
RolePolicyRepresentation created = response.readEntity(RolePolicyRepresentation.class);
|
||||
RolePolicyResource permission = permissions.findById(created.getId());
|
||||
|
|
|
@ -54,7 +54,7 @@ public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateUserPolicy() {
|
||||
public void testCreate() {
|
||||
AuthorizationResource authorization = getClient().authorization();
|
||||
UserPolicyRepresentation representation = new UserPolicyRepresentation();
|
||||
|
||||
|
@ -89,7 +89,7 @@ public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
representation.setLogic(Logic.POSITIVE);
|
||||
representation.setUsers(representation.getUsers().stream().filter(userName -> !userName.equals("User A")).collect(Collectors.toSet()));
|
||||
|
||||
UserPoliciesResource policies = authorization.policies().users();
|
||||
UserPoliciesResource policies = authorization.policies().user();
|
||||
UserPolicyResource permission = policies.findById(representation.getId());
|
||||
|
||||
permission.update(representation);
|
||||
|
@ -109,7 +109,7 @@ public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
representation.setName("Test Delete Permission");
|
||||
representation.addUser("User A");
|
||||
|
||||
UserPoliciesResource policies = authorization.policies().users();
|
||||
UserPoliciesResource policies = authorization.policies().user();
|
||||
Response response = policies.create(representation);
|
||||
UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
|
||||
|
||||
|
@ -133,7 +133,7 @@ public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
representation.setName("Test Generic Config Permission");
|
||||
representation.addUser("User A");
|
||||
|
||||
UserPoliciesResource policies = authorization.policies().users();
|
||||
UserPoliciesResource policies = authorization.policies().user();
|
||||
Response response = policies.create(representation);
|
||||
UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
|
||||
|
||||
|
@ -149,7 +149,7 @@ public class UserPolicyManagementTest extends AbstractPolicyManagementTest {
|
|||
}
|
||||
|
||||
private void assertCreated(AuthorizationResource authorization, UserPolicyRepresentation representation) {
|
||||
UserPoliciesResource permissions = authorization.policies().users();
|
||||
UserPoliciesResource permissions = authorization.policies().user();
|
||||
Response response = permissions.create(representation);
|
||||
UserPolicyRepresentation created = response.readEntity(UserPolicyRepresentation.class);
|
||||
UserPolicyResource permission = permissions.findById(created.getId());
|
||||
|
|
|
@ -161,6 +161,20 @@
|
|||
"config": {
|
||||
"code": "var context = $evaluation.getContext();\nvar identity = context.getIdentity();\nvar attributes = identity.getAttributes();\nvar email = attributes.getValue('email').asString(0);\n\nif (identity.hasRole('admin') || email.endsWith('@keycloak.org')) {\n $evaluation.grant();\n}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Test Client Policy",
|
||||
"type": "client",
|
||||
"config": {
|
||||
"clients": "[\"admin-cli\"]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Test User Policy",
|
||||
"type": "user",
|
||||
"config": {
|
||||
"users": "[\"alice\"]"
|
||||
}
|
||||
}
|
||||
],
|
||||
"scopes": [
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.console.page.clients.authorization.policy;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ClientPolicy implements PolicyTypeUI {
|
||||
|
||||
@Page
|
||||
private ClientPolicyForm form;
|
||||
|
||||
public ClientPolicyForm form() {
|
||||
return form;
|
||||
}
|
||||
|
||||
public ClientPolicyRepresentation toRepresentation() {
|
||||
return form.toRepresentation();
|
||||
}
|
||||
|
||||
public void update(ClientPolicyRepresentation expected) {
|
||||
form().populate(expected);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.console.page.clients.authorization.policy;
|
||||
|
||||
import static org.openqa.selenium.By.tagName;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
import org.keycloak.testsuite.console.page.fragment.MultipleStringSelect2;
|
||||
import org.keycloak.testsuite.page.Form;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.support.FindBy;
|
||||
import org.openqa.selenium.support.ui.Select;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ClientPolicyForm extends Form {
|
||||
|
||||
@FindBy(id = "name")
|
||||
private WebElement name;
|
||||
|
||||
@FindBy(id = "description")
|
||||
private WebElement description;
|
||||
|
||||
@FindBy(id = "logic")
|
||||
private Select logic;
|
||||
|
||||
@FindBy(xpath = "//i[contains(@class,'pficon-delete')]")
|
||||
private WebElement deleteButton;
|
||||
|
||||
@FindBy(id = "s2id_clients")
|
||||
private ClientSelect clientsInput;
|
||||
|
||||
@FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Delete']")
|
||||
private WebElement confirmDelete;
|
||||
|
||||
public void populate(ClientPolicyRepresentation expected) {
|
||||
setInputValue(name, expected.getName());
|
||||
setInputValue(description, expected.getDescription());
|
||||
logic.selectByValue(expected.getLogic().name());
|
||||
|
||||
clientsInput.update(expected.getClients());
|
||||
|
||||
save();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
deleteButton.click();
|
||||
confirmDelete.click();
|
||||
}
|
||||
|
||||
public ClientPolicyRepresentation toRepresentation() {
|
||||
ClientPolicyRepresentation representation = new ClientPolicyRepresentation();
|
||||
|
||||
representation.setName(getInputValue(name));
|
||||
representation.setDescription(getInputValue(description));
|
||||
representation.setLogic(Logic.valueOf(logic.getFirstSelectedOption().getText().toUpperCase()));
|
||||
representation.setClients(clientsInput.getSelected());
|
||||
|
||||
return representation;
|
||||
}
|
||||
|
||||
public class ClientSelect extends MultipleStringSelect2 {
|
||||
|
||||
@Override
|
||||
protected List<WebElement> getSelectedElements() {
|
||||
return getRoot().findElements(By.xpath("(//table[@id='selected-clients'])/tbody/tr")).stream()
|
||||
.filter(webElement -> webElement.findElements(tagName("td")).size() > 1)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BiFunction<WebElement, String, Boolean> deselect() {
|
||||
return (webElement, name) -> {
|
||||
List<WebElement> tds = webElement.findElements(tagName("td"));
|
||||
|
||||
if (!tds.get(0).getText().isEmpty()) {
|
||||
if (tds.get(0).getText().equals(name)) {
|
||||
tds.get(1).findElement(By.tagName("button")).click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Function<WebElement, String> representation() {
|
||||
return webElement -> webElement.findElements(tagName("td")).get(0).getText();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ import static org.openqa.selenium.By.tagName;
|
|||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.keycloak.representations.idm.authorization.AbstractPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.AggregatePolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.JSPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.RolePolicyRepresentation;
|
||||
|
@ -62,6 +63,9 @@ public class Policies extends Form {
|
|||
@Page
|
||||
private RulePolicy rulePolicy;
|
||||
|
||||
@Page
|
||||
private ClientPolicy clientPolicy;
|
||||
|
||||
public PoliciesTable policies() {
|
||||
return table;
|
||||
}
|
||||
|
@ -95,6 +99,10 @@ public class Policies extends Form {
|
|||
rulePolicy.form().populate((RulePolicyRepresentation) expected);
|
||||
rulePolicy.form().save();
|
||||
return (P) rulePolicy;
|
||||
} else if ("client".equals(type)) {
|
||||
clientPolicy.form().populate((ClientPolicyRepresentation) expected);
|
||||
clientPolicy.form().save();
|
||||
return (P) clientPolicy;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -120,6 +128,8 @@ public class Policies extends Form {
|
|||
timePolicy.form().populate((TimePolicyRepresentation) representation);
|
||||
} else if ("rules".equals(type)) {
|
||||
rulePolicy.form().populate((RulePolicyRepresentation) representation);
|
||||
} else if ("client".equals(type)) {
|
||||
clientPolicy.form().populate((ClientPolicyRepresentation) representation);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -146,6 +156,8 @@ public class Policies extends Form {
|
|||
return (P) timePolicy;
|
||||
} else if ("rules".equals(type)) {
|
||||
return (P) rulePolicy;
|
||||
} else if ("client".equals(type)) {
|
||||
return (P) clientPolicy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +185,8 @@ public class Policies extends Form {
|
|||
timePolicy.form().delete();
|
||||
} else if ("rules".equals(type)) {
|
||||
rulePolicy.form().delete();
|
||||
} else if ("client".equals(type)) {
|
||||
clientPolicy.form().delete();
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -55,7 +55,7 @@ public class AggregatePolicyManagementTest extends AbstractAuthorizationSettings
|
|||
|
||||
AuthorizationResource authorization = testRealmResource().clients().get(newClient.getId()).authorization();
|
||||
PoliciesResource policies = authorization.policies();
|
||||
RolePoliciesResource roles = policies.roles();
|
||||
RolePoliciesResource roles = policies.role();
|
||||
|
||||
roles.create(policyA);
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class AggregatePolicyManagementTest extends AbstractAuthorizationSettings
|
|||
policyC.setName("Policy C");
|
||||
policyC.addUser("test");
|
||||
|
||||
policies.users().create(policyC);
|
||||
policies.user().create(policyC);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.console.authorization;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.ClientsResource;
|
||||
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.Logic;
|
||||
import org.keycloak.testsuite.console.page.clients.authorization.policy.ClientPolicy;
|
||||
import org.keycloak.testsuite.util.ClientBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class ClientPolicyManagementTest extends AbstractAuthorizationSettingsTest {
|
||||
|
||||
@Before
|
||||
public void configureTest() {
|
||||
super.configureTest();
|
||||
ClientsResource clients = testRealmResource().clients();
|
||||
clients.create(ClientBuilder.create().clientId("client a").build());
|
||||
clients.create(ClientBuilder.create().clientId("client b").build());
|
||||
clients.create(ClientBuilder.create().clientId("client c").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws InterruptedException {
|
||||
authorizationPage.navigateTo();
|
||||
ClientPolicyRepresentation expected = new ClientPolicyRepresentation();
|
||||
|
||||
expected.setName("Test Client Policy");
|
||||
expected.setDescription("description");
|
||||
expected.addClient("client a");
|
||||
expected.addClient("client b");
|
||||
expected.addClient("client c");
|
||||
|
||||
expected = createPolicy(expected);
|
||||
|
||||
String previousName = expected.getName();
|
||||
|
||||
expected.setName("Changed Test Client Policy");
|
||||
expected.setDescription("Changed description");
|
||||
expected.setLogic(Logic.NEGATIVE);
|
||||
|
||||
expected.setClients(expected.getClients().stream().filter(client -> !client.equals("client b")).collect(Collectors.toSet()));
|
||||
|
||||
authorizationPage.navigateTo();
|
||||
authorizationPage.authorizationTabs().policies().update(previousName, expected);
|
||||
assertAlertSuccess();
|
||||
|
||||
authorizationPage.navigateTo();
|
||||
ClientPolicy actual = authorizationPage.authorizationTabs().policies().name(expected.getName());
|
||||
|
||||
assertPolicy(expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeletePolicy() throws InterruptedException {
|
||||
authorizationPage.navigateTo();
|
||||
ClientPolicyRepresentation expected = new ClientPolicyRepresentation();
|
||||
|
||||
expected.setName("Test Client Policy");
|
||||
expected.setDescription("description");
|
||||
expected.addClient("client c");
|
||||
|
||||
expected = createPolicy(expected);
|
||||
authorizationPage.navigateTo();
|
||||
authorizationPage.authorizationTabs().policies().delete(expected.getName());
|
||||
assertAlertSuccess();
|
||||
authorizationPage.navigateTo();
|
||||
assertNull(authorizationPage.authorizationTabs().policies().policies().findByName(expected.getName()));
|
||||
}
|
||||
|
||||
private ClientPolicyRepresentation createPolicy(ClientPolicyRepresentation expected) {
|
||||
ClientPolicy policy = authorizationPage.authorizationTabs().policies().create(expected);
|
||||
assertAlertSuccess();
|
||||
return assertPolicy(expected, policy);
|
||||
}
|
||||
|
||||
private ClientPolicyRepresentation assertPolicy(ClientPolicyRepresentation expected, ClientPolicy policy) {
|
||||
ClientPolicyRepresentation actual = policy.toRepresentation();
|
||||
|
||||
assertEquals(expected.getName(), actual.getName());
|
||||
assertEquals(expected.getDescription(), actual.getDescription());
|
||||
assertEquals(expected.getLogic(), actual.getLogic());
|
||||
|
||||
assertNotNull(actual.getClients());
|
||||
assertEquals(expected.getClients().size(), actual.getClients().size());
|
||||
assertEquals(0, actual.getClients().stream().filter(actualClient -> !expected.getClients().stream()
|
||||
.filter(expectedClient -> actualClient.equals(expectedClient))
|
||||
.findFirst().isPresent())
|
||||
.count());
|
||||
return actual;
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ public class ResourcePermissionManagementTest extends AbstractAuthorizationSetti
|
|||
|
||||
AuthorizationResource authorization = testRealmResource().clients().get(newClient.getId()).authorization();
|
||||
PoliciesResource policies = authorization.policies();
|
||||
RolePoliciesResource roles = policies.roles();
|
||||
RolePoliciesResource roles = policies.role();
|
||||
|
||||
roles.create(policyA);
|
||||
|
||||
|
@ -72,7 +72,7 @@ public class ResourcePermissionManagementTest extends AbstractAuthorizationSetti
|
|||
policyC.setName("Policy C");
|
||||
policyC.addUser("test");
|
||||
|
||||
policies.users().create(policyC);
|
||||
policies.user().create(policyC);
|
||||
|
||||
ResourcesResource resources = authorization.resources();
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
|
|||
|
||||
AuthorizationResource authorization = testRealmResource().clients().get(newClient.getId()).authorization();
|
||||
PoliciesResource policies = authorization.policies();
|
||||
RolePoliciesResource roles = policies.roles();
|
||||
RolePoliciesResource roles = policies.role();
|
||||
|
||||
roles.create(policyA);
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class ScopePermissionManagementTest extends AbstractAuthorizationSettings
|
|||
policyC.setName("Policy C");
|
||||
policyC.addUser("test");
|
||||
|
||||
policies.users().create(policyC);
|
||||
policies.user().create(policyC);
|
||||
|
||||
authorization.scopes().create(new ScopeRepresentation("Scope A"));
|
||||
authorization.scopes().create(new ScopeRepresentation("Scope B"));
|
||||
|
|
|
@ -1434,8 +1434,8 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
|
|||
onInitUpdate : function(policy) {
|
||||
var selectedClients = [];
|
||||
|
||||
if (policy.config.clients) {
|
||||
var clients = eval(policy.config.clients);
|
||||
if (policy.clients) {
|
||||
var clients = policy.clients;
|
||||
|
||||
for (var i = 0; i < clients.length; i++) {
|
||||
Client.get({realm: $route.current.params.realm, client: clients[i]}, function(data) {
|
||||
|
@ -1461,7 +1461,8 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
|
|||
clients.push($scope.selectedClients[i].id);
|
||||
}
|
||||
|
||||
$scope.policy.config.clients = JSON.stringify(clients);
|
||||
$scope.policy.clients = clients;
|
||||
delete $scope.policy.config;
|
||||
},
|
||||
|
||||
onInitCreate : function() {
|
||||
|
@ -1481,7 +1482,8 @@ module.controller('ResourceServerPolicyClientDetailCtrl', function($scope, $rout
|
|||
clients.push($scope.selectedClients[i].id);
|
||||
}
|
||||
|
||||
$scope.policy.config.clients = JSON.stringify(clients);
|
||||
$scope.policy.clients = clients;
|
||||
delete $scope.policy.config;
|
||||
}
|
||||
}, realm, client, $scope);
|
||||
});
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<div class="form-group clearfix" style="margin-top: -15px;">
|
||||
<label class="col-md-2 control-label"></label>
|
||||
<div class="col-sm-3">
|
||||
<table class="table table-striped table-bordered">
|
||||
<table class="table table-striped table-bordered" id="selected-clients">
|
||||
<thead>
|
||||
<tr data-ng-hide="!selectedClients.length">
|
||||
<th>{{:: 'clientId' | translate}}</th>
|
||||
|
@ -64,10 +64,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group clearfix">
|
||||
<label class="col-md-2 control-label" for="policy.logic">{{:: 'authz-policy-logic' | translate}}</label>
|
||||
<label class="col-md-2 control-label" for="logic">{{:: 'authz-policy-logic' | translate}}</label>
|
||||
|
||||
<div class="col-sm-1">
|
||||
<select class="form-control" id="policy.logic"
|
||||
<select class="form-control" id="logic"
|
||||
data-ng-model="policy.logic">
|
||||
<option value="POSITIVE">{{:: 'authz-policy-logic-positive' | translate}}</option>
|
||||
<option value="NEGATIVE">{{:: 'authz-policy-logic-negative' | translate}}</option>
|
||||
|
|
Loading…
Reference in a new issue