1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
58
59 protected final List<URI> locations;
60
61
62
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
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
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
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
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
139 }
140
141 @Override
142 public void checkServerTrusted(final X509Certificate[] chain, final String authType)
143 throws CertificateException {
144
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
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
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
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
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
256 APIConfiguration apiConfig = info.createDefaultAPIConfiguration();
257
258
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 }