From ffc7542d4d634fa92ecb4207a22c9fdd2f075968 Mon Sep 17 00:00:00 2001 From: mharindu Date: Thu, 28 Apr 2016 11:36:34 +0530 Subject: [PATCH 1/4] Modified authorization to support API gateway --- .../apimgt/annotations/api/Permission.java | 36 ++ .../publisher/APIPublisherServiceImpl.java | 204 +++++----- .../webapp/publisher/APIPublisherUtil.java | 82 ++-- .../webapp/publisher/config/APIResource.java | 133 +++---- .../config/PermissionConfiguration.java | 45 +++ .../config/PermissionManagementException.java | 60 +++ .../APIPublisherLifecycleListener.java | 370 +++++++++--------- .../lifecycle/util/AnnotationUtil.java | 104 +++-- .../lifecycle/util/PermissionUtils.java | 91 +++++ .../WebAppDeploymentLifecycleListener.java | 44 +-- .../pom.xml | 9 +- 11 files changed, 734 insertions(+), 444 deletions(-) create mode 100644 components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java create mode 100644 components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionConfiguration.java create mode 100644 components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionManagementException.java create mode 100644 components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/PermissionUtils.java diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java new file mode 100644 index 0000000000..5cb590d3a5 --- /dev/null +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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.wso2.carbon.apimgt.annotations.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This class is the representation of custom developed Permission annotation. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Permission { + + String scope(); + String[] permissions(); + +} diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java index 3ce653c6c8..1d29863f86 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java @@ -18,16 +18,17 @@ */ package org.wso2.carbon.apimgt.webapp.publisher; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.APIProvider; import org.wso2.carbon.apimgt.api.FaultGatewaysException; -import org.wso2.carbon.apimgt.api.model.API; -import org.wso2.carbon.apimgt.api.model.APIIdentifier; -import org.wso2.carbon.apimgt.api.model.APIStatus; -import org.wso2.carbon.apimgt.api.model.URITemplate; +import org.wso2.carbon.apimgt.api.model.*; import org.wso2.carbon.apimgt.impl.APIManagerFactory; import org.wso2.carbon.apimgt.webapp.publisher.internal.APIPublisherDataHolder; import org.wso2.carbon.context.PrivilegedCarbonContext; @@ -38,10 +39,7 @@ import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import javax.xml.stream.XMLStreamException; import java.io.FileNotFoundException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; /** * This class represents the concrete implementation of the APIPublisherService that corresponds to providing all @@ -50,96 +48,122 @@ import java.util.Map; public class APIPublisherServiceImpl implements APIPublisherService { private static final Log log = LogFactory.getLog(APIPublisherServiceImpl.class); - private static final String PUBLISH_ACTION = "Publish"; + private static final String PUBLISH_ACTION = "Publish"; - @Override - public void publishAPI(final API api) throws APIManagementException, FaultGatewaysException { - String tenantDomain = MultitenantUtils.getTenantDomain(api.getApiOwner()); - PrivilegedCarbonContext.startTenantFlow(); - PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); - try { - int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); - // Below code snippet is added to load API Lifecycle in tenant mode. - RegistryService registryService = APIPublisherDataHolder.getInstance().getRegistryService(); - CommonUtil.addDefaultLifecyclesIfNotAvailable(registryService.getConfigSystemRegistry(tenantId), - CommonUtil.getRootSystemRegistry(tenantId)); - APIProvider provider = APIManagerFactory.getInstance().getAPIProvider(api.getApiOwner()); - MultitenantUtils.getTenantDomain(api.getApiOwner()); - if (provider != null) { - if (provider.isDuplicateContextTemplate(api.getContext())) { - throw new APIManagementException( - "Error occurred while adding the API. A duplicate API" + - " context already exists for " + api.getContext()); - } - if (!provider.isAPIAvailable(api.getId())) { - provider.addAPI(api); - provider.changeLifeCycleStatus(api.getId(), PUBLISH_ACTION); - if (log.isDebugEnabled()) { - log.debug("Successfully published API '" + api.getId().getApiName() + - "' with context '" + api.getContext() + "' and version '" - + api.getId().getVersion() + "'"); - } - } else { - api.setStatus(APIStatus.PUBLISHED); - provider.updateAPI(api); - if (log.isDebugEnabled()) { - log.debug("An API already exists with the name '" + api.getId().getApiName() + - "', context '" + api.getContext() + "' and version '" - + api.getId().getVersion() + "'. Thus, the API config is updated"); - } - } - provider.saveSwagger20Definition(api.getId(), createSwaggerDefinition(api)); - } else { - throw new APIManagementException("API provider configured for the given API configuration " + - "is null. Thus, the API is not published"); - } - } catch (FileNotFoundException e) { - throw new APIManagementException("Failed to retrieve life cycle file ", e); - } catch (RegistryException e) { - throw new APIManagementException("Failed to access the registry ", e); - } catch (XMLStreamException e) { - throw new APIManagementException("Failed parsing the lifecycle xml.", e); - } finally { - PrivilegedCarbonContext.endTenantFlow(); - } - } + @Override + public void publishAPI(final API api) throws APIManagementException, FaultGatewaysException { + String tenantDomain = MultitenantUtils.getTenantDomain(api.getApiOwner()); + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); + try { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + // Below code snippet is added to load API Lifecycle in tenant mode. + RegistryService registryService = APIPublisherDataHolder.getInstance().getRegistryService(); + CommonUtil.addDefaultLifecyclesIfNotAvailable(registryService.getConfigSystemRegistry(tenantId), + CommonUtil.getRootSystemRegistry(tenantId)); + APIProvider provider = APIManagerFactory.getInstance().getAPIProvider(api.getApiOwner()); + MultitenantUtils.getTenantDomain(api.getApiOwner()); + if (provider != null) { + if (provider.isDuplicateContextTemplate(api.getContext())) { + throw new APIManagementException( + "Error occurred while adding the API. A duplicate API" + + " context already exists for " + api.getContext()); + } + if (!provider.isAPIAvailable(api.getId())) { + provider.addAPI(api); + provider.changeLifeCycleStatus(api.getId(), PUBLISH_ACTION); + if (log.isDebugEnabled()) { + log.debug("Successfully published API '" + api.getId().getApiName() + + "' with context '" + api.getContext() + "' and version '" + + api.getId().getVersion() + "'"); + } + } else { + api.setStatus(provider.getAPI(api.getId()).getStatus()); + provider.updateAPI(api); + if (log.isDebugEnabled()) { + log.debug("An API already exists with the name '" + api.getId().getApiName() + + "', context '" + api.getContext() + "' and version '" + + api.getId().getVersion() + "'. Thus, the API config is updated"); + } + } + provider.saveSwagger20Definition(api.getId(), createSwaggerDefinition(api)); + } else { + throw new APIManagementException("API provider configured for the given API configuration " + + "is null. Thus, the API is not published"); + } + } catch (FileNotFoundException e) { + throw new APIManagementException("Failed to retrieve life cycle file ", e); + } catch (RegistryException e) { + throw new APIManagementException("Failed to access the registry ", e); + } catch (XMLStreamException e) { + throw new APIManagementException("Failed parsing the lifecycle xml.", e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } + + private String createSwaggerDefinition(API api) { + Map httpVerbsMap = new HashMap<>(); + List scopes = new ArrayList<>(); - private String createSwaggerDefinition(API api) { - Map httpVerbsMap = new HashMap<>(); + for (URITemplate uriTemplate : api.getUriTemplates()) { + JsonObject authType = new JsonObject(); + authType.addProperty("x-auth-type", "Application%20%26%20Application%20User"); + JsonObject response = new JsonObject(); + response.addProperty("200", ""); - for (URITemplate uriTemplate : api.getUriTemplates()) { - JsonObject response = new JsonObject(); - response.addProperty("200", ""); + JsonObject responses = new JsonObject(); + responses.add("responses", response); + JsonObject httpVerbs = httpVerbsMap.get(uriTemplate.getUriTemplate()); + if (httpVerbs == null) { + httpVerbs = new JsonObject(); + } + JsonObject httpVerb = new JsonObject(); + httpVerb.add("responses", response); - JsonObject responses = new JsonObject(); - responses.add("responses", response); - JsonObject httpVerbs = httpVerbsMap.get(uriTemplate.getUriTemplate()); - if (httpVerbs == null) { - httpVerbs = new JsonObject(); - } - httpVerbs.add(uriTemplate.getHTTPVerb().toLowerCase(), responses); - httpVerbsMap.put(uriTemplate.getUriTemplate(), httpVerbs); - } + httpVerb.addProperty("x-auth-type", "Application%20%26%20Application%20User"); + httpVerb.addProperty("x-throttling-tier", "Unlimited"); + if (uriTemplate.getScope() != null) { + httpVerb.addProperty("x-scope", uriTemplate.getScope().getName()); + scopes.add(uriTemplate.getScope()); + } + httpVerbs.add(uriTemplate.getHTTPVerb().toLowerCase(), httpVerb); + httpVerbsMap.put(uriTemplate.getUriTemplate(), httpVerbs); + } - Iterator it = httpVerbsMap.entrySet().iterator(); - JsonObject paths = new JsonObject(); - while (it.hasNext()) { - Map.Entry pair = (Map.Entry) it.next(); - paths.add(pair.getKey(), pair.getValue()); - it.remove(); - } + Iterator it = httpVerbsMap.entrySet().iterator(); + JsonObject paths = new JsonObject(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + paths.add(pair.getKey(), pair.getValue()); + it.remove(); + } - JsonObject info = new JsonObject(); - info.addProperty("title", api.getId().getApiName()); - info.addProperty("version", api.getId().getVersion()); + JsonObject info = new JsonObject(); + info.addProperty("title", api.getId().getApiName()); + info.addProperty("version", api.getId().getVersion()); - JsonObject swaggerDefinition = new JsonObject(); - swaggerDefinition.add("paths", paths); - swaggerDefinition.addProperty("swagger", "2.0"); - swaggerDefinition.add("info", info); + JsonObject swaggerDefinition = new JsonObject(); + swaggerDefinition.add("paths", paths); + swaggerDefinition.addProperty("swagger", "2.0"); + swaggerDefinition.add("info", info); - return swaggerDefinition.toString(); - } + // adding scopes to definition + if (!api.getScopes().isEmpty()) { + Gson gson = new Gson(); + JsonElement element = gson.toJsonTree(api.getScopes(), new TypeToken>() { + }.getType()); + if (element != null) { + JsonArray apiScopes = element.getAsJsonArray(); + JsonObject apim = new JsonObject(); + apim.add("x-wso2-scopes", apiScopes); + JsonObject wso2Security = new JsonObject(); + wso2Security.add("apim", apim); + swaggerDefinition.add("x-wso2-security", wso2Security); + } + } + return swaggerDefinition.toString(); + } @Override public void removeAPI(APIIdentifier id) throws APIManagementException { diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java index 9ae9340513..42644a6766 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java @@ -24,35 +24,18 @@ import org.wso2.carbon.apimgt.api.model.*; import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.webapp.publisher.internal.APIPublisherDataHolder; import org.wso2.carbon.base.MultitenantConstants; -import org.wso2.carbon.user.api.TenantManager; -import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.ConfigurationContextService; import org.wso2.carbon.utils.NetworkUtils; -import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import java.util.*; public class APIPublisherUtil { private static final String DEFAULT_API_VERSION = "1.0.0"; - public static final String API_VERSION_PARAM="{version}"; - public static final String API_PUBLISH_ENVIRONEMENT = "Production and Sandbox"; + private static final String API_VERSION_PARAM="{version}"; + private static final String API_PUBLISH_ENVIRONMENT = "Production and Sandbox"; - enum HTTPMethod { - GET, POST, DELETE, PUT, OPTIONS - } - - private static List httpMethods; - - static { - httpMethods = new ArrayList(5); - httpMethods.add(HTTPMethod.GET); - httpMethods.add(HTTPMethod.POST); - httpMethods.add(HTTPMethod.DELETE); - httpMethods.add(HTTPMethod.PUT); - httpMethods.add(HTTPMethod.OPTIONS); - } public static API getAPI(APIConfig config) throws APIManagementException { APIProvider provider = config.getProvider(); @@ -79,11 +62,10 @@ public class APIPublisherUtil { api.setStatus(APIStatus.CREATED); api.setTransports(config.getTransports()); api.setContextTemplate(config.getContextTemplate()); - api.setUriTemplates(config.getUriTemplates()); - Set environements = new HashSet<>(); - environements.add(API_PUBLISH_ENVIRONEMENT); - api.setEnvironments(environements); - Set tiers = new HashSet(); + Set environments = new HashSet<>(); + environments.add(API_PUBLISH_ENVIRONMENT); + api.setEnvironments(environments); + Set tiers = new HashSet<>(); tiers.add(new Tier(APIConstants.UNLIMITED_TIER)); api.addAvailableTiers(tiers); if (config.isSharedWithAllTenants()) { @@ -95,7 +77,8 @@ public class APIPublisherUtil { } api.setResponseCache(APIConstants.DISABLED); - String endpointConfig = "{\"production_endpoints\":{\"url\":\" " + config.getEndpoint() + "\",\"config\":null},\"implementation_status\":\"managed\",\"endpoint_type\":\"http\"}"; + String endpointConfig = "{\"production_endpoints\":{\"url\":\" " + config.getEndpoint() + + "\",\"config\":null},\"implementation_status\":\"managed\",\"endpoint_type\":\"http\"}"; api.setEndpointConfig(endpointConfig); if ("".equals(id.getVersion()) || (DEFAULT_API_VERSION.equals(id.getVersion()))) { @@ -106,35 +89,31 @@ public class APIPublisherUtil { Set tags = new HashSet<>(Arrays.asList(config.getTags())); api.addTags(tags); } - return api; - } - private static Set getURITemplates(String endpoint, String authType) { - Set uriTemplates = new LinkedHashSet(); - if (APIConstants.AUTH_NO_AUTHENTICATION.equals(authType)) { - for (HTTPMethod method : httpMethods) { - URITemplate template = new URITemplate(); - template.setAuthType(APIConstants.AUTH_NO_AUTHENTICATION); - template.setHTTPVerb(method.toString()); - template.setResourceURI(endpoint); - template.setUriTemplate("/*"); - uriTemplates.add(template); - } - } else { - for (HTTPMethod method : httpMethods) { - URITemplate template = new URITemplate(); - if (HTTPMethod.OPTIONS.equals(method)) { - template.setAuthType(APIConstants.AUTH_NO_AUTHENTICATION); - } else { - template.setAuthType(APIConstants.AUTH_APPLICATION_OR_USER_LEVEL_TOKEN); + // adding scopes to the api + Set uriTemplates = config.getUriTemplates(); + Map apiScopes = new HashMap<>(); + + if (uriTemplates != null) { + // this creates distinct scopes list + for (URITemplate template : uriTemplates) { + Scope scope = template.getScope(); + if (scope != null) { + if (apiScopes.get(scope.getKey()) == null) { + apiScopes.put(scope.getKey(), scope); + } else { + // this has to be done because of the use of pass by reference + // where same object reference of scope should be available for both + // api scope and uri template scope + template.setScope(apiScopes.get(scope.getKey())); + } } - template.setHTTPVerb(method.toString()); - template.setResourceURI(endpoint); - template.setUriTemplate("/*"); - uriTemplates.add(template); } + Set scopes = new HashSet<>(apiScopes.values()); + api.setScopes(scopes); + api.setUriTemplates(uriTemplates); } - return uriTemplates; + return api; } public static String getServerBaseUrl() { @@ -163,7 +142,8 @@ public class APIPublisherUtil { } /** - * When an input is having '@',replace it with '-AT-' [This is required to persist API data in registry,as registry paths don't allow '@' sign.] + * When an input is having '@',replace it with '-AT-' + * [This is required to persist API data in registry,as registry paths don't allow '@' sign.] * @param input inputString * @return String modifiedString */ diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResource.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResource.java index 2f28fbe6a5..577f4efecb 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResource.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/APIResource.java @@ -18,70 +18,71 @@ package org.wso2.carbon.apimgt.webapp.publisher.config; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -@XmlRootElement(name = "Resource") -public class APIResource{ - - private String AuthType; - private String HttpVerb; - private String Uri; - private String UriTemplate; - private String consumes; - private String produces; - - public String getAuthType() { - return AuthType; - } - - @XmlElement(name = "AuthType", required = true) - public void setAuthType(String authType) { - AuthType = authType; - } - - public String getHttpVerb() { - return HttpVerb; - } - - @XmlElement(name = "HttpVerb", required = true) - public void setHttpVerb(String httpVerb) { - HttpVerb = httpVerb; - } - - public String getUri() { - return Uri; - } - - @XmlElement(name = "Uri", required = true) - public void setUri(String uri) { - Uri = uri; - } - - public String getUriTemplate() { - return UriTemplate; - } - - @XmlElement(name = "UriTemplate", required = true) - public void setUriTemplate(String uriTemplate) { - UriTemplate = uriTemplate; - } - - public String getConsumes() { - return consumes; - } - - @XmlElement(name = "Consumes", required = true) - public void setConsumes(String consumes) { - this.consumes = consumes; - } - - public String getProduces() { - return produces; - } - - @XmlElement(name = "Produces", required = true) - public void setProduces(String produces) { - this.produces = produces; - } +import org.wso2.carbon.apimgt.api.model.Scope; + +public class APIResource { + + private String AuthType; + private String HttpVerb; + private String Uri; + private String UriTemplate; + private String consumes; + private String produces; + private Scope scope; + + public String getAuthType() { + return AuthType; + } + + public void setAuthType(String authType) { + AuthType = authType; + } + + public String getHttpVerb() { + return HttpVerb; + } + + public void setHttpVerb(String httpVerb) { + HttpVerb = httpVerb; + } + + public String getUri() { + return Uri; + } + + public void setUri(String uri) { + Uri = uri; + } + + public String getUriTemplate() { + return UriTemplate; + } + + public void setUriTemplate(String uriTemplate) { + UriTemplate = uriTemplate; + } + + public String getConsumes() { + return consumes; + } + + public void setConsumes(String consumes) { + this.consumes = consumes; + } + + public String getProduces() { + return produces; + } + + public void setProduces(String produces) { + this.produces = produces; + } + + public Scope getScope() { + return scope; + } + + public void setScope(Scope scope) { + this.scope = scope; + } } diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionConfiguration.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionConfiguration.java new file mode 100644 index 0000000000..7d16d198b9 --- /dev/null +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionConfiguration.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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.wso2.carbon.apimgt.webapp.publisher.config; + +/** + * This class represents the information related to permissions. + */ +public class PermissionConfiguration { + + private String scopeName; + private String[] permissions; + + public String getScopeName() { + return scopeName; + } + + public void setScopeName(String scope) { + this.scopeName = scope; + } + + public String[] getPermissions() { + return permissions; + } + + public void setPermissions(String[] permissions) { + this.permissions = permissions; + } + +} diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionManagementException.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionManagementException.java new file mode 100644 index 0000000000..651c285723 --- /dev/null +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/config/PermissionManagementException.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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.wso2.carbon.apimgt.webapp.publisher.config; + +/** + * Custom exception class of Permission related operations. + */ +public class PermissionManagementException extends Exception { + + private static final long serialVersionUID = -3151279311929070298L; + + private String errorMessage; + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public PermissionManagementException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public PermissionManagementException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public PermissionManagementException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public PermissionManagementException() { + super(); + } + + public PermissionManagementException(Throwable cause) { + super(cause); + } + +} diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/listener/APIPublisherLifecycleListener.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/listener/APIPublisherLifecycleListener.java index 0b4a3cff91..f9fbad7c48 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/listener/APIPublisherLifecycleListener.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/listener/APIPublisherLifecycleListener.java @@ -32,6 +32,7 @@ import org.wso2.carbon.apimgt.webapp.publisher.config.APIResourceConfiguration; import org.wso2.carbon.apimgt.webapp.publisher.internal.APIPublisherDataHolder; import org.wso2.carbon.apimgt.webapp.publisher.lifecycle.util.AnnotationUtil; import org.wso2.carbon.base.MultitenantConstants; + import javax.servlet.ServletContext; import java.io.IOException; import java.util.LinkedHashSet; @@ -41,189 +42,190 @@ import java.util.Set; @SuppressWarnings("unused") public class APIPublisherLifecycleListener implements LifecycleListener { - private static final String API_CONFIG_DEFAULT_VERSION = "1.0.0"; - - private static final String PARAM_MANAGED_API_ENABLED = "managed-api-enabled"; - private static final String PARAM_MANAGED_API_NAME = "managed-api-name"; - private static final String PARAM_MANAGED_API_VERSION = "managed-api-version"; - private static final String PARAM_MANAGED_API_CONTEXT = "managed-api-context"; - private static final String PARAM_MANAGED_API_ENDPOINT = "managed-api-endpoint"; - private static final String PARAM_MANAGED_API_OWNER = "managed-api-owner"; - private static final String PARAM_MANAGED_API_TRANSPORTS = "managed-api-transports"; - private static final String PARAM_MANAGED_API_IS_SECURED = "managed-api-isSecured"; - private static final String PARAM_MANAGED_API_APPLICATION = "managed-api-application"; - private static final String PARAM_SHARED_WITH_ALL_TENANTS = "isSharedWithAllTenants"; - private static final String PARAM_PROVIDER_TENANT_DOMAIN = "providerTenantDomain"; - private static final Log log = LogFactory.getLog(APIPublisherLifecycleListener.class); - - @Override - public void lifecycleEvent(LifecycleEvent lifecycleEvent) { - if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType())) { - StandardContext context = (StandardContext) lifecycleEvent.getLifecycle(); - ServletContext servletContext = context.getServletContext(); - String param = servletContext.getInitParameter(PARAM_MANAGED_API_ENABLED); - boolean isManagedApi = (param != null && !param.isEmpty()) && Boolean.parseBoolean(param); - - if (isManagedApi) { - try { - AnnotationUtil annotationUtil = new AnnotationUtil(context); - Set annotatedAPIClasses = annotationUtil. - scanStandardContext(org.wso2.carbon.apimgt.annotations.api.API.class.getName()); - List apiDefinitions = annotationUtil.extractAPIInfo(servletContext, annotatedAPIClasses); - - for (APIResourceConfiguration apiDefinition : apiDefinitions) { - APIConfig apiConfig = this.buildApiConfig(servletContext, apiDefinition); - try { - int tenantId = APIPublisherDataHolder.getInstance().getTenantManager().getTenantId(apiConfig.getTenantDomain()); - boolean isTenantActive = APIPublisherDataHolder.getInstance().getTenantManager().isTenantActive(tenantId); - if (isTenantActive) { - apiConfig.init(); - API api = APIPublisherUtil.getAPI(apiConfig); - APIPublisherService apiPublisherService = - APIPublisherDataHolder.getInstance().getApiPublisherService(); - if (apiPublisherService == null) { - throw new IllegalStateException( - "API Publisher service is not initialized properly"); - } - apiPublisherService.publishAPI(api); - } else { - log.error("No tenant [" + apiConfig.getTenantDomain() + "] found when publishing the webapp"); - } - } catch (Throwable e) { - log.error("Error occurred while publishing API '" + apiConfig.getName() + - "' with the context '" + apiConfig.getContext() + - "' and version '" + apiConfig.getVersion() + "'", e); - } - } - } catch (IOException e) { - log.error("Error enconterd while discovering annotated classes", e); - } catch (ClassNotFoundException e) { - log.error("Error while scanning class for annotations", e); - } - } - } - } - - private List mergeAPIDefinitions(List inputList) { - //TODO : Need to implemented, to merge API Definitions in cases where implementation of an API Lies in two - // classes - return null; - } - - /** - * Build the API Configuration to be passed to APIM, from a given list of URL templates - * - * @param servletContext - * @return - */ - private APIConfig buildApiConfig(ServletContext servletContext, APIResourceConfiguration apidef) { - APIConfig apiConfig = new APIConfig(); - - String name = apidef.getName(); - if (name == null || name.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("API Name not set in @API Annotation"); - } - name = servletContext.getServletContextName(); - } - apiConfig.setName(name); - - String version = apidef.getVersion(); - if (version == null || version.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("'API Version not set in @API Annotation'"); - } - version = API_CONFIG_DEFAULT_VERSION; - } - apiConfig.setVersion(version); - - - String context = apidef.getContext(); - if (context == null || context.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("'API Context not set in @API Annotation'"); - } - context = servletContext.getContextPath(); - } - apiConfig.setContext(context); - - String[] tags = apidef.getTags(); - if (tags == null || tags.length == 0) { - if (log.isDebugEnabled()) { - log.debug("'API tag not set in @API Annotation'"); - } - } else { - apiConfig.setTags(tags); - } - - String tenantDomain = servletContext.getInitParameter(PARAM_PROVIDER_TENANT_DOMAIN); - tenantDomain = (tenantDomain != null && !tenantDomain.isEmpty()) ? tenantDomain : - MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; - apiConfig.setTenantDomain(tenantDomain); - String contextTemplate = context + "/" + APIConstants.VERSION_PLACEHOLDER; - if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { - contextTemplate = context + "/t/" + tenantDomain + "/" + APIConstants.VERSION_PLACEHOLDER; - } - apiConfig.setContextTemplate(contextTemplate); - - String endpoint = servletContext.getInitParameter(PARAM_MANAGED_API_ENDPOINT); - if (endpoint == null || endpoint.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("'managed-api-endpoint' attribute is not configured"); - } - String endpointContext = servletContext.getContextPath(); - endpoint = APIPublisherUtil.getApiEndpointUrl(endpointContext); - } - apiConfig.setEndpoint(endpoint); - - String owner = servletContext.getInitParameter(PARAM_MANAGED_API_OWNER); - if (owner == null || owner.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("'managed-api-owner' attribute is not configured"); - } - } - apiConfig.setOwner(owner); - - String isSecuredParam = servletContext.getInitParameter(PARAM_MANAGED_API_IS_SECURED); - boolean isSecured; - if (isSecuredParam == null || isSecuredParam.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("'managed-api-isSecured' attribute is not configured. Therefore, using the default, " + - "which is 'true'"); - } - isSecured = false; - } else { - isSecured = Boolean.parseBoolean(isSecuredParam); - } - apiConfig.setSecured(isSecured); - - String transports = servletContext.getInitParameter(PARAM_MANAGED_API_TRANSPORTS); - if (transports == null || transports.isEmpty()) { - if (log.isDebugEnabled()) { - log.debug("'managed-api-transports' attribute is not configured. Therefore using the default, " + - "which is 'https'"); - } - transports = "https"; - } - apiConfig.setTransports(transports); - - String sharingValueParam = servletContext.getInitParameter(PARAM_SHARED_WITH_ALL_TENANTS); - boolean isSharedWithAllTenants = (sharingValueParam == null || (!sharingValueParam.isEmpty()) && Boolean.parseBoolean( - sharingValueParam)); - apiConfig.setSharedWithAllTenants(isSharedWithAllTenants); - - Set uriTemplates = new LinkedHashSet(); - for (APIResource apiResource : apidef.getResources()) { - URITemplate template = new URITemplate(); - template.setAuthType(apiResource.getAuthType()); - template.setHTTPVerb(apiResource.getHttpVerb()); - template.setResourceURI(apiResource.getUri()); - template.setUriTemplate(apiResource.getUriTemplate()); - uriTemplates.add(template); - } - apiConfig.setUriTemplates(uriTemplates); - - return apiConfig; - } + private static final String API_CONFIG_DEFAULT_VERSION = "1.0.0"; + + private static final String PARAM_MANAGED_API_ENABLED = "managed-api-enabled"; + private static final String PARAM_MANAGED_API_NAME = "managed-api-name"; + private static final String PARAM_MANAGED_API_VERSION = "managed-api-version"; + private static final String PARAM_MANAGED_API_CONTEXT = "managed-api-context"; + private static final String PARAM_MANAGED_API_ENDPOINT = "managed-api-endpoint"; + private static final String PARAM_MANAGED_API_OWNER = "managed-api-owner"; + private static final String PARAM_MANAGED_API_TRANSPORTS = "managed-api-transports"; + private static final String PARAM_MANAGED_API_IS_SECURED = "managed-api-isSecured"; + private static final String PARAM_MANAGED_API_APPLICATION = "managed-api-application"; + private static final String PARAM_SHARED_WITH_ALL_TENANTS = "isSharedWithAllTenants"; + private static final String PARAM_PROVIDER_TENANT_DOMAIN = "providerTenantDomain"; + private static final Log log = LogFactory.getLog(APIPublisherLifecycleListener.class); + + @Override + public void lifecycleEvent(LifecycleEvent lifecycleEvent) { + if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType())) { + StandardContext context = (StandardContext) lifecycleEvent.getLifecycle(); + ServletContext servletContext = context.getServletContext(); + String param = servletContext.getInitParameter(PARAM_MANAGED_API_ENABLED); + boolean isManagedApi = (param != null && !param.isEmpty()) && Boolean.parseBoolean(param); + + if (isManagedApi) { + try { + AnnotationUtil annotationUtil = new AnnotationUtil(context); + Set annotatedAPIClasses = annotationUtil. + scanStandardContext(org.wso2.carbon.apimgt.annotations.api.API.class.getName()); + List apiDefinitions = annotationUtil.extractAPIInfo(servletContext, annotatedAPIClasses); + + for (APIResourceConfiguration apiDefinition : apiDefinitions) { + APIConfig apiConfig = this.buildApiConfig(servletContext, apiDefinition); + try { + int tenantId = APIPublisherDataHolder.getInstance().getTenantManager().getTenantId(apiConfig.getTenantDomain()); + boolean isTenantActive = APIPublisherDataHolder.getInstance().getTenantManager().isTenantActive(tenantId); + if (isTenantActive) { + apiConfig.init(); + API api = APIPublisherUtil.getAPI(apiConfig); + APIPublisherService apiPublisherService = + APIPublisherDataHolder.getInstance().getApiPublisherService(); + if (apiPublisherService == null) { + throw new IllegalStateException( + "API Publisher service is not initialized properly"); + } + apiPublisherService.publishAPI(api); + } else { + log.error("No tenant [" + apiConfig.getTenantDomain() + "] found when publishing the webapp"); + } + } catch (Throwable e) { + log.error("Error occurred while publishing API '" + apiConfig.getName() + + "' with the context '" + apiConfig.getContext() + + "' and version '" + apiConfig.getVersion() + "'", e); + } + } + } catch (IOException e) { + log.error("Error encountered while discovering annotated classes", e); + } catch (ClassNotFoundException e) { + log.error("Error while scanning class for annotations", e); + } + } + } + } + + private List mergeAPIDefinitions(List inputList) { + //TODO : Need to implemented, to merge API Definitions in cases where implementation of an API Lies in two + // classes + return null; + } + + /** + * Build the API Configuration to be passed to APIM, from a given list of URL templates + * + * @param servletContext + * @return + */ + private APIConfig buildApiConfig(ServletContext servletContext, APIResourceConfiguration apidef) { + APIConfig apiConfig = new APIConfig(); + + String name = apidef.getName(); + if (name == null || name.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("API Name not set in @API Annotation"); + } + name = servletContext.getServletContextName(); + } + apiConfig.setName(name); + + String version = apidef.getVersion(); + if (version == null || version.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'API Version not set in @API Annotation'"); + } + version = API_CONFIG_DEFAULT_VERSION; + } + apiConfig.setVersion(version); + + + String context = apidef.getContext(); + if (context == null || context.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'API Context not set in @API Annotation'"); + } + context = servletContext.getContextPath(); + } + apiConfig.setContext(context); + + String[] tags = apidef.getTags(); + if (tags == null || tags.length == 0) { + if (log.isDebugEnabled()) { + log.debug("'API tag not set in @API Annotation'"); + } + } else { + apiConfig.setTags(tags); + } + + String tenantDomain = servletContext.getInitParameter(PARAM_PROVIDER_TENANT_DOMAIN); + tenantDomain = (tenantDomain != null && !tenantDomain.isEmpty()) ? tenantDomain : + MultitenantConstants.SUPER_TENANT_DOMAIN_NAME; + apiConfig.setTenantDomain(tenantDomain); + String contextTemplate = context + "/" + APIConstants.VERSION_PLACEHOLDER; + if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + contextTemplate = context + "/t/" + tenantDomain + "/" + APIConstants.VERSION_PLACEHOLDER; + } + apiConfig.setContextTemplate(contextTemplate); + + String endpoint = servletContext.getInitParameter(PARAM_MANAGED_API_ENDPOINT); + if (endpoint == null || endpoint.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'managed-api-endpoint' attribute is not configured"); + } + String endpointContext = servletContext.getContextPath(); + endpoint = APIPublisherUtil.getApiEndpointUrl(endpointContext); + } + apiConfig.setEndpoint(endpoint); + + String owner = servletContext.getInitParameter(PARAM_MANAGED_API_OWNER); + if (owner == null || owner.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'managed-api-owner' attribute is not configured"); + } + } + apiConfig.setOwner(owner); + + String isSecuredParam = servletContext.getInitParameter(PARAM_MANAGED_API_IS_SECURED); + boolean isSecured; + if (isSecuredParam == null || isSecuredParam.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'managed-api-isSecured' attribute is not configured. Therefore, using the default, " + + "which is 'true'"); + } + isSecured = false; + } else { + isSecured = Boolean.parseBoolean(isSecuredParam); + } + apiConfig.setSecured(isSecured); + + String transports = servletContext.getInitParameter(PARAM_MANAGED_API_TRANSPORTS); + if (transports == null || transports.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("'managed-api-transports' attribute is not configured. Therefore using the default, " + + "which is 'https'"); + } + transports = "https"; + } + apiConfig.setTransports(transports); + + String sharingValueParam = servletContext.getInitParameter(PARAM_SHARED_WITH_ALL_TENANTS); + boolean isSharedWithAllTenants = (sharingValueParam == null || (!sharingValueParam.isEmpty()) && Boolean.parseBoolean( + sharingValueParam)); + apiConfig.setSharedWithAllTenants(isSharedWithAllTenants); + + Set uriTemplates = new LinkedHashSet(); + Scope scope; + for (APIResource apiResource : apidef.getResources()) { + URITemplate template = new URITemplate(); + template.setAuthType(apiResource.getAuthType()); + template.setHTTPVerb(apiResource.getHttpVerb()); + template.setResourceURI(apiResource.getUri()); + template.setUriTemplate(apiResource.getUriTemplate());; + template.setScope(apiResource.getScope()); + uriTemplates.add(template); + } + apiConfig.setUriTemplates(uriTemplates); + return apiConfig; + } } diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java index a26c2042f1..52db3720d3 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java @@ -20,12 +20,19 @@ package org.wso2.carbon.apimgt.webapp.publisher.lifecycle.util; import org.apache.catalina.core.StandardContext; import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.scannotation.AnnotationDB; import org.scannotation.WarUrlFinder; +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Permission; +import org.wso2.carbon.apimgt.api.model.Scope; import org.wso2.carbon.apimgt.webapp.publisher.config.APIResource; import org.wso2.carbon.apimgt.webapp.publisher.config.APIResourceConfiguration; +import org.wso2.carbon.apimgt.webapp.publisher.config.PermissionConfiguration; +import org.wso2.carbon.apimgt.webapp.publisher.config.PermissionManagementException; + import javax.servlet.ServletContext; import javax.ws.rs.*; import java.io.IOException; @@ -36,7 +43,9 @@ import java.lang.reflect.Proxy; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; public class AnnotationUtil { @@ -68,6 +77,7 @@ public class AnnotationUtil { /** * Scan the context for classes with annotations + * * @return * @throws IOException */ @@ -89,6 +99,7 @@ public class AnnotationUtil { /** * Method identifies the URL templates and context by reading the annotations of a class + * * @param entityClasses * @return */ @@ -107,8 +118,8 @@ public class AnnotationUtil { APIResourceConfiguration apiResourceConfig = null; try { clazz = classLoader.loadClass(className); - Class apiClazz = (Class) - classLoader.loadClass(org.wso2.carbon.apimgt.annotations.api.API.class.getName()); + Class apiClazz = (Class) + classLoader.loadClass(API.class.getName()); Annotation apiAnno = clazz.getAnnotation(apiClazz); List resourceList; @@ -123,18 +134,18 @@ public class AnnotationUtil { } try { - for(int k=0;k consumesClass = (Class) classLoader.loadClass(Consumes.class.getName()); Method[] consumesClassMethods = consumesClass.getMethods(); Annotation consumesAnno = method.getAnnotation(consumesClass); resource.setConsumes(invokeMethod(consumesClassMethods[0], consumesAnno, STRING_ARR)); } - if(annotations[i].annotationType().getName().equals(Produces.class.getName())){ + if (annotations[i].annotationType().getName().equals(Produces.class.getName())) { Class producesClass = (Class) classLoader.loadClass(Produces.class.getName()); Method[] producesClassMethods = producesClass.getMethods(); Annotation producesAnno = method.getAnnotation(producesClass); resource.setProduces(invokeMethod(producesClassMethods[0], producesAnno, STRING_ARR)); } + if (annotations[i].annotationType().getName().equals(Permission.class.getName())) { + PermissionConfiguration permissionConf = this.getPermission(method); + if (permissionConf != null) { + Scope scope = new Scope(); + scope.setKey(permissionConf.getScopeName()); + scope.setDescription(permissionConf.getScopeName()); + scope.setName(permissionConf.getScopeName()); + String roles = StringUtils.join(permissionConf.getPermissions(), ","); + scope.setRoles(roles); + resource.setScope(scope); + } + } } resourceList.add(resource); } @@ -231,12 +254,12 @@ public class AnnotationUtil { return resourceList; } - private String makeContextURLReady(String context){ - if(context != null && !context.equalsIgnoreCase("")){ - if(context.startsWith("/")){ + private String makeContextURLReady(String context) { + if (context != null && !context.equalsIgnoreCase("")) { + if (context.startsWith("/")) { return context; - }else{ - return "/"+context; + } else { + return "/" + context; } } return ""; @@ -244,6 +267,7 @@ public class AnnotationUtil { /** * When an annotation and method is passed, this method invokes that executes said method against the annotation + * * @param method * @param annotation * @param returnType @@ -252,11 +276,11 @@ public class AnnotationUtil { */ private String invokeMethod(Method method, Annotation annotation, String returnType) throws Throwable { InvocationHandler methodHandler = Proxy.getInvocationHandler(annotation); - switch (returnType){ + switch (returnType) { case STRING: return (String) methodHandler.invoke(annotation, method, null); case STRING_ARR: - return ((String[])methodHandler.invoke(annotation, method, null))[0]; + return ((String[]) methodHandler.invoke(annotation, method, null))[0]; default: return null; } @@ -267,6 +291,36 @@ public class AnnotationUtil { */ private String[] invokeMethod(Method method, Annotation annotation) throws Throwable { InvocationHandler methodHandler = Proxy.getInvocationHandler(annotation); - return ((String[])methodHandler.invoke(annotation, method, null)); + return ((String[]) methodHandler.invoke(annotation, method, null)); } + + private PermissionConfiguration getPermission(Method currentMethod) throws Throwable { + Class permissionClass = (Class) classLoader.loadClass(Permission.class.getName()); + Annotation permissionAnnotation = currentMethod.getAnnotation(permissionClass); + if (permissionClass != null) { + Method[] permissionClassMethods = permissionClass.getMethods(); + PermissionConfiguration permissionConf = new PermissionConfiguration(); + for (Method method : permissionClassMethods) { + switch (method.getName()) { + case "scope": + permissionConf.setScopeName(invokeMethod(method, permissionAnnotation, STRING)); + break; + case "permissions": + String permissions[] = invokeMethod(method, permissionAnnotation); + this.addPermission(permissions); + permissionConf.setPermissions(permissions); + break; + } + } + return permissionConf; + } + return null; + } + + private void addPermission(String[] permissions) throws PermissionManagementException { + for (String permission : permissions) { + PermissionUtils.addPermission(permission); + } + } + } diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/PermissionUtils.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/PermissionUtils.java new file mode 100644 index 0000000000..32c5bad855 --- /dev/null +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/PermissionUtils.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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.wso2.carbon.apimgt.webapp.publisher.lifecycle.util; + +import org.wso2.carbon.apimgt.webapp.publisher.config.PermissionManagementException; +import org.wso2.carbon.apimgt.webapp.publisher.internal.APIPublisherDataHolder; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.registry.api.RegistryException; +import org.wso2.carbon.registry.api.Resource; +import org.wso2.carbon.registry.core.Registry; + +import java.util.StringTokenizer; + +/** + * Utility class which holds necessary utility methods required for persisting permissions in + * registry. + */ +public class PermissionUtils { + + public static final String ADMIN_PERMISSION_REGISTRY_PATH = "/permission/admin"; + public static final String PERMISSION_PROPERTY_NAME = "name"; + + public static Registry getGovernanceRegistry() throws PermissionManagementException { + try { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + return APIPublisherDataHolder.getInstance().getRegistryService() + .getGovernanceSystemRegistry( + tenantId); + } catch (RegistryException e) { + throw new PermissionManagementException( + "Error in retrieving governance registry instance: " + + e.getMessage(), e); + } + } + + public static void addPermission(String permission) throws PermissionManagementException { + String resourcePermission = getAbsolutePermissionPath(permission); + try { + StringTokenizer tokenizer = new StringTokenizer(resourcePermission, "/"); + String lastToken = "", currentToken, tempPath; + while (tokenizer.hasMoreTokens()) { + currentToken = tokenizer.nextToken(); + tempPath = lastToken + "/" + currentToken; + if (!checkResourceExists(tempPath)) { + createRegistryCollection(tempPath, currentToken); + } + lastToken = tempPath; + } + } catch (RegistryException e) { + throw new PermissionManagementException("Error occurred while persisting permission : " + + resourcePermission, e); + } + } + + public static void createRegistryCollection(String path, String resourceName) + throws PermissionManagementException, + RegistryException { + Resource resource = PermissionUtils.getGovernanceRegistry().newCollection(); + resource.addProperty(PERMISSION_PROPERTY_NAME, resourceName); + PermissionUtils.getGovernanceRegistry().beginTransaction(); + PermissionUtils.getGovernanceRegistry().put(path, resource); + PermissionUtils.getGovernanceRegistry().commitTransaction(); + } + + public static boolean checkResourceExists(String path) + throws PermissionManagementException, + org.wso2.carbon.registry.core.exceptions.RegistryException { + return PermissionUtils.getGovernanceRegistry().resourceExists(path); + } + + private static String getAbsolutePermissionPath(String permissionPath) { + return PermissionUtils.ADMIN_PERMISSION_REGISTRY_PATH + permissionPath; + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/permission/lifecycle/WebAppDeploymentLifecycleListener.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/permission/lifecycle/WebAppDeploymentLifecycleListener.java index eacc7a84c8..f274eee845 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/permission/lifecycle/WebAppDeploymentLifecycleListener.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/permission/lifecycle/WebAppDeploymentLifecycleListener.java @@ -45,23 +45,23 @@ import java.util.List; @SuppressWarnings("unused") public class WebAppDeploymentLifecycleListener implements LifecycleListener { - private static final String PERMISSION_CONFIG_PATH = "META-INF" + File.separator + "permissions.xml"; - private static final Log log = LogFactory.getLog(WebAppDeploymentLifecycleListener.class); + private static final String PERMISSION_CONFIG_PATH = "META-INF" + File.separator + "permissions.xml"; + private static final Log log = LogFactory.getLog(WebAppDeploymentLifecycleListener.class); - @Override - public void lifecycleEvent(LifecycleEvent lifecycleEvent) { - if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType())) { - StandardContext context = (StandardContext) lifecycleEvent.getLifecycle(); - ServletContext servletContext = context.getServletContext(); - String contextPath = context.getServletContext().getContextPath(); - try { - InputStream permissionStream = servletContext.getResourceAsStream(PERMISSION_CONFIG_PATH); - if (permissionStream != null) { + @Override + public void lifecycleEvent(LifecycleEvent lifecycleEvent) { + if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType())) { + StandardContext context = (StandardContext) lifecycleEvent.getLifecycle(); + ServletContext servletContext = context.getServletContext(); + String contextPath = context.getServletContext().getContextPath(); + try { + InputStream permissionStream = servletContext.getResourceAsStream(PERMISSION_CONFIG_PATH); + if (permissionStream != null) { /* Un-marshaling Device Management configuration */ - JAXBContext cdmContext = JAXBContext.newInstance(PermissionConfiguration.class); - Unmarshaller unmarshaller = cdmContext.createUnmarshaller(); - PermissionConfiguration permissionConfiguration = (PermissionConfiguration) - unmarshaller.unmarshal(permissionStream); + JAXBContext cdmContext = JAXBContext.newInstance(PermissionConfiguration.class); + Unmarshaller unmarshaller = cdmContext.createUnmarshaller(); + PermissionConfiguration permissionConfiguration = (PermissionConfiguration) + unmarshaller.unmarshal(permissionStream); List permissions = permissionConfiguration.getPermissions(); String apiVersion = permissionConfiguration.getApiVersion(); if (permissionConfiguration != null && permissions != null) { @@ -69,22 +69,22 @@ public class WebAppDeploymentLifecycleListener implements LifecycleListener { // update the permission path to absolute permission path permission.setPath(PermissionUtils.getAbsolutePermissionPath(permission.getPath())); permission.setUrl(PermissionUtils.getAbsoluteContextPathOfAPI(contextPath, apiVersion, - permission.getUrl()).toLowerCase()); + permission.getUrl()).toLowerCase()); permission.setMethod(permission.getMethod().toUpperCase()); PermissionManagerServiceImpl.getInstance().addPermission(permission); } - } - } - } catch (JAXBException e) { + } + } + } catch (JAXBException e) { log.error( "Exception occurred while parsing the permission configuration of webapp : " - + context.getServletContext().getContextPath(), e); + + context.getServletContext().getContextPath(), e); } catch (PermissionManagementException e) { log.error("Exception occurred while adding the permissions from webapp : " - + servletContext.getContextPath(), e); + + servletContext.getContextPath(), e); } } - } + } } diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml index 5c343926c1..e36f684f36 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml @@ -17,7 +17,8 @@ ~ under the License. --> - + org.wso2.carbon.devicemgt @@ -46,10 +47,6 @@ org.wso2.carbon org.wso2.carbon.user.core - - org.wso2.carbon - org.wso2.carbon.user.api - @@ -89,7 +86,7 @@ org.wso2.carbon.user.api, org.wso2.carbon.user.core.service, org.wso2.carbon.identity.application.common.model, - org.wso2.carbon.identity.application.authentication.framework.model, + org.wso2.carbon.identity.application.authentication.framework.model, org.wso2.carbon.user.core.tenant From 6db9b2162358eadfa5db2d00925d8da1072e2ca6 Mon Sep 17 00:00:00 2001 From: mharindu Date: Thu, 28 Apr 2016 14:49:34 +0530 Subject: [PATCH 2/4] Code cleanup in webapp publisher --- .../apimgt/annotations/api/Permission.java | 9 +++++ .../publisher/APIPublisherServiceImpl.java | 8 +++-- .../webapp/publisher/APIPublisherUtil.java | 35 ++++++++++--------- .../lifecycle/util/AnnotationUtil.java | 1 - 4 files changed, 33 insertions(+), 20 deletions(-) diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java index 5cb590d3a5..65ade5cc3a 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/src/main/java/org/wso2/carbon/apimgt/annotations/api/Permission.java @@ -30,7 +30,16 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) public @interface Permission { + /** + * Represents the scope name. + * @return Returns scope name. + */ String scope(); + + /** + * Represents the associated permissions. + * @return Returns list of permissions. + */ String[] permissions(); } diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java index 1d29863f86..fcb27541db 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherServiceImpl.java @@ -107,8 +107,6 @@ public class APIPublisherServiceImpl implements APIPublisherService { List scopes = new ArrayList<>(); for (URITemplate uriTemplate : api.getUriTemplates()) { - JsonObject authType = new JsonObject(); - authType.addProperty("x-auth-type", "Application%20%26%20Application%20User"); JsonObject response = new JsonObject(); response.addProperty("200", ""); @@ -148,7 +146,7 @@ public class APIPublisherServiceImpl implements APIPublisherService { swaggerDefinition.addProperty("swagger", "2.0"); swaggerDefinition.add("info", info); - // adding scopes to definition + // adding scopes to swagger definition if (!api.getScopes().isEmpty()) { Gson gson = new Gson(); JsonElement element = gson.toJsonTree(api.getScopes(), new TypeToken>() { @@ -162,6 +160,9 @@ public class APIPublisherServiceImpl implements APIPublisherService { swaggerDefinition.add("x-wso2-security", wso2Security); } } + if (log.isDebugEnabled()) { + log.debug("API swagger definition: " + swaggerDefinition.toString()); + } return swaggerDefinition.toString(); } @@ -193,4 +194,5 @@ public class APIPublisherServiceImpl implements APIPublisherService { log.debug("End of publishing the batch of APIs"); } } + } diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java index d684e12562..2e7c8caac7 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/APIPublisherUtil.java @@ -42,14 +42,10 @@ public class APIPublisherUtil { public static final String API_VERSION_PARAM = "{version}"; public static final String API_PUBLISH_ENVIRONMENT = "Production and Sandbox"; private static final String API_CONFIG_DEFAULT_VERSION = "1.0.0"; - private static final String PARAM_MANAGED_API_NAME = "managed-api-name"; - private static final String PARAM_MANAGED_API_VERSION = "managed-api-version"; - private static final String PARAM_MANAGED_API_CONTEXT = "managed-api-context"; private static final String PARAM_MANAGED_API_ENDPOINT = "managed-api-endpoint"; private static final String PARAM_MANAGED_API_OWNER = "managed-api-owner"; private static final String PARAM_MANAGED_API_TRANSPORTS = "managed-api-transports"; private static final String PARAM_MANAGED_API_IS_SECURED = "managed-api-isSecured"; - private static final String PARAM_MANAGED_API_APPLICATION = "managed-api-application"; private static final String PARAM_SHARED_WITH_ALL_TENANTS = "isSharedWithAllTenants"; private static final String PARAM_PROVIDER_TENANT_DOMAIN = "providerTenantDomain"; @@ -125,16 +121,22 @@ public class APIPublisherUtil { if (scope != null) { if (apiScopes.get(scope.getKey()) == null) { apiScopes.put(scope.getKey(), scope); - } else { - // this has to be done because of the use of pass by reference - // where same object reference of scope should be available for both - // api scope and uri template scope - template.setScope(apiScopes.get(scope.getKey())); } } } Set scopes = new HashSet<>(apiScopes.values()); api.setScopes(scopes); + + // this has to be done because of the use of pass by reference + // where same object reference of scope should be available for both + // api scope and uri template scope + for (Scope scope : scopes) { + for (URITemplate template : uriTemplates) { + if (scope.getKey().equals(template.getScope().getKey())) { + template.setScope(scope); + } + } + } api.setUriTemplates(uriTemplates); } return api; @@ -202,12 +204,13 @@ public class APIPublisherUtil { * Build the API Configuration to be passed to APIM, from a given list of URL templates * * @param servletContext + * @param apiDef * @return */ - public static APIConfig buildApiConfig(ServletContext servletContext, APIResourceConfiguration apidef) { + public static APIConfig buildApiConfig(ServletContext servletContext, APIResourceConfiguration apiDef) { APIConfig apiConfig = new APIConfig(); - String name = apidef.getName(); + String name = apiDef.getName(); if (name == null || name.isEmpty()) { if (log.isDebugEnabled()) { log.debug("API Name not set in @API Annotation"); @@ -216,7 +219,7 @@ public class APIPublisherUtil { } apiConfig.setName(name); - String version = apidef.getVersion(); + String version = apiDef.getVersion(); if (version == null || version.isEmpty()) { if (log.isDebugEnabled()) { log.debug("'API Version not set in @API Annotation'"); @@ -226,7 +229,7 @@ public class APIPublisherUtil { apiConfig.setVersion(version); - String context = apidef.getContext(); + String context = apiDef.getContext(); if (context == null || context.isEmpty()) { if (log.isDebugEnabled()) { log.debug("'API Context not set in @API Annotation'"); @@ -235,7 +238,7 @@ public class APIPublisherUtil { } apiConfig.setContext(context); - String[] tags = apidef.getTags(); + String[] tags = apiDef.getTags(); if (tags == null || tags.length == 0) { if (log.isDebugEnabled()) { log.debug("'API tag not set in @API Annotation'"); @@ -300,8 +303,8 @@ public class APIPublisherUtil { && Boolean.parseBoolean(sharingValueParam)); apiConfig.setSharedWithAllTenants(isSharedWithAllTenants); - Set uriTemplates = new LinkedHashSet(); - for (APIResource apiResource : apidef.getResources()) { + Set uriTemplates = new LinkedHashSet<>(); + for (APIResource apiResource : apiDef.getResources()) { URITemplate template = new URITemplate(); template.setAuthType(apiResource.getAuthType()); template.setHTTPVerb(apiResource.getHttpVerb()); diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java index 070924bef6..4974a54c0e 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/src/main/java/org/wso2/carbon/apimgt/webapp/publisher/lifecycle/util/AnnotationUtil.java @@ -125,7 +125,6 @@ public class AnnotationUtil { .class.getName()); Annotation apiAnno = clazz.getAnnotation(apiClazz); - List resourceList; if (apiAnno != null) { From 04fa0f4874fd551ce4fb5b5cd3bcff0ba665579d Mon Sep 17 00:00:00 2001 From: mharindu Date: Thu, 28 Apr 2016 14:50:52 +0530 Subject: [PATCH 3/4] Implemented new password grant handler --- .../pom.xml | 27 +- .../mgt/oauth/extensions/OAuthExtUtils.java | 183 +++++++++- .../grant/ExtendedPasswordGrantHandler.java | 340 ++++++++++++++++++ .../OAuthExtensionServiceComponent.java | 46 ++- .../internal/OAuthExtensionsDataHolder.java | 11 + 5 files changed, 603 insertions(+), 4 deletions(-) create mode 100644 components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml index e36f684f36..a40e30b63f 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml @@ -47,6 +47,14 @@ org.wso2.carbon org.wso2.carbon.user.core + + org.wso2.carbon.apimgt + org.wso2.carbon.apimgt.impl + + + com.googlecode.json-simple.wso2 + json-simple + @@ -87,7 +95,24 @@ org.wso2.carbon.user.core.service, org.wso2.carbon.identity.application.common.model, org.wso2.carbon.identity.application.authentication.framework.model, - org.wso2.carbon.user.core.tenant + org.wso2.carbon.user.core.tenant, + org.json.simple, + javax.cache, + javax.xml.namespace, + org.apache.axiom.om, + org.wso2.carbon.apimgt.api, + org.wso2.carbon.apimgt.impl, + org.wso2.carbon.apimgt.impl.dao, + org.wso2.carbon.apimgt.impl.utils, + org.wso2.carbon.identity.application.common.cache, + org.wso2.carbon.identity.core.util, + org.wso2.carbon.identity.oauth2.dto, + org.wso2.carbon.identity.oauth2.token, + org.wso2.carbon.identity.oauth2.token.handlers.grant, + org.wso2.carbon.user.core, + org.wso2.carbon.user.core.config, + org.wso2.carbon.user.core.util, + org.wso2.carbon.utils diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java index aa5a73b140..c6a626db0d 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java @@ -20,9 +20,23 @@ package org.wso2.carbon.device.mgt.oauth.extensions; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.dao.ApiMgtDAO; +import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.device.mgt.oauth.extensions.internal.OAuthExtensionsDataHolder; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; import org.wso2.carbon.user.api.TenantManager; +import org.wso2.carbon.user.api.UserRealm; import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.service.RealmService; + +import javax.cache.Caching; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; /** * This class holds util methods used by OAuth extension bundle. @@ -30,6 +44,8 @@ import org.wso2.carbon.user.api.UserStoreException; public class OAuthExtUtils { private static final Log log = LogFactory.getLog(OAuthExtUtils.class); + private static final String DEFAULT_SCOPE_NAME = "default"; + private static final String UI_EXECUTE = "ui.execute"; public static int getTenantId(String tenantDomain) { int tenantId = 0; @@ -39,10 +55,175 @@ public class OAuthExtUtils { tenantId = tenantManager.getTenantId(tenantDomain); } catch (UserStoreException e) { String errorMsg = "Error when getting the tenant id from the tenant domain : " + - tenantDomain; + tenantDomain; log.error(errorMsg, e); } } return tenantId; } + + public static boolean setScopes(OAuthTokenReqMessageContext tokReqMsgCtx) { + String[] requestedScopes = tokReqMsgCtx.getScope(); + String[] defaultScope = new String[]{DEFAULT_SCOPE_NAME}; + //If no scopes were requested. + if (requestedScopes == null || requestedScopes.length == 0) { + tokReqMsgCtx.setScope(defaultScope); + return true; + } + + String consumerKey = tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId(); + String username = tokReqMsgCtx.getAuthorizedUser().getUserName(); + List reqScopeList = Arrays.asList(requestedScopes); + Map restAPIScopesOfCurrentTenant; + + try { + + Map appScopes; + ApiMgtDAO apiMgtDAO = new ApiMgtDAO(); + //Get all the scopes and roles against the scopes defined for the APIs subscribed to the application. + appScopes = apiMgtDAO.getScopeRolesOfApplication(consumerKey); + //Add API Manager rest API scopes set. This list should be loaded at server start up and keep + //in memory and add it to each and every request coming. + String tenantDomain = tokReqMsgCtx.getAuthorizedUser().getTenantDomain(); + restAPIScopesOfCurrentTenant = (Map) Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER) + .getCache("REST_API_SCOPE_CACHE") + .get(tenantDomain); + if (restAPIScopesOfCurrentTenant != null) { + appScopes.putAll(restAPIScopesOfCurrentTenant); + } else { + restAPIScopesOfCurrentTenant = APIUtil.getRESTAPIScopesFromConfig(APIUtil.getTenantRESTAPIScopesConfig(tenantDomain)); + //call load tenant config for rest API. + //then put cache + appScopes.putAll(restAPIScopesOfCurrentTenant); + Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER) + .getCache("REST_API_SCOPE_CACHE") + .put(tenantDomain, restAPIScopesOfCurrentTenant); + } + //If no scopes can be found in the context of the application + if (appScopes.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("No scopes defined for the Application " + + tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId()); + } + + String[] allowedScopes = getAllowedScopes(reqScopeList); + tokReqMsgCtx.setScope(allowedScopes); + return true; + } + + int tenantId; + RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService(); + + try { + tenantId = realmService.getTenantManager().getTenantId(tenantDomain); + + // If tenant Id is not set in the tokenReqContext, deriving it from username. + if (tenantId == 0 || tenantId == -1) { + tenantId = IdentityTenantUtil.getTenantIdOfUser(username); + } + + } catch (UserStoreException e) { + //Log and return since we do not want to stop issuing the token in case of scope validation failures. + log.error("Error when getting the tenant's UserStoreManager or when getting roles of user ", e); + return false; + } + + UserRealm userRealm = OAuthExtensionsDataHolder.getInstance().getRealmService().getTenantUserRealm(tenantId); + List authorizedScopes = new ArrayList<>(); + boolean status; + //List userRoleList = new ArrayList<>(Arrays.asList(userRoles)); + + //Iterate the requested scopes list. + for (String scope : reqScopeList) { + status = false; + //Get the set of roles associated with the requested scope. + String appPermissions = appScopes.get(scope); + //If the scope has been defined in the context of the App and if roles have been defined for the scope + if (appPermissions != null && appPermissions.length() != 0) { + List permissions = new ArrayList<>(Arrays.asList(appPermissions.replaceAll(" ", "").split(","))); + //Check if user has at least one of the roles associated with the scope + if (!permissions.isEmpty()) { + for (String permission : permissions) { + if (userRealm != null && userRealm.getAuthorizationManager() != null) { + String userStore = tokReqMsgCtx.getAuthorizedUser().getUserStoreDomain(); + + if (userStore != null) { + status = userRealm.getAuthorizationManager() + .isUserAuthorized(userStore + "/" + username, permission, UI_EXECUTE); + } else { + status = userRealm.getAuthorizationManager() + .isUserAuthorized(username, permission, UI_EXECUTE); + } + if (status) { + break; + } + } + } + if (status) { + authorizedScopes.add(scope); + } + } + } + //The requested scope is defined for the context of the App but no roles have been associated with the scope + //OR + //The scope string starts with 'device_'. + else if (appScopes.containsKey(scope) || isWhiteListedScope(scope)) { + authorizedScopes.add(scope); + } + } + if (!authorizedScopes.isEmpty()) { + String[] authScopesArr = authorizedScopes.toArray(new String[authorizedScopes.size()]); + tokReqMsgCtx.setScope(authScopesArr); + } else { + tokReqMsgCtx.setScope(defaultScope); + } + } catch (APIManagementException e) { + log.error("Error while getting scopes of application " + e.getMessage()); + return false; + } catch (UserStoreException e) { + e.printStackTrace(); + } + return true; + } + + /** + * Determines if the scope is specified in the whitelist. + * + * @param scope - The scope key to check + * @return - 'true' if the scope is white listed. 'false' if not. + */ + public static boolean isWhiteListedScope(String scope) { + // load white listed scopes + List scopeSkipList = OAuthExtensionsDataHolder.getInstance().getWhitelistedScopes(); + for (String scopeTobeSkipped : scopeSkipList) { + if (scope.matches(scopeTobeSkipped)) { + return true; + } + } + return false; + } + + /** + * Get the set of default scopes. If a requested scope is matches with the patterns specified in the whitelist, + * then such scopes will be issued without further validation. If the scope list is empty, + * token will be issued for default scope. + * + * @param requestedScopes - The set of requested scopes + * @return - The subset of scopes that are allowed + */ + private static String[] getAllowedScopes(List requestedScopes) { + List authorizedScopes = new ArrayList(); + + //Iterate the requested scopes list. + for (String scope : requestedScopes) { + if (isWhiteListedScope(scope)) { + authorizedScopes.add(scope); + } + } + if (authorizedScopes.isEmpty()) { + authorizedScopes.add(DEFAULT_SCOPE_NAME); + } + return authorizedScopes.toArray(new String[authorizedScopes.size()]); + } + } diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java new file mode 100644 index 0000000000..2223a915bc --- /dev/null +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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.wso2.carbon.device.mgt.oauth.extensions.handlers.grant; + +import org.apache.axiom.om.OMElement; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.oauth.extensions.OAuthExtUtils; +import org.wso2.carbon.device.mgt.oauth.extensions.internal.OAuthExtensionsDataHolder; +import org.wso2.carbon.identity.application.common.cache.BaseCache; +import org.wso2.carbon.identity.core.util.IdentityConfigParser; +import org.wso2.carbon.identity.core.util.IdentityCoreConstants; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.oauth.common.OAuthConstants; +import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception; +import org.wso2.carbon.identity.oauth2.ResponseHeader; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenReqDTO; +import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; +import org.wso2.carbon.identity.oauth2.token.handlers.grant.PasswordGrantHandler; +import org.wso2.carbon.user.api.Claim; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.api.UserStoreManager; +import org.wso2.carbon.user.core.UserRealm; +import org.wso2.carbon.user.core.config.RealmConfiguration; +import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.user.core.util.UserCoreUtil; + +import javax.xml.namespace.QName; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@SuppressWarnings("unused") +public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { + + private static Log log = LogFactory.getLog(ExtendedPasswordGrantHandler.class); + + private static final String CONFIG_ELEM_OAUTH = "OAuth"; + + // Claims that are set as response headers of access token response + private static final String REQUIRED_CLAIM_URIS = "RequiredRespHeaderClaimUris"; + private BaseCache userClaimsCache; + + // Primary/Secondary Login configuration + private static final String CLAIM_URI = "ClaimUri"; + private static final String LOGIN_CONFIG = "LoginConfig"; + private static final String USERID_LOGIN = "UserIdLogin"; + private static final String EMAIL_LOGIN = "EmailLogin"; + private static final String PRIMARY_LOGIN = "primary"; + + private Map> loginConfiguration = new ConcurrentHashMap<>(); + + private List requiredHeaderClaimUris = new ArrayList<>(); + + public void init() throws IdentityOAuth2Exception { + + super.init(); + + IdentityConfigParser configParser; + configParser = IdentityConfigParser.getInstance(); + OMElement oauthElem = configParser.getConfigElement(CONFIG_ELEM_OAUTH); + + // Get the required claim uris that needs to be included in the response. + parseRequiredHeaderClaimUris(oauthElem.getFirstChildWithName(getQNameWithIdentityNS(REQUIRED_CLAIM_URIS))); + + // read login config + parseLoginConfig(oauthElem); + + userClaimsCache = new BaseCache<>("UserClaimsCache"); + if (log.isDebugEnabled()) { + log.debug("Successfully created UserClaimsCache under " + OAuthConstants.OAUTH_CACHE_MANAGER); + } + } + + @Override + public boolean validateGrant(OAuthTokenReqMessageContext tokReqMsgCtx) + throws IdentityOAuth2Exception { + + OAuth2AccessTokenReqDTO oAuth2AccessTokenReqDTO = tokReqMsgCtx.getOauth2AccessTokenReqDTO(); + String username = oAuth2AccessTokenReqDTO.getResourceOwnerUsername(); + String loginUserName = getLoginUserName(username); + tokReqMsgCtx.getOauth2AccessTokenReqDTO().setResourceOwnerUsername(loginUserName); + + boolean isValidated = super.validateGrant(tokReqMsgCtx); + + if(isValidated){ + + int tenantId; + tenantId = IdentityTenantUtil.getTenantIdOfUser(username); + + RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService(); + UserStoreManager userStoreManager; + try { + userStoreManager = realmService.getTenantUserRealm(tenantId).getUserStoreManager(); + } catch (UserStoreException e) { + log.error("Error when getting the tenant's UserStoreManager", e); + return false; + } + + List respHeaders = new ArrayList<>(); + + if (oAuth2AccessTokenReqDTO.getResourceOwnerUsername() != null) { + + try { + + if (requiredHeaderClaimUris != null && !requiredHeaderClaimUris.isEmpty()) { + // Get user's claim values from the default profile. + String userStoreDomain = tokReqMsgCtx.getAuthorizedUser().getUserStoreDomain(); + + String endUsernameWithDomain = UserCoreUtil.addDomainToName + (oAuth2AccessTokenReqDTO.getResourceOwnerUsername(), + userStoreDomain); + + Claim[] mapClaimValues = getUserClaimValues(endUsernameWithDomain,userStoreManager); + + if(mapClaimValues != null && mapClaimValues.length > 0){ + ResponseHeader header; + for (String claimUri : requiredHeaderClaimUris) { + for (Claim claim : mapClaimValues) { + if (claimUri.equals(claim.getClaimUri())) { + header = new ResponseHeader(); + header.setKey(claim.getDisplayTag()); + header.setValue(claim.getValue()); + respHeaders.add(header); + break; + } + } + } + } + else if(log.isDebugEnabled()){ + log.debug("No claim values for user : "+endUsernameWithDomain); + } + + } + } catch (Exception e) { + throw new IdentityOAuth2Exception(e.getMessage(), e); + } + } + + tokReqMsgCtx.addProperty("RESPONSE_HEADERS", respHeaders.toArray( + new ResponseHeader[respHeaders.size()])); + } + + return isValidated; + } + + @Override + public boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx){ + return OAuthExtUtils.setScopes(tokReqMsgCtx); + } + + private String getLoginUserName(String userID) { + String loginUserName = userID; + if (isSecondaryLogin(userID)) { + loginUserName = getPrimaryFromSecondary(userID); + } + return loginUserName; + } + + /** + * Identify whether the logged in user used his Primary Login name or + * Secondary login name + * + * @param userId - The username used to login. + * @return true if secondary login name is used, + * false if primary login name has been used + */ + private boolean isSecondaryLogin(String userId) { + + if (loginConfiguration.get(EMAIL_LOGIN) != null) { + Map emailConf = loginConfiguration.get(EMAIL_LOGIN); + if ("true".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) { + return !isUserLoggedInEmail(userId); + } + else if ("false".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) { + return isUserLoggedInEmail(userId); + } + } + else if (loginConfiguration.get(USERID_LOGIN) != null) { + Map userIdConf = loginConfiguration.get(USERID_LOGIN); + if ("true".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) { + return isUserLoggedInEmail(userId); + } + else if ("false".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) { + return !isUserLoggedInEmail(userId); + } + } + return false; + } + + /** + * Identify whether the logged in user used his ordinal username or email + * + * @param userId - username used to login. + * @return - true if userId contains '@'. false otherwise + */ + private boolean isUserLoggedInEmail(String userId) { + return userId.contains("@"); + } + + /** + * Get the primaryLogin name using secondary login name. Primary secondary + * Configuration is provided in the identitiy.xml. In the userstore, it is + * users responsibility TO MAINTAIN THE SECONDARY LOGIN NAME AS UNIQUE for + * each and every users. If it is not unique, we will pick the very first + * entry from the userlist. + * + * @param login - username used to login. + * @return - + */ + private String getPrimaryFromSecondary(String login) { + + String claimURI, username = null; + if (isUserLoggedInEmail(login)) { + Map emailConf = loginConfiguration.get(EMAIL_LOGIN); + claimURI = emailConf.get(CLAIM_URI); + } else { + Map userIdConf = loginConfiguration.get(USERID_LOGIN); + claimURI = userIdConf.get(CLAIM_URI); + } + + try { + RealmService realmSvc = OAuthExtensionsDataHolder.getInstance().getRealmService(); + RealmConfiguration config = new RealmConfiguration(); + UserRealm realm = realmSvc.getUserRealm(config); + org.wso2.carbon.user.core.UserStoreManager storeManager = realm.getUserStoreManager(); + String[] user = storeManager.getUserList(claimURI, login, null); + if (user.length > 0) { + username = user[0]; + } + } catch (UserStoreException e) { + log.error("Error while retrieving the primaryLogin name using secondary login name : " + login, e); + } + return username; + } + + private Claim[] getUserClaimValues(String authorizedUser, UserStoreManager userStoreManager) + throws + UserStoreException { + Claim[] userClaims = userClaimsCache.getValueFromCache(authorizedUser); + if(userClaims != null){ + return userClaims; + }else{ + if(log.isDebugEnabled()){ + log.debug("Cache miss for user claims. Username :" + authorizedUser); + } + userClaims = userStoreManager.getUserClaimValues( + authorizedUser, null); + userClaimsCache.addToCache(authorizedUser,userClaims); + return userClaims; + } + } + + // Read the required claim configuration from identity.xml + private void parseRequiredHeaderClaimUris(OMElement requiredClaimUrisElem) { + if (requiredClaimUrisElem == null) { + return; + } + + Iterator claimUris = requiredClaimUrisElem.getChildrenWithLocalName(CLAIM_URI); + if (claimUris != null) { + while (claimUris.hasNext()) { + OMElement claimUri = (OMElement) claimUris.next(); + if (claimUri != null) { + requiredHeaderClaimUris.add(claimUri.getText()); + } + } + } + } + + /** + * Read the primary/secondary login configuration + * + * .... + * + * + * + * + * + * http://wso2.org/claims/emailaddress + * + * + * ..... + * + * @param oauthConfigElem - The '' xml configuration element in the api-manager.xml + */ + private void parseLoginConfig(OMElement oauthConfigElem) { + OMElement loginConfigElem = oauthConfigElem.getFirstChildWithName(getQNameWithIdentityNS(LOGIN_CONFIG)); + if (loginConfigElem != null) { + if (log.isDebugEnabled()) { + log.debug("Login configuration is set "); + } + // Primary/Secondary supported login mechanisms + OMElement emailConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(EMAIL_LOGIN)); + + OMElement userIdConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(USERID_LOGIN)); + + Map emailConf = new HashMap(2); + emailConf.put(PRIMARY_LOGIN, + emailConfigElem.getAttributeValue(new QName(PRIMARY_LOGIN))); + emailConf.put(CLAIM_URI, + emailConfigElem.getFirstChildWithName(getQNameWithIdentityNS(CLAIM_URI)) + .getText()); + + Map userIdConf = new HashMap(2); + userIdConf.put(PRIMARY_LOGIN, + userIdConfigElem.getAttributeValue(new QName(PRIMARY_LOGIN))); + userIdConf.put(CLAIM_URI, + userIdConfigElem.getFirstChildWithName(getQNameWithIdentityNS(CLAIM_URI)) + .getText()); + + loginConfiguration.put(EMAIL_LOGIN, emailConf); + loginConfiguration.put(USERID_LOGIN, userIdConf); + } + } + + private QName getQNameWithIdentityNS(String localPart) { + return new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, localPart); + } +} diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java index b8be1c467f..3cdeeb5e8d 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionServiceComponent.java @@ -21,9 +21,17 @@ package org.wso2.carbon.device.mgt.oauth.extensions.internal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.apimgt.api.APIManagementException; +import org.wso2.carbon.apimgt.impl.APIConstants; +import org.wso2.carbon.apimgt.impl.APIManagerConfiguration; import org.wso2.carbon.device.mgt.common.permission.mgt.PermissionManagerService; import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; import org.wso2.carbon.user.core.service.RealmService; +import org.wso2.carbon.utils.CarbonUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; /** * @scr.component name="org.wso2.carbon.device.mgt.oauth.extensions" immediate="true" @@ -49,17 +57,51 @@ import org.wso2.carbon.user.core.service.RealmService; public class OAuthExtensionServiceComponent { private static final Log log = LogFactory.getLog(OAuthExtensionServiceComponent.class); + private static final String REPOSITORY = "repository"; + private static final String CONFIGURATION = "conf"; + private static final String APIM_CONF_FILE = "api-manager.xml"; + @SuppressWarnings("unused") protected void activate(ComponentContext componentContext) { - if(log.isDebugEnabled()){ + if (log.isDebugEnabled()) { log.debug("Starting OAuthExtensionBundle"); } + try { + APIManagerConfiguration configuration = new APIManagerConfiguration(); + String filePath = new StringBuilder(). + append(CarbonUtils.getCarbonHome()). + append(File.separator). + append(REPOSITORY). + append(File.separator). + append(CONFIGURATION). + append(File.separator). + append(APIM_CONF_FILE).toString(); + + configuration.load(filePath); + // loading white listed scopes + List whiteList; + + // Read scope whitelist from Configuration. + whiteList = configuration.getProperty(APIConstants.API_KEY_MANGER_SCOPE_WHITELIST); + + // If whitelist is null, default scopes will be put. + if (whiteList == null) { + whiteList = new ArrayList(); + whiteList.add(APIConstants.OPEN_ID_SCOPE_NAME); + whiteList.add(APIConstants.DEVICE_SCOPE_PATTERN); + } + + OAuthExtensionsDataHolder.getInstance().setWhitelistedScopes(whiteList); + + } catch (APIManagementException e) { + log.error("Error occurred while loading APIM configurations", e); + } } @SuppressWarnings("unused") protected void deactivate(ComponentContext componentContext) { - if(log.isDebugEnabled()){ + if (log.isDebugEnabled()) { log.debug("Stopping OAuthExtensionBundle"); } } diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java index f87ac765d3..f591688001 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/internal/OAuthExtensionsDataHolder.java @@ -22,6 +22,8 @@ import org.wso2.carbon.device.mgt.common.permission.mgt.PermissionManagerService import org.wso2.carbon.identity.oauth2.OAuth2TokenValidationService; import org.wso2.carbon.user.core.service.RealmService; +import java.util.List; + /** * This holds the OSGi service references required for oauth extensions bundle. */ @@ -30,6 +32,7 @@ public class OAuthExtensionsDataHolder { private RealmService realmService; private OAuth2TokenValidationService oAuth2TokenValidationService; private PermissionManagerService permissionManagerService; + private List whitelistedScopes; private static OAuthExtensionsDataHolder thisInstance = new OAuthExtensionsDataHolder(); @@ -72,4 +75,12 @@ public class OAuthExtensionsDataHolder { } return permissionManagerService; } + + public List getWhitelistedScopes() { + return whitelistedScopes; + } + + public void setWhitelistedScopes(List whitelistedScopes) { + this.whitelistedScopes = whitelistedScopes; + } } From c824e63f1ea07199aac563ef2bf45cbd8dcffad2 Mon Sep 17 00:00:00 2001 From: mharindu Date: Thu, 28 Apr 2016 16:43:13 +0530 Subject: [PATCH 4/4] Refactored and fixed findbug issues in OAuth extension --- .../mgt/oauth/extensions/OAuthExtUtils.java | 174 +++++++++++------- .../grant/ExtendedPasswordGrantHandler.java | 86 ++++----- 2 files changed, 141 insertions(+), 119 deletions(-) diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java index c6a626db0d..32ad46ccd1 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/OAuthExtUtils.java @@ -46,7 +46,14 @@ public class OAuthExtUtils { private static final Log log = LogFactory.getLog(OAuthExtUtils.class); private static final String DEFAULT_SCOPE_NAME = "default"; private static final String UI_EXECUTE = "ui.execute"; + private static final String REST_API_SCOPE_CACHE = "REST_API_SCOPE_CACHE"; + /** + * This method is used to get the tenant id when given tenant domain. + * + * @param tenantDomain Tenant domain name. + * @return Returns the tenant id. + */ public static int getTenantId(String tenantDomain) { int tenantId = 0; if (tenantDomain != null) { @@ -62,9 +69,16 @@ public class OAuthExtUtils { return tenantId; } + /** + * This method is used to set scopes that are authorized to the OAuth token request message context. + * + * @param tokReqMsgCtx OAuth token request message context + * @return Returns true if success. + */ public static boolean setScopes(OAuthTokenReqMessageContext tokReqMsgCtx) { String[] requestedScopes = tokReqMsgCtx.getScope(); String[] defaultScope = new String[]{DEFAULT_SCOPE_NAME}; + //If no scopes were requested. if (requestedScopes == null || requestedScopes.length == 0) { tokReqMsgCtx.setScope(defaultScope); @@ -72,7 +86,6 @@ public class OAuthExtUtils { } String consumerKey = tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId(); - String username = tokReqMsgCtx.getAuthorizedUser().getUserName(); List reqScopeList = Arrays.asList(requestedScopes); Map restAPIScopesOfCurrentTenant; @@ -80,23 +93,27 @@ public class OAuthExtUtils { Map appScopes; ApiMgtDAO apiMgtDAO = new ApiMgtDAO(); - //Get all the scopes and roles against the scopes defined for the APIs subscribed to the application. + + //Get all the scopes and permissions against the scopes defined for the APIs subscribed to the application. appScopes = apiMgtDAO.getScopeRolesOfApplication(consumerKey); + //Add API Manager rest API scopes set. This list should be loaded at server start up and keep //in memory and add it to each and every request coming. String tenantDomain = tokReqMsgCtx.getAuthorizedUser().getTenantDomain(); restAPIScopesOfCurrentTenant = (Map) Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER) - .getCache("REST_API_SCOPE_CACHE") + .getCache(REST_API_SCOPE_CACHE) .get(tenantDomain); if (restAPIScopesOfCurrentTenant != null) { appScopes.putAll(restAPIScopesOfCurrentTenant); } else { - restAPIScopesOfCurrentTenant = APIUtil.getRESTAPIScopesFromConfig(APIUtil.getTenantRESTAPIScopesConfig(tenantDomain)); + restAPIScopesOfCurrentTenant = APIUtil. + getRESTAPIScopesFromConfig(APIUtil.getTenantRESTAPIScopesConfig(tenantDomain)); + //call load tenant config for rest API. //then put cache appScopes.putAll(restAPIScopesOfCurrentTenant); Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER) - .getCache("REST_API_SCOPE_CACHE") + .getCache(REST_API_SCOPE_CACHE) .put(tenantDomain, restAPIScopesOfCurrentTenant); } //If no scopes can be found in the context of the application @@ -111,66 +128,9 @@ public class OAuthExtUtils { return true; } - int tenantId; - RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService(); - - try { - tenantId = realmService.getTenantManager().getTenantId(tenantDomain); - - // If tenant Id is not set in the tokenReqContext, deriving it from username. - if (tenantId == 0 || tenantId == -1) { - tenantId = IdentityTenantUtil.getTenantIdOfUser(username); - } - - } catch (UserStoreException e) { - //Log and return since we do not want to stop issuing the token in case of scope validation failures. - log.error("Error when getting the tenant's UserStoreManager or when getting roles of user ", e); - return false; - } - - UserRealm userRealm = OAuthExtensionsDataHolder.getInstance().getRealmService().getTenantUserRealm(tenantId); - List authorizedScopes = new ArrayList<>(); - boolean status; - //List userRoleList = new ArrayList<>(Arrays.asList(userRoles)); + // check for authorized scopes + List authorizedScopes = getAuthorizedScopes(tokReqMsgCtx, reqScopeList, appScopes); - //Iterate the requested scopes list. - for (String scope : reqScopeList) { - status = false; - //Get the set of roles associated with the requested scope. - String appPermissions = appScopes.get(scope); - //If the scope has been defined in the context of the App and if roles have been defined for the scope - if (appPermissions != null && appPermissions.length() != 0) { - List permissions = new ArrayList<>(Arrays.asList(appPermissions.replaceAll(" ", "").split(","))); - //Check if user has at least one of the roles associated with the scope - if (!permissions.isEmpty()) { - for (String permission : permissions) { - if (userRealm != null && userRealm.getAuthorizationManager() != null) { - String userStore = tokReqMsgCtx.getAuthorizedUser().getUserStoreDomain(); - - if (userStore != null) { - status = userRealm.getAuthorizationManager() - .isUserAuthorized(userStore + "/" + username, permission, UI_EXECUTE); - } else { - status = userRealm.getAuthorizationManager() - .isUserAuthorized(username, permission, UI_EXECUTE); - } - if (status) { - break; - } - } - } - if (status) { - authorizedScopes.add(scope); - } - } - } - //The requested scope is defined for the context of the App but no roles have been associated with the scope - //OR - //The scope string starts with 'device_'. - else if (appScopes.containsKey(scope) || isWhiteListedScope(scope)) { - authorizedScopes.add(scope); - } - } if (!authorizedScopes.isEmpty()) { String[] authScopesArr = authorizedScopes.toArray(new String[authorizedScopes.size()]); tokReqMsgCtx.setScope(authScopesArr); @@ -180,19 +140,17 @@ public class OAuthExtUtils { } catch (APIManagementException e) { log.error("Error while getting scopes of application " + e.getMessage()); return false; - } catch (UserStoreException e) { - e.printStackTrace(); } return true; } /** - * Determines if the scope is specified in the whitelist. + * Determines if the scope is specified in the white list. * * @param scope - The scope key to check * @return - 'true' if the scope is white listed. 'false' if not. */ - public static boolean isWhiteListedScope(String scope) { + private static boolean isWhiteListedScope(String scope) { // load white listed scopes List scopeSkipList = OAuthExtensionsDataHolder.getInstance().getWhitelistedScopes(); for (String scopeTobeSkipped : scopeSkipList) { @@ -204,7 +162,7 @@ public class OAuthExtUtils { } /** - * Get the set of default scopes. If a requested scope is matches with the patterns specified in the whitelist, + * Get the set of default scopes. If a requested scope is matches with the patterns specified in the white list, * then such scopes will be issued without further validation. If the scope list is empty, * token will be issued for default scope. * @@ -212,7 +170,7 @@ public class OAuthExtUtils { * @return - The subset of scopes that are allowed */ private static String[] getAllowedScopes(List requestedScopes) { - List authorizedScopes = new ArrayList(); + List authorizedScopes = new ArrayList<>(); //Iterate the requested scopes list. for (String scope : requestedScopes) { @@ -226,4 +184,80 @@ public class OAuthExtUtils { return authorizedScopes.toArray(new String[authorizedScopes.size()]); } + /** + * This method is used to get the authorized scopes out of requested scopes. It checks requested scopes with app + * scopes whether user has permissions to take actions for the requested scopes. + * + * @param tokReqMsgCtx OAuth token request message context. + * @param reqScopeList Requested scope list. + * @param appScopes App scopes. + * @return Returns a list of scopes. + */ + private static List getAuthorizedScopes(OAuthTokenReqMessageContext tokReqMsgCtx, List reqScopeList, + Map appScopes) { + + boolean status; + List authorizedScopes = new ArrayList<>(); + + int tenantId; + String username = tokReqMsgCtx.getAuthorizedUser().getUserName(); + String tenantDomain = tokReqMsgCtx.getAuthorizedUser().getTenantDomain(); + RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService(); + + try { + tenantId = realmService.getTenantManager().getTenantId(tenantDomain); + + // If tenant Id is not set in the tokenReqContext, deriving it from username. + if (tenantId == 0 || tenantId == -1) { + tenantId = IdentityTenantUtil.getTenantIdOfUser(username); + } + + UserRealm userRealm = OAuthExtensionsDataHolder.getInstance().getRealmService().getTenantUserRealm(tenantId); + + //Iterate the requested scopes list. + for (String scope : reqScopeList) { + status = false; + + //Get the set of roles associated with the requested scope. + String appPermissions = appScopes.get(scope); + + //If the scope has been defined in the context of the App and if permissions have been defined for the scope + if (appPermissions != null && appPermissions.length() != 0) { + List permissions = new ArrayList<>(Arrays.asList(appPermissions.replaceAll(" ", "").split(","))); + + //Check if user has at least one of the permission associated with the scope + if (!permissions.isEmpty()) { + for (String permission : permissions) { + if (userRealm != null && userRealm.getAuthorizationManager() != null) { + String userStore = tokReqMsgCtx.getAuthorizedUser().getUserStoreDomain(); + + if (userStore != null) { + status = userRealm.getAuthorizationManager() + .isUserAuthorized(userStore + "/" + username, permission, UI_EXECUTE); + } else { + status = userRealm.getAuthorizationManager() + .isUserAuthorized(username, permission, UI_EXECUTE); + } + if (status) { + break; + } + } + } + if (status) { + authorizedScopes.add(scope); + } + } + } + + //The scope string starts with 'device_'. + else if (appScopes.containsKey(scope) || isWhiteListedScope(scope)) { + authorizedScopes.add(scope); + } + } + } catch (UserStoreException e) { + log.error("Error occurred while initializing user store.", e); + } + return authorizedScopes; + } + } diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java index 2223a915bc..d39ea69f0c 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/src/main/java/org/wso2/carbon/device/mgt/oauth/extensions/handlers/grant/ExtendedPasswordGrantHandler.java @@ -42,12 +42,7 @@ import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.util.UserCoreUtil; import javax.xml.namespace.QName; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; @SuppressWarnings("unused") @@ -68,7 +63,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { private static final String EMAIL_LOGIN = "EmailLogin"; private static final String PRIMARY_LOGIN = "primary"; - private Map> loginConfiguration = new ConcurrentHashMap<>(); + private Map> loginConfiguration = new ConcurrentHashMap<>(); private List requiredHeaderClaimUris = new ArrayList<>(); @@ -103,7 +98,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { boolean isValidated = super.validateGrant(tokReqMsgCtx); - if(isValidated){ + if (isValidated) { int tenantId; tenantId = IdentityTenantUtil.getTenantIdOfUser(username); @@ -120,20 +115,17 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { List respHeaders = new ArrayList<>(); if (oAuth2AccessTokenReqDTO.getResourceOwnerUsername() != null) { - try { - if (requiredHeaderClaimUris != null && !requiredHeaderClaimUris.isEmpty()) { // Get user's claim values from the default profile. String userStoreDomain = tokReqMsgCtx.getAuthorizedUser().getUserStoreDomain(); - String endUsernameWithDomain = UserCoreUtil.addDomainToName - (oAuth2AccessTokenReqDTO.getResourceOwnerUsername(), - userStoreDomain); + String endUsernameWithDomain = UserCoreUtil. + addDomainToName(oAuth2AccessTokenReqDTO.getResourceOwnerUsername(), userStoreDomain); - Claim[] mapClaimValues = getUserClaimValues(endUsernameWithDomain,userStoreManager); + Claim[] mapClaimValues = getUserClaimValues(endUsernameWithDomain, userStoreManager); - if(mapClaimValues != null && mapClaimValues.length > 0){ + if (mapClaimValues != null && mapClaimValues.length > 0) { ResponseHeader header; for (String claimUri : requiredHeaderClaimUris) { for (Claim claim : mapClaimValues) { @@ -146,26 +138,22 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { } } } + } else if (log.isDebugEnabled()) { + log.debug("No claim values for user : " + endUsernameWithDomain); } - else if(log.isDebugEnabled()){ - log.debug("No claim values for user : "+endUsernameWithDomain); - } - } } catch (Exception e) { - throw new IdentityOAuth2Exception(e.getMessage(), e); + throw new IdentityOAuth2Exception("Error occurred while retrieving user claims", e); } } - - tokReqMsgCtx.addProperty("RESPONSE_HEADERS", respHeaders.toArray( - new ResponseHeader[respHeaders.size()])); + tokReqMsgCtx.addProperty("RESPONSE_HEADERS", respHeaders.toArray(new ResponseHeader[respHeaders.size()])); } return isValidated; } @Override - public boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx){ + public boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx) { return OAuthExtUtils.setScopes(tokReqMsgCtx); } @@ -183,7 +171,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { * * @param userId - The username used to login. * @return true if secondary login name is used, - * false if primary login name has been used + * false if primary login name has been used */ private boolean isSecondaryLogin(String userId) { @@ -191,17 +179,14 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { Map emailConf = loginConfiguration.get(EMAIL_LOGIN); if ("true".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) { return !isUserLoggedInEmail(userId); - } - else if ("false".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) { + } else if ("false".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) { return isUserLoggedInEmail(userId); } - } - else if (loginConfiguration.get(USERID_LOGIN) != null) { + } else if (loginConfiguration.get(USERID_LOGIN) != null) { Map userIdConf = loginConfiguration.get(USERID_LOGIN); if ("true".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) { return isUserLoggedInEmail(userId); - } - else if ("false".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) { + } else if ("false".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) { return !isUserLoggedInEmail(userId); } } @@ -258,20 +243,22 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { throws UserStoreException { Claim[] userClaims = userClaimsCache.getValueFromCache(authorizedUser); - if(userClaims != null){ + if (userClaims != null) { return userClaims; - }else{ - if(log.isDebugEnabled()){ + } else { + if (log.isDebugEnabled()) { log.debug("Cache miss for user claims. Username :" + authorizedUser); } userClaims = userStoreManager.getUserClaimValues( authorizedUser, null); - userClaimsCache.addToCache(authorizedUser,userClaims); + userClaimsCache.addToCache(authorizedUser, userClaims); return userClaims; } } - // Read the required claim configuration from identity.xml + /** + * Read the required claim configuration from identity.xml + */ private void parseRequiredHeaderClaimUris(OMElement requiredClaimUrisElem) { if (requiredClaimUrisElem == null) { return; @@ -291,21 +278,22 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { /** * Read the primary/secondary login configuration * - * .... - * - * - * - * - * - * http://wso2.org/claims/emailaddress - * - * - * ..... - * + * .... + * + * + * + * + * + * http://wso2.org/claims/emailaddress + * + * + * ..... + * + * * @param oauthConfigElem - The '' xml configuration element in the api-manager.xml */ private void parseLoginConfig(OMElement oauthConfigElem) { - OMElement loginConfigElem = oauthConfigElem.getFirstChildWithName(getQNameWithIdentityNS(LOGIN_CONFIG)); + OMElement loginConfigElem = oauthConfigElem.getFirstChildWithName(getQNameWithIdentityNS(LOGIN_CONFIG)); if (loginConfigElem != null) { if (log.isDebugEnabled()) { log.debug("Login configuration is set "); @@ -313,7 +301,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler { // Primary/Secondary supported login mechanisms OMElement emailConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(EMAIL_LOGIN)); - OMElement userIdConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(USERID_LOGIN)); + OMElement userIdConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(USERID_LOGIN)); Map emailConf = new HashMap(2); emailConf.put(PRIMARY_LOGIN,