View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.provisioning.java;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.URI;
24  import java.net.URL;
25  import java.security.cert.CertificateException;
26  import java.security.cert.X509Certificate;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  import java.util.Map;
32  import javax.net.ssl.TrustManager;
33  import javax.net.ssl.X509TrustManager;
34  import org.apache.commons.lang3.StringUtils;
35  import org.apache.commons.lang3.tuple.Pair;
36  import org.apache.syncope.core.persistence.api.dao.NotFoundException;
37  import org.apache.syncope.core.persistence.api.entity.ConnInstance;
38  import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
39  import org.apache.syncope.core.provisioning.api.utils.URIUtils;
40  import org.identityconnectors.common.IOUtil;
41  import org.identityconnectors.common.security.GuardedString;
42  import org.identityconnectors.framework.api.APIConfiguration;
43  import org.identityconnectors.framework.api.ConfigurationProperties;
44  import org.identityconnectors.framework.api.ConnectorInfo;
45  import org.identityconnectors.framework.api.ConnectorInfoManager;
46  import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;
47  import org.identityconnectors.framework.api.ConnectorKey;
48  import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  public class DefaultConnIdBundleManager implements ConnIdBundleManager {
53  
54      protected static final Logger LOG = LoggerFactory.getLogger(ConnIdBundleManager.class);
55  
56      /**
57       * ConnId Locations.
58       */
59      protected final List<URI> locations;
60  
61      /**
62       * ConnectorInfoManager instances.
63       */
64      protected final Map<URI, ConnectorInfoManager> connInfoManagers =
65              Collections.synchronizedMap(new LinkedHashMap<>());
66  
67      public DefaultConnIdBundleManager(final List<String> stringLocations) {
68          locations = new ArrayList<>();
69          stringLocations.forEach(location -> {
70              try {
71                  locations.add(URIUtils.buildForConnId(location));
72                  LOG.info("Valid ConnId location: {}", location.trim());
73              } catch (Exception e) {
74                  LOG.error("Invalid ConnId location: {}", location.trim(), e);
75              }
76          });
77      }
78  
79      @Override
80      public List<URI> getLocations() {
81          return locations;
82      }
83  
84      protected void initLocal(final URI location) {
85          // 1. Find bundles inside local directory
86          File bundleDirectory = new File(location);
87          String[] bundleFiles = bundleDirectory.list();
88          if (bundleFiles == null) {
89              throw new NotFoundException("Local bundles directory " + location);
90          }
91  
92          List<URL> bundleFileURLs = new ArrayList<>();
93          for (String file : bundleFiles) {
94              try {
95                  bundleFileURLs.add(IOUtil.makeURL(bundleDirectory, file));
96              } catch (IOException ignore) {
97                  // ignore exception and don't add bundle
98                  LOG.debug("{}/{} is not a valid connector bundle", bundleDirectory.toString(), file, ignore);
99              }
100         }
101 
102         if (bundleFileURLs.isEmpty()) {
103             LOG.warn("No connector bundles found in {}", location);
104         }
105         LOG.debug("Configuring local connector server:"
106                 + "\n\tFiles: {}", bundleFileURLs);
107 
108         // 2. Get connector info manager
109         ConnectorInfoManager manager =
110                 ConnectorInfoManagerFactory.getInstance().getLocalManager(bundleFileURLs.toArray(URL[]::new));
111         if (manager == null) {
112             throw new NotFoundException("Local ConnectorInfoManager");
113         }
114 
115         connInfoManagers.put(location, manager);
116     }
117 
118     protected void initRemote(final URI location) {
119         // 1. Extract conf params for remote connection from given URI
120         String host = location.getHost();
121         int port = location.getPort();
122         GuardedString key = new GuardedString(location.getUserInfo().toCharArray());
123         boolean useSSL = location.getScheme().equals("connids");
124 
125         List<TrustManager> trustManagers = new ArrayList<>();
126         String[] params = StringUtils.isBlank(location.getQuery()) ? null : location.getQuery().split("&");
127         if (params != null && params.length > 0) {
128             final String[] trustAllCerts = params[0].split("=");
129             if (trustAllCerts.length > 1
130                     && "trustAllCerts".equalsIgnoreCase(trustAllCerts[0])
131                     && "true".equalsIgnoreCase(trustAllCerts[1])) {
132 
133                 trustManagers.add(new X509TrustManager() {
134 
135                     @Override
136                     public void checkClientTrusted(final X509Certificate[] chain, final String authType)
137                             throws CertificateException {
138                         // no checks, trust all
139                     }
140 
141                     @Override
142                     public void checkServerTrusted(final X509Certificate[] chain, final String authType)
143                             throws CertificateException {
144                         // no checks, trust all
145                     }
146 
147                     @Override
148                     public X509Certificate[] getAcceptedIssuers() {
149                         return null;
150                     }
151                 });
152             }
153         }
154 
155         LOG.debug("Configuring remote connector server:"
156                 + "\n\tHost: {}"
157                 + "\n\tPort: {}"
158                 + "\n\tKey: {}"
159                 + "\n\tUseSSL: {}"
160                 + "\n\tTrustAllCerts: {}",
161                 host, port, key, useSSL, !trustManagers.isEmpty());
162 
163         RemoteFrameworkConnectionInfo info =
164                 new RemoteFrameworkConnectionInfo(host, port, key, useSSL, trustManagers, 60 * 1000);
165         LOG.debug("Remote connection info: {}", info);
166 
167         // 2. Get connector info manager
168         ConnectorInfoManager manager = ConnectorInfoManagerFactory.getInstance().getRemoteManager(info);
169         if (manager == null) {
170             throw new NotFoundException("Remote ConnectorInfoManager");
171         }
172 
173         connInfoManagers.put(location, manager);
174     }
175 
176     @Override
177     public void resetConnManagers() {
178         connInfoManagers.clear();
179     }
180 
181     @Override
182     public Map<URI, ConnectorInfoManager> getConnManagers() {
183         if (connInfoManagers.isEmpty()) {
184             locations.forEach(location -> {
185                 try {
186                     if ("file".equals(location.getScheme())) {
187                         LOG.debug("Local initialization: {}", location);
188                         initLocal(location);
189                     } else if (location.getScheme().startsWith("connid")) {
190                         LOG.debug("Remote initialization: {}", location);
191                         initRemote(location);
192                     } else {
193                         LOG.warn("Unsupported scheme: {}", location);
194                     }
195                 } catch (Exception e) {
196                     LOG.error("Could not process {}", location, e);
197                 }
198             });
199         }
200 
201         if (LOG.isDebugEnabled()) {
202             connInfoManagers.entrySet().stream().map(entry -> {
203                 LOG.debug("Connector bundles found at {}", entry.getKey());
204                 return entry;
205             }).forEach(entry -> entry.getValue().getConnectorInfos().forEach(
206                     connInfo -> LOG.debug("\t{}", connInfo.getConnectorDisplayName())));
207         }
208 
209         return connInfoManagers;
210     }
211 
212     @Override
213     public Pair<URI, ConnectorInfo> getConnectorInfo(final ConnInstance connInstance) {
214         // check ConnIdLocation
215         URI uriLocation = null;
216         try {
217             uriLocation = URIUtils.buildForConnId(connInstance.getLocation());
218         } catch (Exception e) {
219             throw new IllegalArgumentException("Invalid ConnId location " + connInstance.getLocation(), e);
220         }
221 
222         // create key for search all properties
223         ConnectorKey key = new ConnectorKey(
224                 connInstance.getBundleName(), connInstance.getVersion(), connInstance.getConnectorName());
225 
226         if (LOG.isDebugEnabled()) {
227             LOG.debug("\nBundle name: " + key.getBundleName()
228                     + "\nBundle version: " + key.getBundleVersion()
229                     + "\nBundle class: " + key.getConnectorName());
230         }
231 
232         // get the specified connector
233         ConnectorInfo info = null;
234         if (getConnManagers().containsKey(uriLocation)) {
235             info = getConnManagers().get(uriLocation).findConnectorInfo(key);
236         }
237         if (info == null) {
238             throw new NotFoundException("ConnectorInfo for location " + connInstance.getLocation() + " and key " + key);
239         }
240 
241         return Pair.of(uriLocation, info);
242     }
243 
244     @Override
245     public Map<URI, ConnectorInfoManager> getConnInfoManagers() {
246         return connInfoManagers;
247     }
248 
249     @Override
250     public ConfigurationProperties getConfigurationProperties(final ConnectorInfo info) {
251         if (info == null) {
252             throw new NotFoundException("Invalid: connector info is null");
253         }
254 
255         // create default configuration
256         APIConfiguration apiConfig = info.createDefaultAPIConfiguration();
257 
258         // retrieve the ConfigurationProperties.
259         ConfigurationProperties properties = apiConfig.getConfigurationProperties();
260         if (properties == null) {
261             throw new NotFoundException("Configuration properties");
262         }
263 
264         if (LOG.isDebugEnabled()) {
265             properties.getPropertyNames().forEach(propName -> LOG.debug("Property Name: {}"
266                     + "\nProperty Type: {}",
267                     properties.getProperty(propName).getName(),
268                     properties.getProperty(propName).getType()));
269         }
270 
271         return properties;
272     }
273 }