[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