Repository: airavata Updated Branches: refs/heads/develop 28709245b -> f52352760
http://git-wip-us.apache.org/repos/asf/airavata/blob/f5235276/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/interceptor/SecurityModule.java ---------------------------------------------------------------------- diff --git a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/interceptor/SecurityModule.java b/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/interceptor/SecurityModule.java deleted file mode 100644 index 2eba1b4..0000000 --- a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/interceptor/SecurityModule.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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.apache.airavata.api.server.security.interceptor; - -import com.google.inject.AbstractModule; -import com.google.inject.matcher.Matchers; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This does the plumbing work of integrating the interceptor with Guice framework for the methods to be - * intercepted upon their invocation. - */ -public class SecurityModule extends AbstractModule { - private final static Logger logger = LoggerFactory.getLogger(SecurityModule.class); - - public void configure(){ - logger.info("Security module reached..."); - SecurityInterceptor interceptor = new SecurityInterceptor(); - //requestInjection(interceptor); - - bindInterceptor(Matchers.any(), Matchers.annotatedWith(SecurityCheck.class), interceptor); - } - -} http://git-wip-us.apache.org/repos/asf/airavata/blob/f5235276/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/oauth/DefaultOAuthClient.java ---------------------------------------------------------------------- diff --git a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/oauth/DefaultOAuthClient.java b/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/oauth/DefaultOAuthClient.java deleted file mode 100644 index 528fa17..0000000 --- a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/oauth/DefaultOAuthClient.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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.apache.airavata.api.server.security.oauth; - -import org.apache.airavata.security.AiravataSecurityException; -import org.apache.axis2.AxisFault; -import org.apache.axis2.context.ConfigurationContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.wso2.carbon.identity.oauth2.stub.OAuth2TokenValidationServiceStub; -import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO; -import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationRequestDTO_OAuth2AccessToken; -import org.wso2.carbon.identity.oauth2.stub.dto.OAuth2TokenValidationResponseDTO; -import org.wso2.carbon.utils.CarbonUtils; - -import java.rmi.RemoteException; - -/** - * This is the default OAuth Client that talks to WSO2 IS's OAuth Authentication Server - * to get the OAuth token validated. - */ -public class DefaultOAuthClient { - - private OAuth2TokenValidationServiceStub stub; - private final static Logger logger = LoggerFactory.getLogger(DefaultOAuthClient.class); - public static final String BEARER_TOKEN_TYPE = "bearer"; - - /** - * OAuth2TokenValidationService Admin Service Client - * - * @param auhorizationServerURL - * @param username - * @param password - * @param configCtx - * @throws Exception - */ - public DefaultOAuthClient(String auhorizationServerURL, String username, String password, - ConfigurationContext configCtx) throws AiravataSecurityException { - try { - String serviceURL = auhorizationServerURL + "OAuth2TokenValidationService"; - stub = new OAuth2TokenValidationServiceStub(configCtx, serviceURL); - CarbonUtils.setBasicAccessSecurityHeaders(username, password, true, stub._getServiceClient()); - } catch (AxisFault e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error initializing OAuth client."); - } - } - - /** - * Validates the OAuth 2.0 access token - * - * @param accessToken - * @return - * @throws Exception - */ - public OAuth2TokenValidationResponseDTO validateAccessToken(String accessToken) - throws AiravataSecurityException { - - try { - OAuth2TokenValidationRequestDTO oauthReq = new OAuth2TokenValidationRequestDTO(); - OAuth2TokenValidationRequestDTO_OAuth2AccessToken token = - new OAuth2TokenValidationRequestDTO_OAuth2AccessToken(); - token.setIdentifier(accessToken); - token.setTokenType(BEARER_TOKEN_TYPE); - oauthReq.setAccessToken(token); - return stub.validate(oauthReq); - } catch (RemoteException e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error in validating the OAuth access token."); - } - } - -} http://git-wip-us.apache.org/repos/asf/airavata/blob/f5235276/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultPAPClient.java ---------------------------------------------------------------------- diff --git a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultPAPClient.java b/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultPAPClient.java deleted file mode 100644 index f82dff6..0000000 --- a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultPAPClient.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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.apache.airavata.api.server.security.xacml; - -import org.apache.airavata.common.exception.ApplicationSettingsException; -import org.apache.airavata.common.utils.ServerSettings; -import org.apache.airavata.security.AiravataSecurityException; -import org.apache.axis2.AxisFault; -import org.apache.axis2.context.ConfigurationContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.wso2.carbon.identity.entitlement.stub.EntitlementPolicyAdminServiceStub; -import org.wso2.carbon.identity.entitlement.stub.dto.PaginatedStatusHolder; -import org.wso2.carbon.identity.entitlement.stub.dto.PolicyDTO; -import org.wso2.carbon.identity.entitlement.stub.dto.StatusHolder; -import org.wso2.carbon.identity.entitlement.common.EntitlementConstants; -import org.wso2.carbon.identity.entitlement.stub.EntitlementPolicyAdminServiceEntitlementException; -import org.wso2.carbon.utils.CarbonUtils; - -import java.rmi.RemoteException; - -/** - * This publishes the airavata-default-xacml-policy.xml to the PDP via PAP API (of WSO2 Identity Server) - */ -public class DefaultPAPClient { - - private final static Logger logger = LoggerFactory.getLogger(DefaultPAPClient.class); - private EntitlementPolicyAdminServiceStub entitlementPolicyAdminServiceStub; - - public DefaultPAPClient(String auhorizationServerURL, String username, String password, - ConfigurationContext configCtx) throws AiravataSecurityException { - try { - - String PDPURL = auhorizationServerURL + "EntitlementPolicyAdminService"; - entitlementPolicyAdminServiceStub = new EntitlementPolicyAdminServiceStub(configCtx, PDPURL); - CarbonUtils.setBasicAccessSecurityHeaders(username, password, true, - entitlementPolicyAdminServiceStub._getServiceClient()); - } catch (AxisFault e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error initializing XACML PEP client."); - } - - } - - public boolean isPolicyAdded(String policyName) { - try { - PolicyDTO policyDTO = entitlementPolicyAdminServiceStub.getPolicy(policyName, false); - } catch (RemoteException e) { - logger.debug("Error in retrieving the policy.", e); - return false; - } catch (EntitlementPolicyAdminServiceEntitlementException e) { - logger.debug("Error in retrieving the policy.", e); - return false; - } - return true; - } - - public void addPolicy(String policy) throws AiravataSecurityException { - new Thread() { - public void run() { - try { - PolicyDTO policyDTO = new PolicyDTO(); - policyDTO.setPolicy(policy); - entitlementPolicyAdminServiceStub.addPolicy(policyDTO); - entitlementPolicyAdminServiceStub.publishToPDP(new String[]{ServerSettings.getAuthorizationPoliyName()}, - EntitlementConstants.PolicyPublish.ACTION_CREATE, null, false, 0); - - //Since policy publishing happens asynchronously, we need to retrieve the status and verify. - Thread.sleep(2000); - PaginatedStatusHolder paginatedStatusHolder = entitlementPolicyAdminServiceStub. - getStatusData(EntitlementConstants.Status.ABOUT_POLICY, ServerSettings.getAuthorizationPoliyName(), - EntitlementConstants.StatusTypes.PUBLISH_POLICY, "*", 1); - StatusHolder statusHolder = paginatedStatusHolder.getStatusHolders()[0]; - if (statusHolder.getSuccess() && EntitlementConstants.PolicyPublish.ACTION_CREATE.equals(statusHolder.getTargetAction())) { - logger.info("Authorization policy is published successfully."); - } else { - throw new AiravataSecurityException("Failed to publish the authorization policy."); - } - - //enable the published policy - entitlementPolicyAdminServiceStub.enableDisablePolicy(ServerSettings.getAuthorizationPoliyName(), true); - //Since policy enabling happens asynchronously, we need to retrieve the status and verify. - Thread.sleep(2000); - paginatedStatusHolder = entitlementPolicyAdminServiceStub. - getStatusData(EntitlementConstants.Status.ABOUT_POLICY, ServerSettings.getAuthorizationPoliyName(), - EntitlementConstants.StatusTypes.PUBLISH_POLICY, "*", 1); - statusHolder = paginatedStatusHolder.getStatusHolders()[0]; - if (statusHolder.getSuccess() && EntitlementConstants.PolicyPublish.ACTION_ENABLE.equals(statusHolder.getTargetAction())) { - logger.info("Authorization policy is enabled successfully."); - } else { - throw new AiravataSecurityException("Failed to enable the authorization policy."); - } - } catch (RemoteException e) { - logger.error(e.getMessage(), e); - } catch (InterruptedException e) { - logger.error(e.getMessage(), e); - } catch (ApplicationSettingsException e) { - logger.error(e.getMessage(), e); - } catch (AiravataSecurityException e) { - logger.error(e.getMessage(), e); - } catch (EntitlementPolicyAdminServiceEntitlementException e) { - logger.error(e.getMessage(), e); - } - } - }.start(); - } -} http://git-wip-us.apache.org/repos/asf/airavata/blob/f5235276/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultXACMLPEP.java ---------------------------------------------------------------------- diff --git a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultXACMLPEP.java b/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultXACMLPEP.java deleted file mode 100644 index e0ef142..0000000 --- a/airavata-api/airavata-api-server/src/main/java/org/apache/airavata/api/server/security/xacml/DefaultXACMLPEP.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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.apache.airavata.api.server.security.xacml; - -import org.apache.airavata.common.utils.Constants; -import org.apache.airavata.model.security.AuthzToken; -import org.apache.airavata.security.AiravataSecurityException; -import org.apache.axis2.AxisFault; -import org.apache.axis2.context.ConfigurationContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.wso2.carbon.identity.entitlement.stub.EntitlementServiceException; -import org.wso2.carbon.identity.entitlement.stub.EntitlementServiceStub; -import org.wso2.carbon.utils.CarbonUtils; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.rmi.RemoteException; -import java.util.Map; - -/** - * This enforces XACML based fine grained authorization on the API calls, by authorizing the API calls - * through default PDP which is WSO2 Identity Server. - */ -public class DefaultXACMLPEP { - - private final static Logger logger = LoggerFactory.getLogger(DefaultXACMLPEP.class); - private EntitlementServiceStub entitlementServiceStub; - - public DefaultXACMLPEP(String auhorizationServerURL, String username, String password, - ConfigurationContext configCtx) throws AiravataSecurityException { - try { - - String PDPURL = auhorizationServerURL + "EntitlementService"; - entitlementServiceStub = new EntitlementServiceStub(configCtx, PDPURL); - CarbonUtils.setBasicAccessSecurityHeaders(username, password, true, entitlementServiceStub._getServiceClient()); - } catch (AxisFault e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error initializing XACML PEP client."); - } - - } - - /** - * Send the XACML authorization request to XAML PDP and return the authorization decision. - * - * @param authzToken - * @param metaData - * @return - */ - public boolean getAuthorizationDecision(AuthzToken authzToken, Map<String, String> metaData) throws AiravataSecurityException { - String decision; - try { - String subject = authzToken.getClaimsMap().get(Constants.USER_NAME); - //FIXME hacky way to fix OpenID -> CILogon issue in WSO2 IS - if(subject.startsWith("http://")){ - subject = subject.substring(6); - } - String action = "/airavata/" + metaData.get(Constants.API_METHOD_NAME); - String decisionString = entitlementServiceStub.getDecisionByAttributes(subject, null, action, null); - //parse the XML decision string and obtain the decision - decision = parseDecisionString(decisionString); - if (Constants.PERMIT.equals(decision)) { - return true; - } else { - logger.error("Authorization decision is: " + decision); - return false; - } - } catch (RemoteException e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error in authorizing the user."); - } catch (EntitlementServiceException e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error in authorizing the user."); - } - } - - /** - * This parses the XML based authorization response by the PDP and returns the decision string. - * - * @param decisionString - * @return - * @throws AiravataSecurityException - */ - private String parseDecisionString(String decisionString) throws AiravataSecurityException { - try { - DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); - InputStream inputStream = new ByteArrayInputStream(decisionString.getBytes("UTF-8")); - Document doc = docBuilderFactory.newDocumentBuilder().parse(inputStream); - Node resultNode = doc.getDocumentElement().getFirstChild(); - Node decisionNode = resultNode.getFirstChild(); - String decision = decisionNode.getTextContent(); - return decision; - } catch (ParserConfigurationException e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error in parsing XACML authorization response."); - } catch (UnsupportedEncodingException e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error in parsing XACML authorization response."); - } catch (SAXException e) { - logger.error(e.getMessage(), e); - throw new AiravataSecurityException("Error in parsing XACML authorization response."); - } catch (IOException e) { - logger.error("Error in parsing XACML authorization response."); - throw new AiravataSecurityException("Error in parsing XACML authorization response."); - } - } -} http://git-wip-us.apache.org/repos/asf/airavata/blob/f5235276/airavata-services/services-security/src/main/java/org/apache/airavata/service/security/KeyCloakSecurityManager.java ---------------------------------------------------------------------- diff --git a/airavata-services/services-security/src/main/java/org/apache/airavata/service/security/KeyCloakSecurityManager.java b/airavata-services/services-security/src/main/java/org/apache/airavata/service/security/KeyCloakSecurityManager.java new file mode 100644 index 0000000..b319540 --- /dev/null +++ b/airavata-services/services-security/src/main/java/org/apache/airavata/service/security/KeyCloakSecurityManager.java @@ -0,0 +1,289 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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.apache.airavata.service.security; + +import org.apache.airavata.common.exception.ApplicationSettingsException; +import org.apache.airavata.common.utils.Constants; +import org.apache.airavata.common.utils.ServerSettings; +import org.apache.airavata.credential.store.client.CredentialStoreClientFactory; +import org.apache.airavata.credential.store.cpi.CredentialStoreService; +import org.apache.airavata.credential.store.exception.CredentialStoreException; +import org.apache.airavata.model.appcatalog.gatewayprofile.GatewayResourceProfile; +import org.apache.airavata.model.credential.store.PasswordCredential; +import org.apache.airavata.model.security.AuthzToken; +import org.apache.airavata.registry.api.client.RegistryServiceClientFactory; +import org.apache.airavata.registry.api.exception.RegistryServiceException; +import org.apache.airavata.security.AiravataSecurityException; +import org.apache.airavata.security.util.TrustStoreManager; +import org.apache.airavata.service.security.authzcache.*; +import org.apache.airavata.registry.api.RegistryService; +import org.apache.thrift.TException; +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class KeyCloakSecurityManager implements AiravataSecurityManager { + private final static Logger logger = LoggerFactory.getLogger(KeyCloakSecurityManager.class); + + private HashMap<String, String> rolePermissionConfig = new HashMap<>(); + + + public KeyCloakSecurityManager() throws AiravataSecurityException { + rolePermissionConfig.put("admin", "/airavata/.*"); + rolePermissionConfig.put("gateway-provider", "/airavata/.*"); + rolePermissionConfig.put("admin-read-only", "/airavata/getSSHPubKey|/airavata/getAllGatewaySSHPubKeys" + + "|/airavata/getAllGatewayPWDCredentials|/airavata/getApplicationModule|/airavata/getAllAppModules" + + "|/airavata/getApplicationDeployment|/airavata/getAllApplicationDeployments|/airavata/getAppModuleDeployedResources" + + "|/airavata/getStorageResource|/airavata/getAllStorageResourceNames|/airavata/getSCPDataMovement" + + "|/airavata/getUnicoreDataMovement|/airavata/getGridFTPDataMovement|/airavata/getResourceJobManager" + + "|/airavata/deleteResourceJobManager|/airavata/getGatewayResourceProfile|/airavata/getGatewayComputeResourcePreference" + + "|/airavata/getGatewayStoragePreference|/airavata/getAllGatewayComputeResourcePreferences" + + "|/airavata/getAllGatewayStoragePreferences|/airavata/getAllGatewayResourceProfiles|/airavata/getAPIVersion" + + "|/airavata/getNotification|/airavata/getAllNotifications|/airavata/createProject|/airavata/updateProject" + + "|/airavata/getProject|/airavata/deleteProject|/airavata/getUserProjects|/airavata/searchProjectsByProjectName" + + "|/airavata/searchProjectsByProjectDesc|/airavata/searchExperimentsByName|/airavata/searchExperimentsByDesc" + + "|/airavata/searchExperimentsByApplication|/airavata/searchExperimentsByStatus|/airavata/searchExperimentsByCreationTime" + + "|/airavata/searchExperiments|/airavata/getExperimentStatistics|/airavata/getExperimentsInProject" + + "|/airavata/getUserExperiments|/airavata/createExperiment|/airavata/deleteExperiment|/airavata/getExperiment" + + "|/airavata/getDetailedExperimentTree|/airavata/updateExperiment|/airavata/updateExperimentConfiguration" + + "|/airavata/updateResourceScheduleing|/airavata/validateExperiment|/airavata/launchExperiment" + + "|/airavata/getExperimentStatus|/airavata/getExperimentOutputs|/airavata/getIntermediateOutputs" + + "|/airavata/getJobStatuses|/airavata/getJobDetails|/airavata/cloneExperiment|/airavata/terminateExperiment" + + "|/airavata/getApplicationInterface|/airavata/getAllApplicationInterfaceNames|/airavata/getAllApplicationInterfaces" + + "|/airavata/getApplicationInputs|/airavata/getApplicationOutputs|/airavata/getAvailableAppInterfaceComputeResources" + + "|/airavata/getComputeResource|/airavata/getAllComputeResourceNames|/airavata/getWorkflow|/airavata/getWorkflowTemplateId" + + "|/airavata/isWorkflowExistWithName|/airavata/registerDataProduct|/airavata/getDataProduct|/airavata/registerReplicaLocation" + + "|/airavata/getParentDataProduct|/airavata/getChildDataProducts"); + rolePermissionConfig.put("gateway-user", "/airavata/getAPIVersion|/airavata/getNotification|/airavata/getAllNotifications|" + + "/airavata/createProject|/airavata/updateProject|/airavata/getProject|/airavata/deleteProject|/airavata/getUserProjects|" + + "/airavata/searchProjectsByProjectName|/airavata/searchProjectsByProjectDesc|/airavata/searchExperimentsByName|" + + "/airavata/searchExperimentsByDesc|/airavata/searchExperimentsByApplication|/airavata/searchExperimentsByStatus|" + + "/airavata/searchExperimentsByCreationTime|/airavata/searchExperiments|/airavata/getExperimentStatistics|" + + "/airavata/getExperimentsInProject|/airavata/getUserExperiments|/airavata/createExperiment|/airavata/deleteExperiment|" + + "/airavata/getExperiment|/airavata/getDetailedExperimentTree|/airavata/updateExperiment|/airavata/updateExperimentConfiguration|" + + "/airavata/updateResourceScheduleing|/airavata/validateExperiment|/airavata/launchExperiment|/airavata/getExperimentStatus|" + + "/airavata/getExperimentOutputs|/airavata/getIntermediateOutputs|/airavata/getJobStatuses|/airavata/getJobDetails|" + + "/airavata/cloneExperiment|/airavata/terminateExperiment|/airavata/getApplicationInterface|/airavata/getAllApplicationInterfaceNames|" + + "/airavata/getAllApplicationInterfaces|/airavata/getApplicationInputs|/airavata/getApplicationOutputs|" + + "/airavata/getAvailableAppInterfaceComputeResources|/airavata/getComputeResource|/airavata/getAllComputeResourceNames|" + + "/airavata/getWorkflow|/airavata/getWorkflowTemplateId|/airavata/isWorkflowExistWithName|/airavata/registerDataProduct|" + + "/airavata/getDataProduct|/airavata/registerReplicaLocation|/airavata/getParentDataProduct|/airavata/getChildDataProducts"); + + initializeSecurityInfra(); + } + + /** + * Implement this method in your SecurityManager to perform necessary initializations at the server startup. + * + * @throws AiravataSecurityException + */ + @Override + public void initializeSecurityInfra() throws AiravataSecurityException { + try { + //initialize SSL context with the trust store that contains the public cert of WSO2 Identity Server. + TrustStoreManager trustStoreManager = new TrustStoreManager(); + trustStoreManager.initializeTrustStoreManager(ServerSettings.getTrustStorePath(), + ServerSettings.getTrustStorePassword()); + } catch (Exception e) { + throw new AiravataSecurityException(e.getMessage(), e); + } + + } + + /** + * Implement this method with the user authentication/authorization logic in your SecurityManager. + * + * @param authzToken : this includes OAuth token and user's claims + * @param metaData : this includes other meta data needed for security enforcements. + * @return + * @throws AiravataSecurityException + */ + @Override + public boolean isUserAuthorized(AuthzToken authzToken, Map<String, String> metaData) throws AiravataSecurityException { + String subject = authzToken.getClaimsMap().get(Constants.USER_NAME); + String accessToken = authzToken.getAccessToken(); + String gatewayId = authzToken.getClaimsMap().get(Constants.GATEWAY_ID); + String action = "/airavata/" + metaData.get(Constants.API_METHOD_NAME); + try { + if (!ServerSettings.isAPISecured()) { + return true; + } + + if (ServerSettings.isAuthzCacheEnabled()) { + //obtain an instance of AuthzCacheManager implementation. + AuthzCacheManager authzCacheManager = AuthzCacheManagerFactory.getAuthzCacheManager(); + + //check in the cache + AuthzCachedStatus authzCachedStatus = authzCacheManager.getAuthzCachedStatus( + new AuthzCacheIndex(subject, gatewayId, accessToken, action)); + + if (AuthzCachedStatus.AUTHORIZED.equals(authzCachedStatus)) { + logger.debug("Authz decision for: (" + subject + ", " + accessToken + ", " + action + ") is retrieved from cache."); + return true; + } else if (AuthzCachedStatus.NOT_AUTHORIZED.equals(authzCachedStatus)) { + logger.debug("Authz decision for: (" + subject + ", " + accessToken + ", " + action + ") is retrieved from cache."); + return false; + } else if (AuthzCachedStatus.NOT_CACHED.equals(authzCachedStatus)) { + logger.debug("Authz decision for: (" + subject + ", " + accessToken + ", " + action + ") is not in the cache. " + + "Obtaining it from the authorization server."); + String[] roles = getUserRolesFromOAuthToken(subject, accessToken, gatewayId); + boolean authorizationDecision = hasPermission(roles, action); + //cache the authorization decision + long currentTime = System.currentTimeMillis(); + //TODO get the actual token expiration time + authzCacheManager.addToAuthzCache(new AuthzCacheIndex(subject, gatewayId, accessToken, action), + new AuthzCacheEntry(authorizationDecision, currentTime + 1000 * 60 * 60, currentTime)); + return authorizationDecision; + } else { + //undefined status returned from the authz cache manager + throw new AiravataSecurityException("Error in reading from the authorization cache."); + } + } else { + String[] roles = getUserRolesFromOAuthToken(subject, accessToken, gatewayId); + return hasPermission(roles, action); + } + + } catch (ApplicationSettingsException e) { + e.printStackTrace(); + throw new AiravataSecurityException(e.getMessage(), e); + } catch (Exception e) { + e.printStackTrace(); + throw new AiravataSecurityException(e.getMessage(), e); + } + } + + private String[] getUserRolesFromOAuthToken(String username, String token, String gatewayId) throws Exception { + GatewayResourceProfile gwrp = getRegistryServiceClient().getGatewayResourceProfile(gatewayId); + String identityServerRealm = gwrp.getIdentityServerTenant(); + String openIdConnectUrl = getOpenIDConfigurationUrl(identityServerRealm); + JSONObject openIdConnectConfig = new JSONObject(getFromUrl(openIdConnectUrl, token)); + String userInfoEndPoint = openIdConnectConfig.getString("userinfo_endpoint"); + JSONObject userInfo = new JSONObject(getFromUrl(userInfoEndPoint, token)); + if (!username.equals(userInfo.get("preferred_username"))) { + throw new AiravataSecurityException("Subject name and username for the token doesn't match"); + } + String userId = userInfo.getString("sub"); + + String userRoleMappingUrl = ServerSettings.getRemoteIDPServiceUrl() + "/admin/realms/" + + identityServerRealm + "/users/" + + userId + "/role-mappings/realm"; + JSONArray roleMappings = new JSONArray(getFromUrl(userRoleMappingUrl, getAdminAccessToken(gatewayId))); + String[] roles = new String[roleMappings.length()]; + for (int i = 0; i < roleMappings.length(); i++) { + roles[i] = (new JSONObject(roleMappings.get(i).toString())).get("name").toString(); + } + + return roles; + } + + private String getOpenIDConfigurationUrl(String realm) throws ApplicationSettingsException { + return ServerSettings.getRemoteIDPServiceUrl() + "/realms/" + realm + "/.well-known/openid-configuration"; + } + + public String getFromUrl(String urlToRead, String token) throws Exception { + StringBuilder result = new StringBuilder(); + URL url = new URL(urlToRead); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + if (token != null) { + String bearerAuth = "Bearer " + token; + conn.setRequestProperty("Authorization", bearerAuth); + } + BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = rd.readLine()) != null) { + result.append(line); + } + rd.close(); + return result.toString(); + } + + private String getAdminAccessToken(String gatewayId) throws Exception { + CredentialStoreService.Client csClient = getCredentialStoreServiceClient(); + GatewayResourceProfile gwrp = getRegistryServiceClient().getGatewayResourceProfile(gatewayId); + String identityServerRealm = gwrp.getIdentityServerTenant(); + String openIdConnectUrl = getOpenIDConfigurationUrl(identityServerRealm); + JSONObject openIdConnectConfig = new JSONObject(getFromUrl(openIdConnectUrl, null)); + PasswordCredential credential = csClient.getPasswordCredential(gwrp.getIdentityServerPwdCredToken(), gwrp.getGatewayID()); + String username = credential.getLoginUserName(); + String password = credential.getPassword(); + String urlString = openIdConnectConfig.getString("token_endpoint"); + StringBuilder result = new StringBuilder(); + URL url = new URL(urlString); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setDoOutput(true); + String postFields = "client_id=admin-cli&username=" + username + "&password=" + password + "&grant_type=password"; + conn.getOutputStream().write(postFields.getBytes()); + BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = rd.readLine()) != null) { + result.append(line); + } + rd.close(); + JSONObject tokenInfo = new JSONObject(result.toString()); + return tokenInfo.get("access_token").toString(); + } + + + private boolean hasPermission(String[] roles, String apiMethod) { + for (int i = 0; i < roles.length; i++) { + String role = roles[i]; + if (this.rolePermissionConfig.keySet().contains(role)) { + Pattern pattern = Pattern.compile(this.rolePermissionConfig.get(role)); + Matcher matcher = pattern.matcher(apiMethod); + if (matcher.matches()) + return true; + } + } + return false; + } + + private RegistryService.Client getRegistryServiceClient() throws TException, ApplicationSettingsException { + final int serverPort = Integer.parseInt(ServerSettings.getRegistryServerPort()); + final String serverHost = ServerSettings.getRegistryServerHost(); + try { + return RegistryServiceClientFactory.createRegistryClient(serverHost, serverPort); + } catch (RegistryServiceException e) { + throw new TException("Unable to create registry client...", e); + } + } + + private CredentialStoreService.Client getCredentialStoreServiceClient() throws TException, ApplicationSettingsException { + final int serverPort = Integer.parseInt(ServerSettings.getCredentialStoreServerPort()); + final String serverHost = ServerSettings.getCredentialStoreServerHost(); + try { + return CredentialStoreClientFactory.createAiravataCSClient(serverHost, serverPort); + } catch (CredentialStoreException e) { + throw new TException("Unable to create credential store client...", e); + } + } +} \ No newline at end of file
