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.client.console.rest;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.nio.charset.StandardCharsets;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.List;
27  import java.util.Optional;
28  import java.util.stream.Collectors;
29  import javax.ws.rs.core.MediaType;
30  import javax.ws.rs.core.Response;
31  import org.apache.commons.io.IOUtils;
32  import org.apache.commons.lang3.tuple.Pair;
33  import org.apache.cxf.jaxrs.client.WebClient;
34  import org.apache.syncope.client.console.SyncopeConsoleSession;
35  import org.apache.syncope.common.lib.SyncopeClientException;
36  import org.apache.syncope.common.lib.to.ConnIdBundle;
37  import org.apache.syncope.common.lib.to.ConnIdObjectClass;
38  import org.apache.syncope.common.lib.to.ConnInstanceTO;
39  import org.apache.syncope.common.lib.to.PlainSchemaTO;
40  import org.apache.syncope.common.lib.types.ConnConfProperty;
41  import org.apache.syncope.common.rest.api.RESTHeaders;
42  import org.apache.syncope.common.rest.api.service.ConnectorService;
43  import org.springframework.beans.BeanUtils;
44  
45  /**
46   * Console client for invoking Rest Connectors services.
47   */
48  public class ConnectorRestClient extends BaseRestClient {
49  
50      private static final long serialVersionUID = -6870366819966266617L;
51  
52      public List<ConnInstanceTO> getAllConnectors() {
53          List<ConnInstanceTO> connectors = List.of();
54          try {
55              connectors = getService(ConnectorService.class).list(SyncopeConsoleSession.get().getLocale().toString());
56          } catch (Exception e) {
57              LOG.error("While reading connectors", e);
58          }
59          return connectors;
60      }
61  
62      public ConnInstanceTO create(final ConnInstanceTO connectorTO) {
63          List<ConnConfProperty> filteredConf = filterProperties(connectorTO.getConf());
64          connectorTO.getConf().clear();
65          connectorTO.getConf().addAll(filteredConf);
66  
67          ConnectorService service = getService(ConnectorService.class);
68          Response response = service.create(connectorTO);
69  
70          return getObject(service, response.getLocation(), ConnInstanceTO.class);
71      }
72  
73      public List<String> getObjectClasses(final String connectorKey) {
74          List<String> result = new ArrayList<>();
75          try {
76              ConnectorService service = getService(ConnectorService.class);
77              ConnInstanceTO connInstance = service.read(connectorKey, SyncopeConsoleSession.get().getLocale().
78                      getLanguage());
79              if (connInstance != null) {
80                  result.addAll(service.buildObjectClassInfo(connInstance, true).stream().
81                          map(ConnIdObjectClass::getType).collect(Collectors.toList()));
82              }
83          } catch (Exception e) {
84              LOG.error("While reading object classes for connector {}", connectorKey, e);
85          }
86          return result;
87      }
88  
89      public List<String> getExtAttrNames(
90              final String adminRealm,
91              final String objectClass,
92              final String connectorKey,
93              final Collection<ConnConfProperty> conf) {
94  
95          ConnInstanceTO connInstanceTO = new ConnInstanceTO();
96          connInstanceTO.setAdminRealm(adminRealm);
97          connInstanceTO.setKey(connectorKey);
98          connInstanceTO.getConf().addAll(conf);
99  
100         // SYNCOPE-156: use provided info to give schema names (and type!) by ObjectClass
101         Optional<ConnIdObjectClass> connIdObjectClass = buildObjectClassInfo(connInstanceTO, false).stream().
102                 filter(object -> object.getType().equalsIgnoreCase(objectClass)).
103                 findAny();
104 
105         return connIdObjectClass.map(connIdObjectClassTO -> connIdObjectClassTO.getAttributes().stream().
106                 map(PlainSchemaTO::getKey).collect(Collectors.toList())).orElseGet(List::of);
107     }
108 
109     /**
110      * Load an already existent connector by its name.
111      *
112      * @param key the id
113      * @return ConnInstanceTO
114      */
115     public ConnInstanceTO read(final String key) {
116         ConnInstanceTO connectorTO = null;
117 
118         try {
119             connectorTO = getService(ConnectorService.class).
120                     read(key, SyncopeConsoleSession.get().getLocale().toString());
121         } catch (SyncopeClientException e) {
122             LOG.error("While reading a connector", e);
123         }
124 
125         return connectorTO;
126     }
127 
128     public void update(final ConnInstanceTO connectorTO) {
129         List<ConnConfProperty> filteredConf = filterProperties(connectorTO.getConf());
130         connectorTO.getConf().clear();
131         connectorTO.getConf().addAll(filteredConf);
132         getService(ConnectorService.class).update(connectorTO);
133     }
134 
135     public ConnInstanceTO delete(final String key) {
136         ConnInstanceTO connectorTO = getService(ConnectorService.class).
137                 read(key, SyncopeConsoleSession.get().getLocale().toString());
138         getService(ConnectorService.class).delete(key);
139         return connectorTO;
140     }
141 
142     public List<ConnIdBundle> getAllBundles() {
143         List<ConnIdBundle> bundles = List.of();
144 
145         try {
146             bundles = getService(ConnectorService.class).getBundles(SyncopeConsoleSession.get().getLocale().toString());
147         } catch (SyncopeClientException e) {
148             LOG.error("While getting connector bundles", e);
149         }
150 
151         return bundles;
152     }
153 
154     protected List<ConnConfProperty> filterProperties(final Collection<ConnConfProperty> properties) {
155         List<ConnConfProperty> newProperties = new ArrayList<>();
156 
157         properties.stream().map(property -> {
158             ConnConfProperty prop = new ConnConfProperty();
159             prop.setSchema(property.getSchema());
160             prop.setOverridable(property.isOverridable());
161             final List<Object> parsed = new ArrayList<>();
162             if (property.getValues() != null) {
163                 property.getValues().stream().
164                         filter(obj -> (obj != null && !obj.toString().isEmpty())).
165                         forEachOrdered(parsed::add);
166             }
167             prop.getValues().addAll(parsed);
168             return prop;
169         }).forEachOrdered(newProperties::add);
170         return newProperties;
171     }
172 
173     public boolean check(final String coreAddress, final String domain, final String jwt, final String key)
174             throws IOException {
175 
176         WebClient client = WebClient.create(coreAddress).path("connectors").
177                 accept(MediaType.APPLICATION_JSON_TYPE).
178                 type(MediaType.APPLICATION_JSON_TYPE).
179                 header(RESTHeaders.DOMAIN, domain).
180                 authorization("Bearer " + jwt);
181         Response response = client.path(key).get();
182         if (response.getStatus() == Response.Status.OK.getStatusCode()) {
183             response = client.back(false).path("check").
184                     post(IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8));
185             return response.getStatus() == Response.Status.NO_CONTENT.getStatusCode();
186         }
187         return false;
188     }
189 
190     public Pair<Boolean, String> check(final ConnInstanceTO connectorTO) {
191         ConnInstanceTO toBeChecked = new ConnInstanceTO();
192         BeanUtils.copyProperties(connectorTO, toBeChecked, new String[] { "configuration", "configurationMap" });
193         toBeChecked.getConf().addAll(filterProperties(connectorTO.getConf()));
194 
195         boolean check = false;
196         String errorMessage = null;
197         try {
198             getService(ConnectorService.class).check(toBeChecked);
199             check = true;
200         } catch (Exception e) {
201             LOG.error("While checking {}", toBeChecked, e);
202             errorMessage = e.getMessage();
203         }
204 
205         return Pair.of(check, errorMessage);
206     }
207 
208     public List<ConnIdObjectClass> buildObjectClassInfo(
209             final ConnInstanceTO connInstanceTO, final boolean includeSpecial) {
210 
211         List<ConnIdObjectClass> result = List.of();
212         try {
213             result = getService(ConnectorService.class).buildObjectClassInfo(connInstanceTO, includeSpecial);
214         } catch (Exception e) {
215             LOG.error("While getting supported object classes", e);
216         }
217 
218         return result;
219     }
220 
221     public void reload() {
222         getService(ConnectorService.class).reload();
223     }
224 }