package org.gcube.oidc.keycloak.d4science; import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.parsers.ParserConfigurationException; import org.gcube.oidc.D4ScienceMappings.Role; import org.gcube.oidc.D4ScienceMappings.Scope; import org.gcube.oidc.keycloak.KeycloakHelper; import org.gcube.oidc.keycloak.KeycloakResourceCreationException; import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.PolicyResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.ResourceResource; import org.keycloak.admin.client.resource.RoleResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.Logic; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; import org.xml.sax.SAXException; public class ClientsCreatorFromExport { private KeycloakHelper kh; private Keycloak keycloak; private ExportParser exportParser; private String realm; // public Map role2Scope; public ClientsCreatorFromExport(String keycloakURL, String adminUsername, String adminPassword, String realm, FileInputStream exportFileFIS) throws SAXException, IOException, ParserConfigurationException, KeyManagementException, NoSuchAlgorithmException { this.exportParser = new ExportParser(exportFileFIS); this.kh = KeycloakHelper.getInstance(keycloakURL); this.keycloak = kh.newKeycloakAdmin(adminUsername, adminPassword); this.realm = realm; // role2Scope = new TreeMap<>(); // role2Scope.put(Role.ACCOUNTING_MANAGER, Scope.BELONGS); } public void createClients() throws KeycloakResourceCreationException, UnsupportedEncodingException { RealmResource realmResource = keycloak.realm(realm); for (String contextClient : exportParser.getAllContexts()) { System.out.println("adding client: " + contextClient); ClientResource client = kh.addClient(realmResource, contextClient, contextClient, contextClient + "'s context", ""); Map roleMap = new HashMap<>(); for (Role roleToAdd : Role.values()) { System.out.println("\tcreating role: " + roleToAdd); RoleResource role = kh.addRole(client, true, roleToAdd.asString(), roleToAdd.asString(), roleToAdd.asString() + " role", null); roleMap.put(roleToAdd, role); } Map scopeMap = new HashMap<>(); for (Scope scopeToAdd : Scope.values()) { ScopeRepresentation scope = new ScopeRepresentation(scopeToAdd.asString()); scopeMap.put(scopeToAdd, scope); } Set resourceScopes = new HashSet<>(scopeMap.values()); String[] resources = getClientResources(contextClient); if (resources.length > 0) { for (String resourceToAdd : resources) { System.out.println("\t\tadding resource: " + resourceToAdd); // TODO Set also the resource type String type = "urn:" + client.toRepresentation().getClientId() + ":resources:service"; ResourceResource resource = kh.addResource(client, resourceToAdd, type, resourceToAdd, false, resourceScopes, null); configureClientResource(client, roleMap, resource.toRepresentation()); } } else { configureClientResource(client, roleMap, client.authorization().resources().resources().get(0)); } } } private String[] getClientResources(String contextClient) { // TODO Implement when/if needed return new String[] {}; } private Role[] getInvolvedRoles(String resourceName) { // TODO Implement when/if needed return Role.values(); } private Set getRoleResourceScopes(String resourceName, Role role) { // TODO Implement when/if needed return Collections.emptySet(); } protected void configureClientResource(ClientResource client, Map roleMap, ResourceRepresentation resource) throws KeycloakResourceCreationException { String resourceName = resource.getName(); Set policies = new HashSet<>(); for (Role role : getInvolvedRoles(resourceName)) { Map> policyClientRoles = new HashMap<>(); policyClientRoles.put(client.toRepresentation().getClientId(), Collections.singleton(roleMap.get(role).toRepresentation().getName())); System.out.println("\t\t\tadding role resource policy for role: " + role); Set roleResourceScopes = getRoleResourceScopes(resourceName, role); PolicyResource newPR = kh.addRoleResourcePolicy(client, Collections.singleton(resourceName), roleResourceScopes, role.asString() + "_policy", Logic.POSITIVE, policyClientRoles); policies.add(newPR.toRepresentation().getName()); } System.out.println( "\t\t\tdeleting default js policy that is no more needed"); // This will also the delete default strategy client.authorization().policies().policy(client.authorization().policies().findByName("Default Policy").getId()) .remove(); System.out.println( "\t\t\tcreating new permission for role policies with affirmative strategy"); kh.addResourcePermission(client, Collections.singleton(resourceName), "Default Permission", DecisionStrategy.AFFIRMATIVE, policies); System.out.println("\t\t\tupdating the default permission on server"); } public void deleteClients() { RealmResource realmResource = keycloak.realm(realm); for (String contextClient : exportParser.getAllContexts()) { System.out.println("- deleting: " + contextClient); try { kh.removeClient(realmResource, contextClient); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } public void mapUsersWithRolesToClients() throws UnsupportedEncodingException { RealmResource realmResource = keycloak.realm(realm); Map>> usersToContextsAndRoles = exportParser.getAllUserContextsAndRoles(); for (String userName : usersToContextsAndRoles.keySet()) { System.out.println("- user: " + userName); UserResource userResource = kh.findUser(realmResource, userName); if (userResource != null) { Map> userContextsAndRoles = usersToContextsAndRoles.get(userName); for (String userContext : userContextsAndRoles.keySet()) { String clientId = userContext; System.out.println("\tcontext: " + userContext); ClientResource clientResource = kh.findClient(realmResource, clientId); if (clientResource != null) { System.out.println("\t\tmapping default role: " + Role.MEMBER.asString()); kh.mapRoleTo(userResource, clientResource, Role.MEMBER.asString()); for (String role : userContextsAndRoles.get(userContext)) { System.out.println("\t\tmapping role: " + role); kh.mapRoleTo(userResource, clientResource, role); } } else { System.err.println("Client not found on keycloak: " + userContext); } } } else { System.err.println("User not found on keycloak: " + userName); } System.out.println(); } } public ExportParser getExportParser() { return exportParser; } public static void main(String[] args) throws Exception { String serverURL = null; String username = null; String password = null; String realm = null; FileInputStream exportFileFIS = null; if (args.length < 5) { System.err.println("Missing params.\n\nUsage: " + ClientsCreatorFromExport.class.getName() + " [serverURL] [username] [password] [realm] [export_file]"); return; } else { serverURL = args[0]; username = args[1]; password = args[2]; realm = args[3]; exportFileFIS = new FileInputStream(args[4]); } ClientsCreatorFromExport creator = new ClientsCreatorFromExport(serverURL, username, password, realm, exportFileFIS); Date start = new Date(); System.out.println("Start at " + start); System.out.println("Deleting clients..."); creator.deleteClients(); System.out.println("\n\n * * * Creating clients * * *"); creator.createClients(); System.out.println("\n\n * * * Mapping users to client's roles * * *"); creator.mapUsersWithRolesToClients(); Date end = new Date(); System.out.println("Elapsed seconds: " + new Long(end.getTime() - start.getTime()).floatValue() / 1000); System.out.println("\nClients: " + creator.getExportParser().getAllContexts().size()); System.out.println("Users: " + creator.getExportParser().getAllUsers().size()); Map>> ucar = creator.getExportParser().getAllUserContextsAndRoles(); float rolesPerUserMean = 0; for (String user : ucar.keySet()) { for (String context : ucar.get(user).keySet()) { rolesPerUserMean += ucar.get(user).get(context).size() + 1; } } System.out.println("Roles per user mean: " + rolesPerUserMean / creator.getExportParser().getAllUsers().size()); } }