/* * JBoss, Home of Professional Open Source. * Copyright 2016 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * 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. */ package org.keycloak.authorization.admin; import static org.keycloak.models.utils.ModelToRepresentation.toRepresentation; import java.io.IOException; import java.util.HashMap; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.AuthorizationProvider; import org.keycloak.authorization.model.ResourceServer; import org.keycloak.authorization.store.PolicyStore; import org.keycloak.authorization.store.ResourceStore; import org.keycloak.authorization.store.ScopeStore; import org.keycloak.authorization.store.StoreFactory; import org.keycloak.exportimport.util.ExportUtils; import org.keycloak.models.ClientModel; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.authorization.DecisionStrategy; import org.keycloak.representations.idm.authorization.Logic; import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.representations.idm.authorization.ResourcePermissionRepresentation; import org.keycloak.representations.idm.authorization.ResourceRepresentation; import org.keycloak.representations.idm.authorization.ResourceServerRepresentation; import org.keycloak.services.resources.admin.RealmAuth; /** * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a> */ public class ResourceServerService { private final AuthorizationProvider authorization; private final RealmAuth auth; private final KeycloakSession session; private ResourceServer resourceServer; private final ClientModel client; public ResourceServerService(AuthorizationProvider authorization, ResourceServer resourceServer, ClientModel client, RealmAuth auth) { this.authorization = authorization; this.session = authorization.getKeycloakSession(); this.client = client; this.resourceServer = resourceServer; this.auth = auth; } public void create() { this.auth.requireManage(); UserModel serviceAccount = this.session.users().getServiceAccount(client); if (serviceAccount == null) { throw new RuntimeException("Client does not have a service account."); } this.resourceServer = this.authorization.getStoreFactory().getResourceServerStore().create(this.client.getId()); createDefaultRoles(serviceAccount); createDefaultPermission(createDefaultResource(), createDefaultPolicy()); } @PUT @Consumes("application/json") @Produces("application/json") public Response update(ResourceServerRepresentation server) { this.auth.requireManage(); this.resourceServer.setAllowRemoteResourceManagement(server.isAllowRemoteResourceManagement()); this.resourceServer.setPolicyEnforcementMode(server.getPolicyEnforcementMode()); return Response.noContent().build(); } public void delete() { this.auth.requireManage(); StoreFactory storeFactory = authorization.getStoreFactory(); ResourceStore resourceStore = storeFactory.getResourceStore(); String id = resourceServer.getId(); resourceStore.findByResourceServer(id).forEach(resource -> resourceStore.delete(resource.getId())); ScopeStore scopeStore = storeFactory.getScopeStore(); scopeStore.findByResourceServer(id).forEach(scope -> scopeStore.delete(scope.getId())); PolicyStore policyStore = storeFactory.getPolicyStore(); policyStore.findByResourceServer(id).forEach(scope -> policyStore.delete(scope.getId())); storeFactory.getResourceServerStore().delete(id); } @GET @Produces("application/json") public Response findById() { this.auth.requireView(); return Response.ok(toRepresentation(this.resourceServer, this.client)).build(); } @Path("/settings") @GET @Produces("application/json") public Response exportSettings() { this.auth.requireManage(); return Response.ok(ExportUtils.exportAuthorizationSettings(session, client)).build(); } @Path("/import") @POST @Consumes(MediaType.APPLICATION_JSON) public Response importSettings(@Context final UriInfo uriInfo, ResourceServerRepresentation rep) throws IOException { this.auth.requireManage(); rep.setClientId(client.getId()); RepresentationToModel.toModel(rep, authorization); return Response.noContent().build(); } @Path("/resource") public ResourceSetService getResourceSetResource() { ResourceSetService resource = new ResourceSetService(this.resourceServer, this.authorization, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); return resource; } @Path("/scope") public ScopeService getScopeResource() { ScopeService resource = new ScopeService(this.resourceServer, this.authorization, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); return resource; } @Path("/policy") public PolicyService getPolicyResource() { PolicyService resource = new PolicyService(this.resourceServer, this.authorization, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); return resource; } @Path("/permission") public Object getPermissionTypeResource() { this.auth.requireView(); PermissionService resource = new PermissionService(this.resourceServer, this.authorization, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); return resource; } private void createDefaultPermission(ResourceRepresentation resource, PolicyRepresentation policy) { ResourcePermissionRepresentation defaultPermission = new ResourcePermissionRepresentation(); defaultPermission.setName("Default Permission"); defaultPermission.setDescription("A permission that applies to the default resource type"); defaultPermission.setDecisionStrategy(DecisionStrategy.UNANIMOUS); defaultPermission.setLogic(Logic.POSITIVE); defaultPermission.setResourceType(resource.getType()); defaultPermission.addPolicy(policy.getName()); getPolicyResource().create(defaultPermission); } private PolicyRepresentation createDefaultPolicy() { PolicyRepresentation defaultPolicy = new PolicyRepresentation(); defaultPolicy.setName("Default Policy"); defaultPolicy.setDescription("A policy that grants access only for users within this realm"); defaultPolicy.setType("js"); defaultPolicy.setDecisionStrategy(DecisionStrategy.AFFIRMATIVE); defaultPolicy.setLogic(Logic.POSITIVE); HashMap<String, String> defaultPolicyConfig = new HashMap<>(); defaultPolicyConfig.put("code", "// by default, grants any permission associated with this policy\n$evaluation.grant();\n"); defaultPolicy.setConfig(defaultPolicyConfig); getPolicyResource().create(defaultPolicy); return defaultPolicy; } private ResourceRepresentation createDefaultResource() { ResourceRepresentation defaultResource = new ResourceRepresentation(); defaultResource.setName("Default Resource"); defaultResource.setUri("/*"); defaultResource.setType("urn:" + this.client.getClientId() + ":resources:default"); getResourceSetResource().create(defaultResource); return defaultResource; } private void createDefaultRoles(UserModel serviceAccount) { RoleModel umaProtectionRole = client.getRole(Constants.AUTHZ_UMA_PROTECTION); if (umaProtectionRole == null) { umaProtectionRole = client.addRole(Constants.AUTHZ_UMA_PROTECTION); } if (!serviceAccount.hasRole(umaProtectionRole)) { serviceAccount.grantRole(umaProtectionRole); } } }