/* * Copyright (c) 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.identity.entitlement.internal; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.transport.TSSLTransportFactory; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import org.osgi.service.component.ComponentContext; import org.wso2.carbon.base.ServerConfigurationException; import org.wso2.carbon.identity.core.util.IdentityCoreInitializedEvent; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.entitlement.EntitlementUtil; import org.wso2.carbon.identity.entitlement.PDPConstants; import org.wso2.carbon.identity.entitlement.dto.PolicyDTO; import org.wso2.carbon.identity.entitlement.listener.CacheClearingUserOperationListener; import org.wso2.carbon.identity.entitlement.pap.store.PAPPolicyStore; import org.wso2.carbon.identity.entitlement.thrift.EntitlementService; import org.wso2.carbon.identity.entitlement.thrift.ThriftConfigConstants; import org.wso2.carbon.identity.entitlement.thrift.ThriftEntitlementServiceImpl; import org.wso2.carbon.identity.notification.mgt.NotificationSender; import org.wso2.carbon.identity.thrift.authentication.ThriftAuthenticatorService; import org.wso2.carbon.registry.core.Registry; import org.wso2.carbon.registry.core.exceptions.RegistryException; import org.wso2.carbon.registry.core.service.RegistryService; import org.wso2.carbon.user.core.listener.UserOperationEventListener; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.NetworkUtils; import java.io.File; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @scr.component name="identity.entitlement.component" immediate="true" * @scr.reference name="registry.service" * interface="org.wso2.carbon.registry.core.service.RegistryService" * cardinality="1..1" policy="dynamic" bind="setRegistryService" * unbind="unsetRegistryService" * @scr.reference name="user.realmservice.default" * interface="org.wso2.carbon.user.core.service.RealmService" cardinality="1..1" * policy="dynamic" bind="setRealmService" unbind="unsetRealmService" * @scr.reference name="identityCoreInitializedEventService" * interface="org.wso2.carbon.identity.core.util.IdentityCoreInitializedEvent" cardinality="1..1" * policy="dynamic" bind="setIdentityCoreInitializedEventService" unbind="unsetIdentityCoreInitializedEventService" * @scr.reference name="org.wso2.carbon.identity.thrift.authentication.internal.ThriftAuthenticationServiceComponent" * interface="org.wso2.carbon.identity.thrift.authentication.ThriftAuthenticatorService" * cardinality="1..1" policy="dynamic" bind="setThriftAuthenticationService" unbind="unsetThriftAuthenticationService" * @scr.reference name="carbon.identity.notification.mgt" * interface="org.wso2.carbon.identity.notification.mgt.NotificationSender" * cardinality="1..1" policy="dynamic" bind="setNotificationSender" * unbind="unsetNotificationSender" */ public class EntitlementServiceComponent { private static final Log log = LogFactory.getLog(EntitlementServiceComponent.class); private static RegistryService registryService = null; private static EntitlementConfigHolder entitlementConfig = null; private static RealmService realmservice; private static NotificationSender notificationSender; private ThriftAuthenticatorService thriftAuthenticationService; private ExecutorService executor = Executors.newFixedThreadPool(2); /** * */ public EntitlementServiceComponent() { } /** * @return */ public static EntitlementConfigHolder getEntitlementConfig() { return entitlementConfig; } /** * @return */ public static RealmService getRealmservice() { return realmservice; } /** * @param realmservice */ public static void setRealmservice(RealmService realmservice) { EntitlementServiceComponent.realmservice = realmservice; } /** * Return registry service * * @return RegistryService */ public static RegistryService getRegistryService() { return registryService; } /** * sets registry service * * @param registryService <code>RegistryService</code> */ protected void setRegistryService(RegistryService registryService) { if (log.isDebugEnabled()) { log.debug("RegistryService set in Entitlement bundle"); } EntitlementServiceComponent.registryService = registryService; } public static Registry getGovernanceRegistry(int tenantId) { try { return registryService.getGovernanceSystemRegistry(tenantId); } catch (RegistryException e) { // ignore } return null; } /** * @param httpService */ /*protected void setHttpService(HttpService httpService) { httpServiceInstance = httpService; } *//** * @param httpService *//* protected void unsetHttpService(HttpService httpService) { httpServiceInstance = null; }*/ public static NotificationSender getNotificationSender() { return EntitlementServiceComponent.notificationSender; } protected void setNotificationSender(NotificationSender notificationSender) { if (log.isDebugEnabled()) { log.debug("Un-setting notification sender in Entitlement bundle"); } this.notificationSender = notificationSender; } /** * @param ctxt */ protected void activate(ComponentContext ctxt) { if (log.isDebugEnabled()) { log.debug("Identity Entitlement bundle is activated"); } try { // build configuration file entitlementConfig = new EntitlementConfigHolder(); EntitlementExtensionBuilder builder = new EntitlementExtensionBuilder(); builder.setBundleContext(ctxt.getBundleContext()); builder.buildEntitlementConfig(entitlementConfig); // Start loading schema. new Thread(new SchemaBuilder(entitlementConfig)).start(); // Read XACML policy files from a pre-defined location in the // filesystem and load to registry at the server startup PAPPolicyStore papPolicyStore = new PAPPolicyStore( registryService.getGovernanceSystemRegistry()); String startUpPolicyAdding = entitlementConfig.getEngineProperties().getProperty( PDPConstants.START_UP_POLICY_ADDING); if (startUpPolicyAdding != null && Boolean.parseBoolean(startUpPolicyAdding)) { if (papPolicyStore.getAllPolicyIds() == null || papPolicyStore.getAllPolicyIds().length == 0) { File policyFolder = null; String policyPathFromConfig = entitlementConfig.getEngineProperties().getProperty( PDPConstants.FILESYSTEM_POLICY_PATH); if (policyPathFromConfig != null && policyPathFromConfig.trim().length() > 0) { policyFolder = new File(policyPathFromConfig); } if (policyFolder != null && !policyFolder.exists()) { log.warn("Defined policy directory location is not exit. " + "Therefore using default policy location"); } if (policyPathFromConfig == null || (policyFolder != null && !policyFolder.exists())) { policyFolder = new File(CarbonUtils.getCarbonHome() + File.separator + "repository" + File.separator + "resources" + File.separator + "identity" + File.separator + "policies" + File.separator + "xacml"); } boolean customPolicies = false; if (policyFolder != null && policyFolder.exists()) { for (File policyFile : policyFolder.listFiles()) { if (policyFile.isFile()) { PolicyDTO policyDTO = new PolicyDTO(); policyDTO.setPolicy(FileUtils.readFileToString(policyFile)); try { EntitlementUtil.addFilesystemPolicy(policyDTO, registryService.getGovernanceSystemRegistry(), true); } catch (Exception e) { // log and ignore log.error("Error while adding XACML policies", e); } customPolicies = true; } } } if (!customPolicies) { // load default policies EntitlementUtil.addSamplePolicies(registryService.getGovernanceSystemRegistry()); } } } // Cache clearing listener is always registered since cache clearing is a must when // an update happens of user attributes CacheClearingUserOperationListener pipUserOperationListener = new CacheClearingUserOperationListener(); ctxt.getBundleContext().registerService( UserOperationEventListener.class.getName(), pipUserOperationListener, null); // Register Notification sending on user operations. Even though this is registered // only subscribed modules will send messages. if (log.isDebugEnabled()) { log.debug("Registering notification sender on user operations"); } //TODO: Read from identity.xml, the configurations to be used in thrift based entitlement service. //initialize thrift authenticator ThriftEntitlementServiceImpl.init(thriftAuthenticationService); //initialize thrift based Entitlement Service. startThriftServices(); } catch (Throwable throwable) { log.error("Failed to initialize Entitlement Service", throwable); } } /** * @param ctxt */ protected void deactivate(ComponentContext ctxt) { if (log.isDebugEnabled()) { log.debug("Identity Entitlement bundle is deactivated"); } } /** * un-sets registry service * * @param registryService <code>RegistryService</code> */ protected void unsetRegistryService(RegistryService registryService) { if (log.isDebugEnabled()) { log.debug("RegistryService unset in Entitlement bundle"); } EntitlementServiceComponent.registryService = null; } /** * sets realm service * * @param realmService <code>RealmService</code> */ protected void setRealmService(RealmService realmService) { if (log.isDebugEnabled()) { log.debug("DefaultUserRealm set in Entitlement bundle"); } EntitlementServiceComponent.realmservice = realmService; } /** * un-sets realm service * * @param realmService <code>RealmService</code> */ protected void unsetRealmService(RealmService realmService) { if (log.isDebugEnabled()) { log.debug("DefaultUserRealm unset in Entitlement bundle"); } EntitlementServiceComponent.realmservice = null; } /** * set Thrift authentication service * * @param authenticationService <code>ThriftAuthenticatorService</code> */ protected void setThriftAuthenticationService(ThriftAuthenticatorService authenticationService) { if (log.isDebugEnabled()) { log.debug("ThriftAuthenticatorService set in Entitlement bundle"); } this.thriftAuthenticationService = authenticationService; } /** * un-set Thrift authentication service * * @param authenticationService <code>ThriftAuthenticatorService</code> */ protected void unsetThriftAuthenticationService( ThriftAuthenticatorService authenticationService) { if (log.isDebugEnabled()) { log.debug("ThriftAuthenticatorService unset in Entitlement bundle"); } this.thriftAuthenticationService = null; } private void startThriftServices() throws Exception { startThriftEntitlementService(); } private void startThriftEntitlementService() throws Exception { try { //read identity.xml IdentityUtil.populateProperties(); //if thrift based EntitlementService is enabled. String thriftEnabled = IdentityUtil.getProperty(ThriftConfigConstants.PARAM_ENABLE_THRIFT_SERVICE); if (thriftEnabled != null && Boolean.parseBoolean(thriftEnabled)) { TSSLTransportFactory.TSSLTransportParameters transportParam = new TSSLTransportFactory.TSSLTransportParameters(); //read the keystore and password used for ssl communication from config String keystorePath = IdentityUtil.getProperty( ThriftConfigConstants.PARAM_KEYSTORE_LOCATION); String keystorePassword = IdentityUtil.getProperty( ThriftConfigConstants.PARAM_KEYSTORE_PASSWORD); //set it in parameters transportParam.setKeyStore(keystorePath, keystorePassword); //int receivePort = 10395; int receivePort = readThriftReceivePort(); //int clientTimeOut = 10000; int clientTimeOut = Integer.parseInt(IdentityUtil.getProperty( ThriftConfigConstants.PARAM_CLIENT_TIMEOUT)); //String ifAddress = "localhost"; TServerSocket serverTransport = TSSLTransportFactory.getServerSocket(receivePort, clientTimeOut, getHostAddress(readThriftHostName()), transportParam); EntitlementService.Processor processor = new EntitlementService.Processor( new ThriftEntitlementServiceImpl()); //TODO: have to decide on the protocol. TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport). processor(processor)); //TServer server = new TThreadPoolServer(new TThreadPoolServer.Args()) /* TServer server = new TThreadPoolServer(processor, serverTransport, new TCompactProtocol.Factory());*/ Runnable serverThread = new ServerRunnable(server); executor.submit(serverThread); if (log.isDebugEnabled()) { log.debug("Started thrift entitlement service at port:" + receivePort); } } } catch (TTransportException e) { String transportErrorMsg = "Error in initializing thrift transport"; log.error(transportErrorMsg, e); throw new Exception(transportErrorMsg); } catch (UnknownHostException e) { String hostErrorMsg = "Error in obtaining host name"; log.error(hostErrorMsg, e); throw new Exception(hostErrorMsg); } } /** * Read the port from identity.xml which is overridden by carbon.xml to facilitating * multiple servers at a time. */ private int readThriftReceivePort() { int port = -1; String portValue = IdentityUtil.getProperty(ThriftConfigConstants.PARAM_RECEIVE_PORT); //if the port contains a template string that refers to carbon.xml if ((portValue.contains("${")) && (portValue.contains("}"))) { port = (CarbonUtils.getPortFromServerConfig(portValue)); } else { //if port directly mentioned in identity.xml port = Integer.parseInt(portValue); } return port; } /** * Get INetAddress by host name or IP Address * * @param host name or host IP String * @return InetAddress * @throws UnknownHostException */ private InetAddress getHostAddress(String host) throws UnknownHostException { String[] splittedString = host.split("\\."); if (splittedString.length == 4) { // check whether this is ip address or not. try { Integer.parseInt(splittedString[0]); Integer.parseInt(splittedString[1]); Integer.parseInt(splittedString[2]); Integer.parseInt(splittedString[3]); byte[] byteAddress = new byte[4]; for (int i = 0; i < splittedString.length; i++) { if (Integer.parseInt(splittedString[i]) > 127) { byteAddress[i] = new Integer(Integer.parseInt(splittedString[i]) - 256).byteValue(); } else { byteAddress[i] = Byte.parseByte(splittedString[i]); } } return InetAddress.getByAddress(byteAddress); } catch (Exception e) { log.debug(e); // ignore. } } // if not ip address return host name return InetAddress.getByName(host); } /** * Read the thrift hostname from identity.xml which overrides the hostName from carbon.xml on facilitating * identifying the host for thrift server . */ private String readThriftHostName() throws SocketException { String thriftHostName = IdentityUtil.getProperty(ThriftConfigConstants.PARAM_HOST_NAME); //if the thrift host name doesn't exist in config, load from carbon.xml if (thriftHostName != null) { return thriftHostName; } else { return NetworkUtils.getLocalHostname(); } } protected void unsetNotificationSender(NotificationSender notificationSender) { if (log.isDebugEnabled()) { log.debug("Setting notification sender in Entitlement bundle"); } this.notificationSender = null; } protected void unsetIdentityCoreInitializedEventService(IdentityCoreInitializedEvent identityCoreInitializedEvent) { /* reference IdentityCoreInitializedEvent service to guarantee that this component will wait until identity core is started */ } protected void setIdentityCoreInitializedEventService(IdentityCoreInitializedEvent identityCoreInitializedEvent) { /* reference IdentityCoreInitializedEvent service to guarantee that this component will wait until identity core is started */ } /** * Thread that starts thrift server */ private static class ServerRunnable implements Runnable { TServer server; public ServerRunnable(TServer server) { this.server = server; } public void run() { server.serve(); } } }