[keycloak-user] Adding permissions programmatically
Pedro Igor Silva
psilva at redhat.com
Mon Jun 12 13:46:58 EDT 2017
In the next release, we should get a better REST API for this. We have
added specific types for each permission/policy type and also updated the
Keycloak Java Admin Client.
On Mon, Jun 12, 2017 at 12:48 PM, Lucian Ochian <okianl at yahoo.com> wrote:
> You can use the REST Admin API or if you use Java, you can use the Admin
> Client library.You have to decide how you want to assign the permissions.
> In my case, I decided to have for each resource scopes and when I want to
> assign a user to a resource with a given scope, I check if the user has a
> user policy, if not create one and then create a scope permission for each
> user policy/resource combination where the scopes are set.
> I have some code in here just to give you an idea... This is for keycloak
> 2.5.x
> You can get to the permissions API by using the ClientResource....
>
>
> public class KeycloakGateway {
>
> @Autowired
> private KeycloakIdentityUtils identityUtils;
>
> @Autowired
> private ScopedPermissionMapper scopedPermissionMapper;
>
> private KeycloakDeployment deployment;
>
>
>
> /**
> * the client scopes; the key is the scope name, the value is the
> representation
> */
> private Map<String, ScopeRepresentation> clientScopes =
> Collections.synchronizedMap(new HashMap<>());
>
> /**
> * this field needs to be lazy loaded so that we can do testing when
> the realm is added after the framework starts
> */
> private Keycloak _keycloak;
>
>
>
> /**
> * lazy loads the keycloak; double check idiom
> * http://www.javaworld.com/article/2077568/learn-java/
> java-tip-67--lazy-instantiation.html
> *
> * @return
> */
> private Keycloak keycloak() {
> if (this._keycloak == null) {
> synchronized (KeycloakGatewayImpl.class) {
> if (this._keycloak == null) {
> this._keycloak = KeycloakBuilder.builder()
> .serverUrl(getDeployment().
> getAuthServerBaseUrl())
> .realm(getDeployment().getRealm())
> .grantType(OAuth2Constants.CLIENT_CREDENTIALS)
> .clientId(getDeployment().getResourceName())
> .clientSecret((String) deployment.
> getResourceCredentials().get("secret"))
> .resteasyClient(new ResteasyClientBuilder().
> connectionPoolSize(20).build())
> .build();
> }
> }
> }
> return _keycloak;
> }
>
> public void removeKeycloakForServiceAccount() {
> this._keycloak = null;
> }
>
> @Override
> @Retryable(value = RuntimeException.class, backoff = @Backoff(delay =
> 1000, multiplier = 2))
> public CreateUserResult createUser(String username, String firstName,
> String lastName, String email, boolean enabled) {
>
> Response response;
> Response.StatusType statusInfo;
> ErrorRepresentation message;
> UserRepresentation representation = new UserRepresentation();
> representation.setUsername(username);
> representation.setFirstName(firstName);
> representation.setLastName(lastName);
> representation.setEmail(email);
> representation.setEnabled(enabled);
>
> response = realm(getUserToken()).users().create(representation);
> statusInfo = response.getStatusInfo();
>
> message = null;
> if (statusInfo.getStatusCode() != Response.Status.CREATED.getStatusCode())
> {
> message = response.readEntity(ErrorRepresentation.class);
> }
> response.close();
>
> if (statusInfo.getStatusCode() == Response.Status.CREATED.getStatusCode())
> {
> String userUuid = ApiUtil.getCreatedId(response);
>
>
> return CreateUserResult.success(userUuid);
> }
>
> //noinspection ConstantConditions
> return CreateUserResult.failure(message.getErrorMessage());
>
> }
>
> private RealmResource realm() {
> return keycloak().realm(getDeployment().getRealm());
> }
>
> private RealmResource realm(String token) {
> KeycloakDeployment deployment = getDeployment();
> return Keycloak.getInstance(deployment.getAuthServerBaseUrl(),
> deployment.getRealm(), deployment.getResourceName(),
> token).realm(deployment.getRealm());
> }
>
> @Override
> @Retryable(value = RuntimeException.class, backoff = @Backoff(delay =
> 1000, multiplier = 2))
> public UpdateUserResult updateUser(String idmId, String username,
> String firstName, String lastName, String email, boolean enabled,
> Set<String> roles) {
>
> UserRepresentation representation = new UserRepresentation();
> representation.setUsername(username);
> representation.setFirstName(firstName);
> representation.setLastName(lastName);
> representation.setEmail(email);
> representation.setEnabled(enabled);
>
> try {
> realm(getUserToken()).users().get(idmId).update(
> representation);
> } catch (ClientErrorException e) {
> // String s = e.getResponse().readEntity(String.class);
> // return UpdateUserResult.failure(idmId, s);
> ErrorRepresentation error = e.getResponse().readEntity(
> ErrorRepresentation.class);
> return UpdateUserResult.failure(idmId,
> error.getErrorMessage());
> }
>
> updateRoles(idmId, roles, realm());
> return UpdateUserResult.success(idmId);
> }
>
> @Override
> @Retryable(value = RuntimeException.class, backoff = @Backoff(delay =
> 1000, multiplier = 2))
> public UpdateUserResult enable(String idmId, boolean enabled) {
> try {
> UserResource userResource = realm(getUserToken()).users().
> get(idmId);
> UserRepresentation userRepresentation =
> userResource.toRepresentation();
> userRepresentation.setEnabled(enabled);
> userResource.update(userRepresentation);
> } catch (ClientErrorException e) {
> ErrorRepresentation error = e.getResponse().readEntity(
> ErrorRepresentation.class);
> return UpdateUserResult.failure(idmId,
> error.getErrorMessage());
> }
> return UpdateUserResult.success(idmId);
> }
>
> @Override
> public void updateRoles(String idmId, Set<String> roles) {
> updateRoles(idmId, roles, getUserToken());
> }
>
> @Override
> @Retryable(value = RuntimeException.class, backoff = @Backoff(delay =
> 1000, multiplier = 2))
> public void updateScopePermission(ScopePermission permission) {
> ResourceRepresentation resource = findResource(permission.getResourceUri(),
> permission.getResourceType());
> setPermission(permission.getUserId(), resource.getId(),
> permission.getScopeNames());
> }
>
> @Override
> @Retryable(value = RuntimeException.class, backoff = @Backoff(delay =
> 1000, multiplier = 2))
> public void updatePermissions(List<ScopePermission> permissions) {
> permissions.forEach(this::updateScopePermission);
> }
>
> @Override
> public List<ScopePermission> userScopePermissions(String userId) {
> List<PolicyRepresentation> scopePermissions =
> findScopePermissions(userId);
> List<ScopePermission> result = scopePermissions.stream().map(policy
> -> {
> ScopePermission permission2 = new ScopePermission();
> permission2.setUserId(scopedPermissionMapper.userId(
> policy.getName()));
> String resourceId = scopedPermissionMapper.
> resourceId(policy.getName());
> ResourceRepresentation resource = findResource(resourceId);
> permission2.setResourceUri(resource.getUri());
> permission2.setResourceType(resource.getType());
>
> List<String> scopeNames = authorizationResource().
> policies().policy(policy.getId()).scopes()
> .stream().map(ScopeRepresentation::getName).
> collect(Collectors.toList());
>
> permission2.setScopeNames(scopeNames);
> return permission2;
> }).collect(Collectors.toList());
> return result;
> }
>
> private String getUserToken() {
> RefreshableKeycloakSecurityContext context = identityUtils.
> getRefreshableKeycloakSecurityContext();
> return context.getTokenString();
> }
>
>
> private synchronized void setDeployment(KeycloakDeployment
> deployment) {
> this.deployment = deployment;
> }
>
>
> public KeycloakDeployment getDeployment() {
> //http://www.javaworld.com/article/2077568/learn-java/
> java-tip-67--lazy-instantiation.html
> if (deployment == null) {
> synchronized (KeycloakGatewayImpl.class) {
> if (deployment == null) {
> setDeployment(identityUtils.getDeployment());
> }
> }
> }
> return deployment;
> }
>
> private String getRealmName() {
> return getDeployment().getRealm();
> }
>
>
>
> private List<ScopeRepresentation> getScopeRepresentations(List<String>
> scopeNames) {
> return scopeNames.stream()
> .map(scopeId -> scopesMap().get(scopeId)).
> collect(Collectors.toList());
> }
>
> @Override
> public ResourceRepresentation findResource(String id) {
> //noinspection UnnecessaryLocalVariable
> ResourceRepresentation representation =
> getClientResources().resource(id).toRepresentation();
> return representation;
> }
>
> @Override
> public ResourceRepresentation findResource(String uri, String type) {
> List<ResourceRepresentation> representations =
> getClientResources().find(null, uri, null, type, null, 0, 10);
> Preconditions.checkState(representations.size() < 2,
> String.format("More than 1 resource was found with type:%s and uri:%s ",
> type, uri));
> return representations.isEmpty() ? null : representations.get(0);
> }
>
> @Override
> @Retryable(value = RuntimeException.class, backoff = @Backoff(delay =
> 1000, multiplier = 2))
> public String createOrUpdateResource(CreateUpdateResourceRequest
> request) {
> ResourceRepresentation existing = findResource(request.getUri(),
> request.getType());
> Set<ScopeRepresentation> scopeRepresentations = new HashSet<>(
> getScopeRepresentations(request.getScopeNames()));
>
> ResourceRepresentation representation = new ResourceRepresentation(request.getName(),
> scopeRepresentations, request.getUri(), request.getType());
> if (existing == null) {
> Response response = getClientResources().create(
> representation);
> representation = response.readEntity(
> ResourceRepresentation.class);
> response.close();
> } else {
> representation.setId(existing.getId());
> getClientResources().resource(representation.getId()).
> update(representation);
> }
> return representation.getId();
> }
>
> private String getClientId() {
> //noinspection ConstantConditions
> return ApiUtil.findClientByClientId(realm(), getDeployment().
> getResourceName()).toRepresentation().getId();
> }
>
> private ResourcesResource getClientResources() {
> //noinspection ConstantConditions
> return ApiUtil.findClientByClientId(realm(), getDeployment().
> getResourceName())
> .authorization().resources();
> }
>
> private AuthorizationResource getClientResources(String clientName) {
> //noinspection ConstantConditions
> return ApiUtil.findClientByClientId(realm(), clientName)
> .authorization();
> }
>
> public AuthorizationResource authorizationResource() {
> //noinspection ConstantConditions
> return getClientResources(getDeployment().getResourceName());
> }
>
> public Map<String, ScopeRepresentation> scopesMap() {
> if (clientScopes.isEmpty()) {
> loadScopes();
> }
> return Collections.unmodifiableMap(clientScopes);
> }
>
> @Override
> public List<ScopeRepresentation> clientScopes() {
> if (clientScopes.isEmpty()) {
> loadScopes();
> }
> return new ArrayList<>(clientScopes.values());
> }
>
> @Override
> public void setPermission(String userId, String resourceId,
> List<String> scopeNames) {
> UserRepresentation user = findApplicationUser(userId);
> ResourceRepresentation resource = findResource(resourceId);
>
> PolicyRepresentation permission = findOrCreateUserPermission(user,
> resource, scopeNames);
> //create if new and it has scopes
> if (permission.getId() == null && !scopeNames.isEmpty()) {
> Response response = authorizationResource().
> policies().create(permission);
> permission = response.readEntity(PolicyRepresentation.class);
> response.close();
> return;
> }
>
> //do nothing if it's new and no scopes
> if (scopeNames.isEmpty() && permission.getId() == null) {
> return;
> }
> // remove if it exists and has no scopes
> if (scopeNames.isEmpty() && permission.getId() != null) {
> authorizationResource().policies().policy(permission.
> getId()).remove();
> return;
> }
>
> // update scopes if it exists
> if (!scopeNames.isEmpty() && permission.getId() != null) {
> permission.setConfig(buildPermissionConfig(resource,
> findOrCreateUserPolicy(user), scopeNames));
> authorizationResource().policies().policy(permission.
> getId()).update(permission);
> return;
> }
>
> }
>
> /**
> * @param user the user
> * @param resource the resource
> * @param scopeNames the scope names
> * @return a policy representation if it exists, or it will create a
> new one that is not in the IAM yet
> */
> public PolicyRepresentation findOrCreateUserPermission(UserRepresentation
> user, ResourceRepresentation resource, List<String> scopeNames) {
> PolicyRepresentation permission = findScopePermission(user,
> resource);
>
> if (permission == null) {
> PolicyRepresentation policy = findOrCreateUserPolicy(user);
>
> permission = new PolicyRepresentation();
> permission.setType("scope");
> permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
> permission.setLogic(Logic.POSITIVE);
> permission.setName(scopedPermissionMapper.scopePermissionName(user,
> resource));
> permission.setDescription(String.format("User(%s) permission
> for resource(%s)", user.getId(), resource.getId()));
> permission.setConfig(buildPermissionConfig(resource, policy,
> scopeNames));
> }
> return permission;
> }
>
> private Map<String, String> buildPermissionConfig(ResourceRepresentation
> resource, PolicyRepresentation userPolicy, List<String> scopeNames) {
> Map<String, String> config = new HashMap<>();
> config.put("applyPolicies", String.format("[\"%s\"]",
> userPolicy.getId()));
> config.put("resources", String.format("[\"%s\"]",
> resource.getId()));
> List<String> scopeIds = scopeNames.stream().map(s ->
> String.format("\"%s\"", scopesMap().get(s).getId())).
> collect(Collectors.toList());
> config.put("scopes", scopeIds.toString());
> return config;
> }
>
> public PolicyRepresentation findOrCreateUserPolicy(UserRepresentation
> user) {
> PolicyRepresentation policy = findUserPolicy(user.getId());
> if (policy == null) {
> policy = new PolicyRepresentation();
> policy.setName(user.getId());
> policy.setDescription(String.format("User policy for
> userId=%s", user.getId()));
> policy.setType("user");
> policy.setLogic(Logic.POSITIVE);
> Map<String, String> config = new HashMap<>();
> config.put("users", String.format("[%s]", user.getId()));
> policy.setConfig(config);
> Response response = authorizationResource().
> policies().create(policy);
> policy = response.readEntity(PolicyRepresentation.class);
> response.close();
> }
> return policy;
> }
>
> /**
> * @param name the name used in the search(the user id is used here)
> * @return a "user policy" that includes in the name the id of the
> user, or null otherwise
> */
> public PolicyRepresentation findUserPolicy(String name) {
> RestTemplate template = new RestTemplate();
> HttpHeaders headers = new HttpHeaders();
> headers.add("Content-Type", "application/json");
> headers.add("Authorization", String.format("Bearer %s",
> keycloak().tokenManager().getAccessTokenString()));
>
> final HttpEntity<PolicyRepresentation> entity = new
> HttpEntity<>(headers);
> String urlBase = getDeployment().getAuthServerBaseUrl() +
> "/admin/realms/" + getDeployment().getRealm() +
> "/clients/" + getClientId() +
> "/authz/resource-server/policy";
>
> String url = UriComponentsBuilder.fromUriString(urlBase)
> .queryParam("first", 0)
> .queryParam("max", 20)
> .queryParam("permission", "false")
> .queryParam("type", "user")
> .queryParam("name", name)
> // .queryParam("name", "default")
> // .queryParam("resource", "")
> .build().toUri().toString();
>
> ResponseEntity<PolicyRepresentation[]> response =
> template.exchange(url, HttpMethod.GET, entity,
> PolicyRepresentation[].class);
> PolicyRepresentation[] body = response.getBody();
> List<PolicyRepresentation> policies = Arrays.asList(body);
> Preconditions.checkState(policies.size() < 2, String.format("User
> %s has more than 1 user policies", name));
> return policies.size() == 0 ? null : policies.get(0);
> }
>
> /**
> * @param user IAM user
> * @param resource IAM resource
> * @return a scope permission that has the name in the format of
> "userId-resourceId-scoped"(max 1), or null if none found
> */
> public PolicyRepresentation findScopePermission(UserRepresentation
> user, ResourceRepresentation resource) {
> RestTemplate template = new RestTemplate();
> HttpHeaders headers = new HttpHeaders();
> headers.add("Content-Type", "application/json");
> headers.add("Authorization", String.format("Bearer %s",
> keycloak().tokenManager().getAccessTokenString()));
>
> final HttpEntity<PolicyRepresentation> entity = new
> HttpEntity<>(headers);
> //I believed this URL changed in Keycloak 3
> String urlBase = getDeployment().getAuthServerBaseUrl() +
> "/admin/realms/" + getDeployment().getRealm() +
> "/clients/" + getClientId() +
> "/authz/resource-server/policy";
>
> String url = UriComponentsBuilder.fromUriString(urlBase)
> .queryParam("first", 0)
> .queryParam("max", 20)
> .queryParam("permission", "true")
> .queryParam("type", "scope")
> .queryParam("name", scopedPermissionMapper.scopePermissionName(user,
> resource))
> .build().toUri().toString();
>
> ResponseEntity<PolicyRepresentation[]> response =
> template.exchange(url, HttpMethod.GET, entity,
> PolicyRepresentation[].class);
> PolicyRepresentation[] body = response.getBody();
> List<PolicyRepresentation> policies = Arrays.asList(body);
> Preconditions.checkState(policies.size() < 2, String.format("User
> %s has more than 1 resource permissions", user.getId()));
> return policies.size() == 0 ? null : policies.get(0);
> }
>
>
> /**
> * @param userId IAM user
> * @return all scope permission that has the name in the format of
> "userId-resourceId-scoped"
> */
> public List<PolicyRepresentation> findScopePermissions(String userId) {
> RestTemplate template = new RestTemplate();
> HttpHeaders headers = new HttpHeaders();
> headers.add("Content-Type", "application/json");
> headers.add("Authorization", String.format("Bearer %s",
> keycloak().tokenManager().getAccessTokenString()));
>
> final HttpEntity<PolicyRepresentation> entity = new
> HttpEntity<>(headers);
> //I believed this URL changed in Keycloak 3
> String urlBase = getDeployment().getAuthServerBaseUrl() +
> "/admin/realms/" + getDeployment().getRealm() +
> "/clients/" + getClientId() +
> "/authz/resource-server/policy";
>
> String url = UriComponentsBuilder.fromUriString(urlBase)
> .queryParam("first", 0)
> .queryParam("max", 1001)
> .queryParam("permission", "true")
> .queryParam("type", "scope")
> .queryParam("name", userId)
> .build().toUri().toString();
>
> ResponseEntity<PolicyRepresentation[]> response =
> template.exchange(url, HttpMethod.GET, entity,
> PolicyRepresentation[].class);
> PolicyRepresentation[] body = response.getBody();
> //noinspection UnnecessaryLocalVariable
> List<PolicyRepresentation> policies = Arrays.asList(body);
> Preconditions.checkState(policies.size() <= 1000, "Too many
> scoped permissions for user:" + userId);
> return policies;
> }
>
>
> public void loadScopes() {
> ClientResource clientResource = ApiUtil.findClientByClientId(realm(),
> deployment.getResourceName());
> //noinspection ConstantConditions
> List<ScopeRepresentation> scopes = clientResource.authorization()
> .scopes().scopes();
> Map<String, ScopeRepresentation> scopesAsMap =
> scopes.stream().collect(Collectors.toMap(ScopeRepresentation::getName, s
> -> s));
> clientScopes.clear();
> clientScopes.putAll(scopesAsMap);
> }
>
> /**
> * @return all the user's realm roles using the service account.
> */
> @Retryable(value = RuntimeException.class, backoff = @Backoff(delay =
> 1000, multiplier = 2))
> public List<RoleRepresentation> getUserRealmRoles(String userId) {
> List<RoleRepresentation> userAppRoles;
> userAppRoles = realm().users().get(userId).
> roles().realmLevel().listAll();
> return userAppRoles;
> }
> }
>
> On Monday, June 12, 2017, 10:16:13 AM CDT, matteo restelli <
> teoreste at gmail.com> wrote:
>
> Hi guys,
> how can I add permissions programmatically for a specific resource?
>
> Thank you in advance,
> Matteo
> _______________________________________________
> keycloak-user mailing list
> keycloak-user at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/keycloak-user
> _______________________________________________
> keycloak-user mailing list
> keycloak-user at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/keycloak-user
>
More information about the keycloak-user
mailing list