| /* |
| * 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.syncope.core.provisioning.java; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.security.cert.CertificateException; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import javax.net.ssl.TrustManager; |
| import javax.net.ssl.X509TrustManager; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.lang3.tuple.Pair; |
| import org.apache.syncope.core.persistence.api.dao.NotFoundException; |
| import org.apache.syncope.core.persistence.api.entity.ConnInstance; |
| import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; |
| import org.apache.syncope.core.provisioning.api.utils.URIUtils; |
| import org.identityconnectors.common.IOUtil; |
| import org.identityconnectors.common.security.GuardedString; |
| import org.identityconnectors.framework.api.APIConfiguration; |
| import org.identityconnectors.framework.api.ConfigurationProperties; |
| import org.identityconnectors.framework.api.ConnectorInfo; |
| import org.identityconnectors.framework.api.ConnectorInfoManager; |
| import org.identityconnectors.framework.api.ConnectorInfoManagerFactory; |
| import org.identityconnectors.framework.api.ConnectorKey; |
| import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class ConnIdBundleManagerImpl implements ConnIdBundleManager { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(ConnIdBundleManager.class); |
| |
| /** |
| * ConnId Locations. |
| */ |
| private final List<URI> locations; |
| |
| /** |
| * ConnectorInfoManager instances. |
| */ |
| private final Map<URI, ConnectorInfoManager> connInfoManagers = Collections.synchronizedMap(new LinkedHashMap<>()); |
| |
| public ConnIdBundleManagerImpl(final List<String> stringLocations) { |
| locations = new ArrayList<>(); |
| stringLocations.forEach(location -> { |
| try { |
| locations.add(URIUtils.buildForConnId(location)); |
| LOG.info("Valid ConnId location: {}", location.trim()); |
| } catch (Exception e) { |
| LOG.error("Invalid ConnId location: {}", location.trim(), e); |
| } |
| }); |
| } |
| |
| @Override |
| public List<URI> getLocations() { |
| return locations; |
| } |
| |
| private void initLocal(final URI location) { |
| // 1. Find bundles inside local directory |
| File bundleDirectory = new File(location); |
| String[] bundleFiles = bundleDirectory.list(); |
| if (bundleFiles == null) { |
| throw new NotFoundException("Local bundles directory " + location); |
| } |
| |
| List<URL> bundleFileURLs = new ArrayList<>(); |
| for (String file : bundleFiles) { |
| try { |
| bundleFileURLs.add(IOUtil.makeURL(bundleDirectory, file)); |
| } catch (IOException ignore) { |
| // ignore exception and don't add bundle |
| LOG.debug("{}/{} is not a valid connector bundle", bundleDirectory.toString(), file, ignore); |
| } |
| } |
| |
| if (bundleFileURLs.isEmpty()) { |
| LOG.warn("No connector bundles found in {}", location); |
| } |
| LOG.debug("Configuring local connector server:" |
| + "\n\tFiles: {}", bundleFileURLs); |
| |
| // 2. Get connector info manager |
| ConnectorInfoManager manager = ConnectorInfoManagerFactory.getInstance().getLocalManager( |
| bundleFileURLs.toArray(new URL[bundleFileURLs.size()])); |
| if (manager == null) { |
| throw new NotFoundException("Local ConnectorInfoManager"); |
| } |
| |
| connInfoManagers.put(location, manager); |
| } |
| |
| private void initRemote(final URI location) { |
| // 1. Extract conf params for remote connection from given URI |
| String host = location.getHost(); |
| int port = location.getPort(); |
| GuardedString key = new GuardedString(location.getUserInfo().toCharArray()); |
| boolean useSSL = location.getScheme().equals("connids"); |
| |
| List<TrustManager> trustManagers = new ArrayList<>(); |
| String[] params = StringUtils.isBlank(location.getQuery()) ? null : location.getQuery().split("&"); |
| if (params != null && params.length > 0) { |
| final String[] trustAllCerts = params[0].split("="); |
| if (trustAllCerts.length > 1 |
| && "trustAllCerts".equalsIgnoreCase(trustAllCerts[0]) |
| && "true".equalsIgnoreCase(trustAllCerts[1])) { |
| |
| trustManagers.add(new X509TrustManager() { |
| |
| @Override |
| public void checkClientTrusted(final X509Certificate[] chain, final String authType) |
| throws CertificateException { |
| // no checks, trust all |
| } |
| |
| @Override |
| public void checkServerTrusted(final X509Certificate[] chain, final String authType) |
| throws CertificateException { |
| // no checks, trust all |
| } |
| |
| @Override |
| public X509Certificate[] getAcceptedIssuers() { |
| return null; |
| } |
| }); |
| } |
| } |
| |
| LOG.debug("Configuring remote connector server:" |
| + "\n\tHost: {}" |
| + "\n\tPort: {}" |
| + "\n\tKey: {}" |
| + "\n\tUseSSL: {}" |
| + "\n\tTrustAllCerts: {}", |
| host, port, key, useSSL, !trustManagers.isEmpty()); |
| |
| RemoteFrameworkConnectionInfo info = |
| new RemoteFrameworkConnectionInfo(host, port, key, useSSL, trustManagers, 60 * 1000); |
| LOG.debug("Remote connection info: {}", info); |
| |
| // 2. Get connector info manager |
| ConnectorInfoManager manager = ConnectorInfoManagerFactory.getInstance().getRemoteManager(info); |
| if (manager == null) { |
| throw new NotFoundException("Remote ConnectorInfoManager"); |
| } |
| |
| connInfoManagers.put(location, manager); |
| } |
| |
| @Override |
| public void resetConnManagers() { |
| connInfoManagers.clear(); |
| } |
| |
| @Override |
| public Map<URI, ConnectorInfoManager> getConnManagers() { |
| if (connInfoManagers.isEmpty()) { |
| locations.forEach(location -> { |
| try { |
| if ("file".equals(location.getScheme())) { |
| LOG.debug("Local initialization: {}", location); |
| initLocal(location); |
| } else if (location.getScheme().startsWith("connid")) { |
| LOG.debug("Remote initialization: {}", location); |
| initRemote(location); |
| } else { |
| LOG.warn("Unsupported scheme: {}", location); |
| } |
| } catch (Exception e) { |
| LOG.error("Could not process {}", location, e); |
| } |
| }); |
| } |
| |
| if (LOG.isDebugEnabled()) { |
| connInfoManagers.entrySet().stream().map(entry -> { |
| LOG.debug("Connector bundles found at {}", entry.getKey()); |
| return entry; |
| }).forEach(entry -> entry.getValue().getConnectorInfos().forEach( |
| connInfo -> LOG.debug("\t{}", connInfo.getConnectorDisplayName()))); |
| } |
| |
| return connInfoManagers; |
| } |
| |
| @Override |
| public Pair<URI, ConnectorInfo> getConnectorInfo(final ConnInstance connInstance) { |
| // check ConnIdLocation |
| URI uriLocation = null; |
| try { |
| uriLocation = URIUtils.buildForConnId(connInstance.getLocation()); |
| } catch (Exception e) { |
| throw new IllegalArgumentException("Invalid ConnId location " + connInstance.getLocation(), e); |
| } |
| |
| // create key for search all properties |
| ConnectorKey key = new ConnectorKey( |
| connInstance.getBundleName(), connInstance.getVersion(), connInstance.getConnectorName()); |
| |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("\nBundle name: " + key.getBundleName() |
| + "\nBundle version: " + key.getBundleVersion() |
| + "\nBundle class: " + key.getConnectorName()); |
| } |
| |
| // get the specified connector |
| ConnectorInfo info = null; |
| if (getConnManagers().containsKey(uriLocation)) { |
| info = getConnManagers().get(uriLocation).findConnectorInfo(key); |
| } |
| if (info == null) { |
| throw new NotFoundException("ConnectorInfo for location " + connInstance.getLocation() + " and key " + key); |
| } |
| |
| return Pair.of(uriLocation, info); |
| } |
| |
| @Override |
| public Map<URI, ConnectorInfoManager> getConnInfoManagers() { |
| return connInfoManagers; |
| } |
| |
| @Override |
| public ConfigurationProperties getConfigurationProperties(final ConnectorInfo info) { |
| if (info == null) { |
| throw new NotFoundException("Invalid: connector info is null"); |
| } |
| |
| // create default configuration |
| APIConfiguration apiConfig = info.createDefaultAPIConfiguration(); |
| |
| // retrieve the ConfigurationProperties. |
| ConfigurationProperties properties = apiConfig.getConfigurationProperties(); |
| if (properties == null) { |
| throw new NotFoundException("Configuration properties"); |
| } |
| |
| if (LOG.isDebugEnabled()) { |
| properties.getPropertyNames().forEach(propName -> LOG.debug("Property Name: {}" |
| + "\nProperty Type: {}", |
| properties.getProperty(propName).getName(), |
| properties.getProperty(propName).getType())); |
| } |
| |
| return properties; |
| } |
| } |