Verified Commit 24b83650 authored by Julien Boubechtoula's avatar Julien Boubechtoula 🤞🏼

Init keycloak permissions

parent 6c669417
Pipeline #151654300 passed with stage
in 7 minutes and 7 seconds
......@@ -19,7 +19,7 @@
*
*/
package org.kathra.usermanager;
package org.kathra.usermanager.configuration;
import org.kathra.utils.ConfigManager;
......@@ -32,16 +32,20 @@ public class Config extends ConfigManager {
private String keycloakRealm;
private String keycloakRealmAdmin;
private String keycloakClientId;
private String keycloakUsername;
private String keycloakPassword;
private String keycloakAdminClientId;
private String keycloakAdminUsername;
private String keycloakAdminPassword;
private String jobVerifyPermission;
public Config() {
keycloakAuthUrl = getProperty("KEYCLOAK_ADMIN_AUTH_URL");
keycloakRealmAdmin = getProperty("KEYCLOAK_ADMIN_REALM");
keycloakRealm = getProperty("KEYCLOAK_REALM");
keycloakClientId = getProperty("KEYCLOAK_ADMIN_CLIENT_ID");
keycloakUsername = getProperty("KEYCLOAK_ADMIN_USERNAME");
keycloakPassword = getProperty("KEYCLOAK_ADMIN_PASSWORD");
keycloakClientId = getProperty("KEYCLOAK_CLIENT_ID");
keycloakAdminClientId = getProperty("KEYCLOAK_ADMIN_CLIENT_ID");
keycloakAdminUsername = getProperty("KEYCLOAK_ADMIN_USERNAME");
keycloakAdminPassword = getProperty("KEYCLOAK_ADMIN_PASSWORD");
jobVerifyPermission = getProperty("SCHEDULE_CHECK_PERMISSION_DELAY", "600s");
}
public String getKeycloakAuthUrl() {
......@@ -52,20 +56,28 @@ public class Config extends ConfigManager {
return keycloakRealm;
}
public String getKeycloakClientId() {
return keycloakClientId;
public String getKeycloakAdminClientId() {
return keycloakAdminClientId;
}
public String getKeycloakUsername() {
return keycloakUsername;
public String getKeycloakAdminUsername() {
return keycloakAdminUsername;
}
public String getKeycloakPassword() {
return keycloakPassword;
public String getKeycloakAdminPassword() {
return keycloakAdminPassword;
}
public String getKeycloakRealmAdmin() {
return keycloakRealmAdmin;
}
public String getKeycloakClientId() {
return keycloakClientId;
}
public String getJobVerifyPermission() {
return jobVerifyPermission;
}
}
package org.kathra.usermanager.configuration;
import org.apache.camel.builder.RouteBuilder;
import org.kathra.usermanager.services.PermissionsServices;
public class ConfigureKeycloakScheduler extends RouteBuilder {
@Override
public void configure() {
Config config = new Config();
from("scheduler://foo?delay="+config.getJobVerifyPermission()).process(PermissionsServices.getInstance()).to("mock:success");
}
}
\ No newline at end of file
......@@ -23,7 +23,7 @@ package org.kathra.usermanager.controller;
import org.kathra.core.model.Assignation;
import org.kathra.core.model.Group;
import org.kathra.core.model.User;
import org.kathra.usermanager.Config;
import org.kathra.usermanager.configuration.Config;
import org.kathra.usermanager.service.UserManagerService;
import org.kathra.usermanager.services.KeycloakService;
import org.apache.camel.cdi.ContextName;
......@@ -48,9 +48,9 @@ public class UserManagerController implements UserManagerService {
config.getKeycloakAuthUrl(),
config.getKeycloakRealmAdmin(),
config.getKeycloakRealm(),
config.getKeycloakClientId(),
config.getKeycloakUsername(),
config.getKeycloakPassword());
config.getKeycloakAdminClientId(),
config.getKeycloakAdminUsername(),
config.getKeycloakAdminPassword());
}
public UserManagerController(KeycloakService keycloakService) {
this.keycloakService = keycloakService;
......
package org.kathra.usermanager.services;
import com.google.common.io.Resources;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.kathra.core.model.Component;
import org.kathra.core.model.Implementation;
import org.kathra.usermanager.configuration.Config;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.authorization.*;
import javax.ws.rs.NotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.logging.Logger;
public class PermissionsServices implements Processor {
private Logger logger = Logger.getLogger(PermissionsServices.class.getName());
private static PermissionsServices instance;
private Keycloak keycloak;
private String realmName;
private String clientName;
private final String[] resources = {
Component.class.getSimpleName().toLowerCase(),
Implementation.class.getSimpleName().toLowerCase()
};
public PermissionsServices(Keycloak keycloak, String realmName, String clientName) {
this.keycloak = keycloak;
this.realmName = realmName;
this.clientName = clientName;
}
public static PermissionsServices getInstance() {
if (instance == null) {
Config config = new Config();
Keycloak keycloak = KeycloakBuilder.builder()
.serverUrl(config.getKeycloakAuthUrl())
.grantType(OAuth2Constants.PASSWORD)
.realm(config.getKeycloakRealmAdmin())
.clientId(config.getKeycloakAdminClientId())
.username(config.getKeycloakAdminUsername())
.password(config.getKeycloakAdminPassword())
.build();
instance = new PermissionsServices(keycloak, config.getKeycloakRealm(), config.getKeycloakClientId());
}
return instance;
}
@Override
public void process(Exchange exchange) throws Exception {
logger.info("Start sync keycloak permission");
ClientRepresentation client = getClient(clientName);
JSPolicyRepresentation policy = initPolicy(client, "OnlyUsers", "Only users from shared group policy");
for (String resource:resources) {
for (Scope access:Scope.values()) {
String scopeName = "kathra:scope:"+resource+":"+access.name().toLowerCase();
ScopeRepresentation scope = initClientScope(client, scopeName);
initPermissionScopeBased("Only users from shared group can "+access+" "+resource, client, scope, policy);
}
}
}
private ClientRepresentation getClient(String clientName) {
return keycloak.realm(realmName).clients().findAll().stream().filter(clientRepresentation -> clientRepresentation.getName().equals(clientName)).findFirst().get();
}
private ClientResource getClientResource(ClientRepresentation client) {
return keycloak.realm(realmName).clients().get(client.getId());
}
private JSPolicyRepresentation initPolicy(ClientRepresentation client, String policyName, String description) throws IOException {
logger.info("Init policy " + policyName);
try {
return getClientResource(client).authorization().policies().js().findByName(policyName);
} catch (NotFoundException e) {}
JSPolicyRepresentation policyRepresentation = new JSPolicyRepresentation();
policyRepresentation.setName(policyName);
policyRepresentation.setDescription(description);
policyRepresentation.setLogic(Logic.POSITIVE);
policyRepresentation.setCode(Resources.toString(this.getClass().getResource("/policy.js"), StandardCharsets.UTF_8));
getClientResource(client).authorization().policies().js().create(policyRepresentation);
return getClientResource(client).authorization().policies().js().findByName(policyName);
}
private ScopeRepresentation initClientScope(ClientRepresentation client, String clientScopeName) {
logger.info("Init client scope " + clientScopeName);
try {
return getClientResource(client).authorization().scopes().scopes().stream().filter(i -> i.getName().equals(clientScopeName)).findFirst().orElseThrow(() -> new NotFoundException());
} catch(NotFoundException e) {}
ScopeRepresentation scope = new ScopeRepresentation();
scope.setName(clientScopeName);
getClientResource(client).authorization().scopes().create(scope);
return getClientResource(client).authorization().scopes().scopes().stream().filter(i -> i.getName().equals(clientScopeName)).findFirst().orElseThrow(() -> new NotFoundException());
}
private ScopePermissionRepresentation initPermissionScopeBased(String name, ClientRepresentation client, ScopeRepresentation scope, JSPolicyRepresentation policy) {
logger.info("Init client permission scope based " + name);
try {
return getClientResource(client).authorization().permissions().scope().findByName(name);
} catch(NotFoundException e) {}
ScopePermissionRepresentation permission = new ScopePermissionRepresentation();
permission.setName(name);
permission.setLogic(Logic.POSITIVE);
permission.setDecisionStrategy(DecisionStrategy.UNANIMOUS);
permission.addScope(scope.getId());
permission.addPolicy(policy.getId());
getClientResource(client).authorization().permissions().scope().create(permission);
return getClientResource(client).authorization().permissions().scope().findByName(name);
}
}
/*
* Copyright 2019 The Kathra Authors.
*
* 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.
*
* Contributors:
*
* IRT SystemX (https://www.kathra.org/)
*
*/
package org.kathra.usermanager.services;
public enum Scope {
READ("read"), UPDATE("update"), DELETE("delete");
private final String scopeName;
Scope(String scopeName) {
this.scopeName = scopeName;
}
public String getScopeName(){
return this.scopeName;
}
}
var context = $evaluation.getContext();
var permission = $evaluation.getPermission();
var identity = context.getIdentity();
var resource = permission.getResource();
var identityAttr = identity.getAttributes().toMap();
var groups = identityAttr.get('groups');
// Prefixing owner-group path with leading /, to match Keycloak groups mapping ['/group1', '/group2']
var ownerGroup = '/' + resource.getSingleAttribute('owner-group');
if (resource.getSingleAttribute('owner-group') != null && resource.getSingleAttribute('owner-group').startsWith('/')){
ownerGroup = resource.getSingleAttribute('owner-group');
}
var groupId = '/' + resource.getSingleAttribute('groupId');
if (resource.getSingleAttribute('groupId') != null && resource.getSingleAttribute('groupId').startsWith('/')){
groupId = resource.getSingleAttribute('groupId');
}
var groupPath = '/' + resource.getSingleAttribute('groupPath');
if (resource.getSingleAttribute('groupPath') != null && resource.getSingleAttribute('groupPath').startsWith('/')){
groupPath = resource.getSingleAttribute('groupPath');
}
for(var i=0; i<groups.length; i++){
print('Current User Group: ' + groups[i]);
if(ownerGroup == groups[i] || groupId == groups[i] || groupPath == groups[i]){
$evaluation.grant();
}
}
\ No newline at end of file
......@@ -73,6 +73,7 @@ public class UserManagerControllerTest {
group.setName(GROUP);
group.setPath("rootGroup/group");
group.setSubGroups(subGroups);
group.setSubGroups(subGroups);
UserRepresentation user = new UserRepresentation();
user.setId("userId");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment