[keycloak-user] Adding permissions programmatically
Lucian Ochian
okianl at yahoo.com
Mon Jun 12 13:53:25 EDT 2017
Matteo,
This is the method where you can start to navigate some of this code:
public void setPermission(String userId, String resourceId, List<String> scopeNames)
On Monday, June 12, 2017, 12:47:01 PM CDT, Pedro Igor Silva <psilva at redhat.com> wrote:
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 removeKeycloakForServiceAccoun t() {
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() {
RefreshableKeycloakSecurityCon text context = identityUtils. getRefreshableKeycloakSecurity Context();
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