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.utils;
20  
21  import java.util.Map;
22  import java.util.Objects;
23  import java.util.Optional;
24  import org.apache.commons.jexl3.MapContext;
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.syncope.common.lib.Attr;
27  import org.apache.syncope.common.lib.EntityTOUtils;
28  import org.apache.syncope.common.lib.RealmMember;
29  import org.apache.syncope.common.lib.SyncopeClientException;
30  import org.apache.syncope.common.lib.request.GroupCR;
31  import org.apache.syncope.common.lib.request.UserCR;
32  import org.apache.syncope.common.lib.to.AnyObjectTO;
33  import org.apache.syncope.common.lib.to.AnyTO;
34  import org.apache.syncope.common.lib.to.GroupTO;
35  import org.apache.syncope.common.lib.to.GroupableRelatableTO;
36  import org.apache.syncope.common.lib.to.UserTO;
37  import org.apache.syncope.common.lib.types.ClientExceptionType;
38  import org.apache.syncope.core.persistence.api.dao.GroupDAO;
39  import org.apache.syncope.core.persistence.api.dao.UserDAO;
40  import org.apache.syncope.core.persistence.api.entity.AnyTemplate;
41  import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
42  import org.springframework.transaction.annotation.Transactional;
43  
44  public class TemplateUtils {
45  
46      protected static Attr evaluateAttr(final Attr template, final MapContext jexlContext) {
47          Attr result = new Attr();
48          result.setSchema(template.getSchema());
49  
50          if (template.getValues() != null && !template.getValues().isEmpty()) {
51              template.getValues().forEach(value -> {
52                  String evaluated = JexlUtils.evaluate(value, jexlContext).toString();
53                  if (StringUtils.isNotBlank(evaluated)) {
54                      result.getValues().add(evaluated);
55                  }
56              });
57          }
58  
59          return result;
60      }
61  
62      protected static void fill(final RealmMember realmMember, final RealmMember template) {
63          MapContext jexlContext = new MapContext();
64          JexlUtils.addFieldsToContext(realmMember, jexlContext);
65          JexlUtils.addAttrsToContext(realmMember.getPlainAttrs(), jexlContext);
66          JexlUtils.addAttrsToContext(realmMember.getDerAttrs(), jexlContext);
67          JexlUtils.addAttrsToContext(realmMember.getVirAttrs(), jexlContext);
68  
69          if (template.getRealm() != null) {
70              String evaluated = JexlUtils.evaluate(template.getRealm(), jexlContext).toString();
71              if (StringUtils.isNotBlank(evaluated)) {
72                  realmMember.setRealm(evaluated);
73              }
74          }
75  
76          Map<String, Attr> currentAttrMap = EntityTOUtils.buildAttrMap(realmMember.getPlainAttrs());
77          for (Attr templatePlainAttr : template.getPlainAttrs()) {
78              if (!templatePlainAttr.getValues().isEmpty()
79                      && (!currentAttrMap.containsKey(templatePlainAttr.getSchema())
80                      || currentAttrMap.get(templatePlainAttr.getSchema()).getValues().isEmpty())) {
81  
82                  Attr evaluated = evaluateAttr(templatePlainAttr, jexlContext);
83                  if (!evaluated.getValues().isEmpty()) {
84                      realmMember.getPlainAttrs().add(evaluated);
85                      jexlContext.set(evaluated.getSchema(), evaluated.getValues().get(0));
86                  }
87              }
88          }
89  
90          currentAttrMap = EntityTOUtils.buildAttrMap(realmMember.getDerAttrs());
91          for (Attr templateDerAttr : template.getDerAttrs()) {
92              if (!currentAttrMap.containsKey(templateDerAttr.getSchema())) {
93                  realmMember.getDerAttrs().add(templateDerAttr);
94              }
95          }
96  
97          currentAttrMap = EntityTOUtils.buildAttrMap(realmMember.getVirAttrs());
98          for (Attr templateVirAttr : template.getVirAttrs()) {
99              if (!templateVirAttr.getValues().isEmpty()
100                     && (!currentAttrMap.containsKey(templateVirAttr.getSchema())
101                     || currentAttrMap.get(templateVirAttr.getSchema()).getValues().isEmpty())) {
102 
103                 Attr evaluated = evaluateAttr(templateVirAttr, jexlContext);
104                 if (!evaluated.getValues().isEmpty()) {
105                     realmMember.getVirAttrs().add(evaluated);
106                     jexlContext.set(evaluated.getSchema(), evaluated.getValues().get(0));
107                 }
108             }
109         }
110 
111         realmMember.getResources().addAll(template.getResources());
112 
113         realmMember.getAuxClasses().addAll(template.getAuxClasses());
114     }
115 
116     protected static void fillRelationships(final GroupableRelatableTO any, final GroupableRelatableTO template) {
117         template.getRelationships().stream().
118                 filter(relationship -> any.getRelationship(
119                 relationship.getOtherEndKey(), relationship.getOtherEndKey()).isEmpty()).
120                 forEachOrdered(relationship -> any.getRelationships().add(relationship));
121     }
122 
123     protected static void fillMemberships(final GroupableRelatableTO any, final GroupableRelatableTO template) {
124         template.getMemberships().stream().
125                 filter(membership -> any.getMembership(membership.getGroupKey()).isEmpty()).
126                 forEachOrdered(membership -> any.getMemberships().add(membership));
127     }
128 
129     protected final UserDAO userDAO;
130 
131     protected final GroupDAO groupDAO;
132 
133     public TemplateUtils(final UserDAO userDAO, final GroupDAO groupDAO) {
134         this.userDAO = userDAO;
135         this.groupDAO = groupDAO;
136     }
137 
138     @Transactional(readOnly = true)
139     public void apply(final RealmMember realmMember, final Optional<? extends AnyTemplate> template) {
140         template.ifPresent(anyTemplate -> apply(realmMember, anyTemplate.get()));
141     }
142 
143     @Transactional(readOnly = true)
144     public void apply(final RealmMember realmMember, final AnyTO template) {
145         fill(realmMember, template);
146 
147         MapContext jexlContext = new MapContext();
148         JexlUtils.addFieldsToContext(realmMember, jexlContext);
149         JexlUtils.addAttrsToContext(realmMember.getPlainAttrs(), jexlContext);
150         JexlUtils.addAttrsToContext(realmMember.getDerAttrs(), jexlContext);
151         JexlUtils.addAttrsToContext(realmMember.getVirAttrs(), jexlContext);
152 
153         if (template instanceof AnyObjectTO) {
154             fillRelationships((GroupableRelatableTO) realmMember, ((GroupableRelatableTO) template));
155             fillMemberships((GroupableRelatableTO) realmMember, ((GroupableRelatableTO) template));
156         } else if (template instanceof UserTO) {
157             if (StringUtils.isNotBlank(((UserTO) template).getUsername())) {
158                 String evaluated = JexlUtils.evaluate(((UserTO) template).getUsername(), jexlContext).toString();
159                 if (StringUtils.isNotBlank(evaluated)) {
160                     if (realmMember instanceof UserTO) {
161                         ((UserTO) realmMember).setUsername(evaluated);
162                     } else if (realmMember instanceof UserCR) {
163                         ((UserCR) realmMember).setUsername(evaluated);
164                     }
165                 }
166             }
167 
168             if (StringUtils.isNotBlank(((UserTO) template).getPassword())) {
169                 String evaluated = JexlUtils.evaluate(((UserTO) template).getPassword(), jexlContext).toString();
170                 if (StringUtils.isNotBlank(evaluated)) {
171                     if (realmMember instanceof UserTO) {
172                         ((UserTO) realmMember).setPassword(evaluated);
173                     } else if (realmMember instanceof UserCR) {
174                         ((UserCR) realmMember).setPassword(evaluated);
175                     }
176                 }
177             }
178 
179             if (((UserTO) template).isMustChangePassword()) {
180                 if (realmMember instanceof UserTO) {
181                     ((UserTO) realmMember).setMustChangePassword(true);
182                 } else if (realmMember instanceof UserCR) {
183                     ((UserCR) realmMember).setMustChangePassword(true);
184                 }
185             }
186 
187             fillRelationships((GroupableRelatableTO) realmMember, ((GroupableRelatableTO) template));
188             fillMemberships((GroupableRelatableTO) realmMember, ((GroupableRelatableTO) template));
189 
190             ((UserTO) template).getRoles().forEach(role -> {
191                 if (realmMember instanceof UserTO
192                         && !((UserTO) realmMember).getRoles().contains(role)) {
193 
194                     ((UserTO) realmMember).getRoles().add(role);
195                 } else if (realmMember instanceof UserCR
196                         && !((UserCR) realmMember).getRoles().contains(role)) {
197 
198                     ((UserCR) realmMember).getRoles().add(role);
199                 }
200             });
201 
202             ((UserTO) template).getLinkedAccounts().forEach(account -> {
203                 if (realmMember instanceof UserTO && ((UserTO) realmMember).getLinkedAccounts().stream().
204                         noneMatch(a -> Objects.equals(account.getConnObjectKeyValue(), a.getConnObjectKeyValue())
205                         && Objects.equals(account.getResource(), a.getResource()))) {
206 
207                     ((UserTO) realmMember).getLinkedAccounts().add(account);
208                 } else if (realmMember instanceof UserCR && ((UserCR) realmMember).getLinkedAccounts().stream().
209                         noneMatch(a -> Objects.equals(account.getConnObjectKeyValue(), a.getConnObjectKeyValue())
210                         && Objects.equals(account.getResource(), a.getResource()))) {
211 
212                     ((UserCR) realmMember).getLinkedAccounts().add(account);
213                 }
214             });
215         } else if (template instanceof GroupTO) {
216             if (StringUtils.isNotBlank(((GroupTO) template).getName())) {
217                 String evaluated = JexlUtils.evaluate(((GroupTO) template).getName(), jexlContext).toString();
218                 if (StringUtils.isNotBlank(evaluated)) {
219                     if (realmMember instanceof GroupTO) {
220                         ((GroupTO) realmMember).setName(evaluated);
221                     } else if (realmMember instanceof GroupCR) {
222                         ((GroupCR) realmMember).setName(evaluated);
223                     }
224                 }
225             }
226 
227             Optional.ofNullable(((GroupTO) template).getUserOwner()).map(userDAO::find).ifPresent(userOwner -> {
228                 if (realmMember instanceof GroupTO) {
229                     ((GroupTO) realmMember).setUserOwner(userOwner.getKey());
230                 } else if (realmMember instanceof GroupCR) {
231                     ((GroupCR) realmMember).setUserOwner(userOwner.getKey());
232                 }
233             });
234             Optional.ofNullable(((GroupTO) template).getGroupOwner()).map(groupDAO::find).ifPresent(groupOwner -> {
235                 if (realmMember instanceof GroupTO) {
236                     ((GroupTO) realmMember).setGroupOwner(groupOwner.getKey());
237                 } else if (realmMember instanceof GroupCR) {
238                     ((GroupCR) realmMember).setGroupOwner(groupOwner.getKey());
239                 }
240             });
241 
242             Optional.ofNullable(((GroupTO) template).getUDynMembershipCond()).ifPresent(udynMembershipCond -> {
243                 if (realmMember instanceof GroupTO) {
244                     ((GroupTO) realmMember).setUDynMembershipCond(udynMembershipCond);
245                 } else if (realmMember instanceof GroupCR) {
246                     ((GroupCR) realmMember).setUDynMembershipCond(udynMembershipCond);
247                 }
248             });
249 
250             ((GroupTO) template).getADynMembershipConds().forEach((anyType, cond) -> {
251                 if (realmMember instanceof GroupTO
252                         && !((GroupTO) realmMember).getADynMembershipConds().containsKey(anyType)) {
253 
254                     ((GroupTO) realmMember).getADynMembershipConds().put(anyType, cond);
255                 } else if (realmMember instanceof GroupCR
256                         && !((GroupCR) realmMember).getADynMembershipConds().containsKey(anyType)) {
257 
258                     ((GroupCR) realmMember).getADynMembershipConds().put(anyType, cond);
259                 }
260             });
261 
262             ((GroupTO) template).getTypeExtensions().forEach(typeExt -> {
263                 if (realmMember instanceof GroupTO
264                         && !((GroupTO) realmMember).getTypeExtensions().contains(typeExt)) {
265 
266                     ((GroupTO) realmMember).getTypeExtensions().add(typeExt);
267                 } else if (realmMember instanceof GroupCR
268                         && !((GroupCR) realmMember).getTypeExtensions().contains(typeExt)) {
269 
270                     ((GroupCR) realmMember).getTypeExtensions().add(typeExt);
271                 }
272             });
273         }
274     }
275 
276     public static void check(final Map<String, AnyTO> templates, final ClientExceptionType clientExceptionType) {
277         SyncopeClientException sce = SyncopeClientException.build(clientExceptionType);
278 
279         templates.values().forEach(value -> {
280             value.getPlainAttrs().stream().
281                     filter(attrTO -> !attrTO.getValues().isEmpty()
282                     && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))).
283                     forEachOrdered(attrTO -> sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0)));
284 
285             value.getVirAttrs().stream().
286                     filter(attrTO -> !attrTO.getValues().isEmpty()
287                     && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))).
288                     forEachOrdered((attrTO) -> sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0)));
289 
290             if (value instanceof UserTO) {
291                 UserTO template = (UserTO) value;
292                 if (StringUtils.isNotBlank(template.getUsername())
293                         && !JexlUtils.isExpressionValid(template.getUsername())) {
294 
295                     sce.getElements().add("Invalid JEXL: " + template.getUsername());
296                 }
297                 if (StringUtils.isNotBlank(template.getPassword())
298                         && !JexlUtils.isExpressionValid(template.getPassword())) {
299 
300                     sce.getElements().add("Invalid JEXL: " + template.getPassword());
301                 }
302             } else if (value instanceof GroupTO) {
303                 GroupTO template = (GroupTO) value;
304                 if (StringUtils.isNotBlank(template.getName())
305                         && !JexlUtils.isExpressionValid(template.getName())) {
306 
307                     sce.getElements().add("Invalid JEXL: " + template.getName());
308                 }
309             }
310         });
311 
312         if (!sce.isEmpty()) {
313             throw sce;
314         }
315     }
316 }