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.ext.opensearch.client;
20  
21  import com.fasterxml.jackson.databind.JsonNode;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Optional;
30  import java.util.Set;
31  import java.util.stream.Collectors;
32  import org.apache.commons.lang3.ArrayUtils;
33  import org.apache.syncope.common.lib.types.AnyTypeKind;
34  import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
35  import org.apache.syncope.core.persistence.api.dao.GroupDAO;
36  import org.apache.syncope.core.persistence.api.dao.UserDAO;
37  import org.apache.syncope.core.persistence.api.entity.Any;
38  import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
39  import org.apache.syncope.core.persistence.api.entity.PlainAttr;
40  import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
41  import org.apache.syncope.core.persistence.api.entity.Privilege;
42  import org.apache.syncope.core.persistence.api.entity.Realm;
43  import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
44  import org.apache.syncope.core.persistence.api.entity.group.Group;
45  import org.apache.syncope.core.persistence.api.entity.user.User;
46  import org.springframework.transaction.annotation.Transactional;
47  
48  /**
49   * Utility methods for usage with OpenSearch.
50   */
51  public class OpenSearchUtils {
52  
53      public static String getAnyIndex(final String domain, final AnyTypeKind kind) {
54          return domain.toLowerCase() + '_' + kind.name().toLowerCase();
55      }
56  
57      public static String getRealmIndex(final String domain) {
58          return domain.toLowerCase() + "_realm";
59      }
60  
61      public static String getAuditIndex(final String domain) {
62          return domain.toLowerCase() + "_audit";
63      }
64  
65      protected static final char[] ELASTICSEARCH_REGEX_CHARS = new char[] {
66          '.', '?', '+', '*', '|', '{', '}', '[', ']', '(', ')', '"', '\\', '&' };
67  
68      public static String escapeForLikeRegex(final char c) {
69          StringBuilder output = new StringBuilder();
70  
71          if (ArrayUtils.contains(ELASTICSEARCH_REGEX_CHARS, c)) {
72              output.append('\\');
73          }
74  
75          output.append(c);
76  
77          return output.toString();
78      }
79  
80      protected final UserDAO userDAO;
81  
82      protected final GroupDAO groupDAO;
83  
84      protected final AnyObjectDAO anyObjectDAO;
85  
86      public OpenSearchUtils(
87              final UserDAO userDAO,
88              final GroupDAO groupDAO,
89              final AnyObjectDAO anyObjectDAO) {
90  
91          this.userDAO = userDAO;
92          this.groupDAO = groupDAO;
93          this.anyObjectDAO = anyObjectDAO;
94      }
95  
96      /**
97       * Returns the document specialized with content from the provided any.
98       *
99       * @param any user, group or any object to index
100      * @return document specialized with content from the provided any
101      */
102     @Transactional
103     public Map<String, Object> document(final Any<?> any) {
104         Collection<String> resources = any instanceof User
105                 ? userDAO.findAllResourceKeys(any.getKey())
106                 : any instanceof AnyObject
107                         ? anyObjectDAO.findAllResourceKeys(any.getKey())
108                         : groupDAO.findAllResourceKeys(any.getKey());
109         Collection<String> dynRealms = any instanceof User
110                 ? userDAO.findDynRealms(any.getKey())
111                 : any instanceof AnyObject
112                         ? anyObjectDAO.findDynRealms(any.getKey())
113                         : groupDAO.findDynRealms(any.getKey());
114 
115         Map<String, Object> builder = new HashMap<>();
116         builder.put("id", any.getKey());
117         builder.put("realm", any.getRealm().getKey());
118         builder.put("anyType", any.getType().getKey());
119         builder.put("creationDate", any.getCreationDate());
120         builder.put("creationContext", any.getCreationContext());
121         builder.put("creator", any.getCreator());
122         builder.put("lastChangeDate", any.getLastChangeDate());
123         builder.put("lastModifier", any.getLastModifier());
124         builder.put("lastChangeContext", any.getLastChangeContext());
125         builder.put("status", any.getStatus());
126         builder.put("auxClasses", any.getAuxClasses().stream().map(AnyTypeClass::getKey).collect(Collectors.toList()));
127         builder.put("resources", resources);
128         builder.put("dynRealms", dynRealms);
129 
130         if (any instanceof AnyObject) {
131             AnyObject anyObject = ((AnyObject) any);
132             builder.put("name", anyObject.getName());
133 
134             builder.put("memberships", anyObjectDAO.findAllGroupKeys(anyObject));
135 
136             List<String> relationships = new ArrayList<>();
137             List<String> relationshipTypes = new ArrayList<>();
138             anyObjectDAO.findAllRelationships(anyObject).forEach(relationship -> {
139                 relationships.add(relationship.getRightEnd().getKey());
140                 relationshipTypes.add(relationship.getType().getKey());
141             });
142             builder.put("relationships", relationships);
143             builder.put("relationshipTypes", relationshipTypes);
144 
145             customizeDocument(builder, anyObject);
146         } else if (any instanceof Group) {
147             Group group = ((Group) any);
148             builder.put("name", group.getName());
149             Optional.ofNullable(group.getUserOwner()).ifPresent(uo -> builder.put("userOwner", uo.getKey()));
150             Optional.ofNullable(group.getGroupOwner()).ifPresent(go -> builder.put("groupOwner", go.getKey()));
151 
152             Set<String> members = new HashSet<>();
153             members.addAll(groupDAO.findUMemberships(group).stream().
154                     map(membership -> membership.getLeftEnd().getKey()).collect(Collectors.toList()));
155             members.addAll(groupDAO.findUDynMembers(group));
156             members.addAll(groupDAO.findAMemberships(group).stream().
157                     map(membership -> membership.getLeftEnd().getKey()).collect(Collectors.toList()));
158             members.addAll(groupDAO.findADynMembers(group));
159             builder.put("members", members);
160 
161             customizeDocument(builder, group);
162         } else if (any instanceof User) {
163             User user = ((User) any);
164             builder.put("username", user.getUsername());
165             builder.put("token", user.getToken());
166             builder.put("tokenExpireTime", user.getTokenExpireTime());
167             builder.put("changePwdDate", user.getChangePwdDate());
168             builder.put("failedLogins", user.getFailedLogins());
169             builder.put("lastLoginDate", user.getLastLoginDate());
170             builder.put("suspended", user.isSuspended());
171             builder.put("mustChangePassword", user.isMustChangePassword());
172 
173             List<String> roles = new ArrayList<>();
174             Set<String> privileges = new HashSet<>();
175             userDAO.findAllRoles(user).forEach(role -> {
176                 roles.add(role.getKey());
177                 privileges.addAll(role.getPrivileges().stream().map(Privilege::getKey).collect(Collectors.toSet()));
178             });
179             builder.put("roles", roles);
180             builder.put("privileges", privileges);
181 
182             builder.put("memberships", userDAO.findAllGroupKeys(user));
183 
184             List<String> relationships = new ArrayList<>();
185             Set<String> relationshipTypes = new HashSet<>();
186             user.getRelationships().forEach(relationship -> {
187                 relationships.add(relationship.getRightEnd().getKey());
188                 relationshipTypes.add(relationship.getType().getKey());
189             });
190             builder.put("relationships", relationships);
191             builder.put("relationshipTypes", relationshipTypes);
192 
193             customizeDocument(builder, user);
194         }
195 
196         for (PlainAttr<?> plainAttr : any.getPlainAttrs()) {
197             List<Object> values = plainAttr.getValues().stream().
198                     map(PlainAttrValue::getValue).collect(Collectors.toList());
199 
200             Optional.ofNullable(plainAttr.getUniqueValue()).ifPresent(v -> values.add(v.getValue()));
201 
202             builder.put(plainAttr.getSchema().getKey(), values.size() == 1 ? values.get(0) : values);
203         }
204 
205         return builder;
206     }
207 
208     protected void customizeDocument(final Map<String, Object> builder, final AnyObject anyObject) {
209     }
210 
211     protected void customizeDocument(final Map<String, Object> builder, final Group group) {
212     }
213 
214     protected void customizeDocument(final Map<String, Object> builder, final User user) {
215     }
216 
217     public Map<String, Object> document(final Realm realm) {
218         Map<String, Object> builder = new HashMap<>();
219         builder.put("id", realm.getKey());
220         builder.put("name", realm.getName());
221         builder.put("parent_id", realm.getParent() == null ? null : realm.getParent().getKey());
222         builder.put("fullPath", realm.getFullPath());
223 
224         customizeDocument(builder, realm);
225 
226         return builder;
227     }
228 
229     protected void customizeDocument(final Map<String, Object> builder, final Realm realm) {
230     }
231 
232     public Map<String, Object> document(
233             final long instant,
234             final JsonNode message,
235             final String domain) throws IOException {
236 
237         Map<String, Object> builder = new HashMap<>();
238 
239         builder.put("instant", instant);
240         builder.put("message", message);
241 
242         customizeDocument(builder, instant, message, domain);
243 
244         return builder;
245     }
246 
247     protected void customizeDocument(
248             final Map<String, Object> builder,
249             final long instant,
250             final JsonNode message,
251             final String domain)
252             throws IOException {
253     }
254 }