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.persistence.jpa.dao;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import javax.persistence.Query;
24  import javax.persistence.TypedQuery;
25  import org.apache.syncope.common.lib.types.AnyTypeKind;
26  import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO;
27  import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
28  import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
29  import org.apache.syncope.core.persistence.api.dao.RoleDAO;
30  import org.apache.syncope.core.persistence.api.entity.Privilege;
31  import org.apache.syncope.core.persistence.api.entity.Realm;
32  import org.apache.syncope.core.persistence.api.entity.Role;
33  import org.apache.syncope.core.persistence.api.entity.user.User;
34  import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
35  import org.apache.syncope.core.persistence.api.search.SearchCondVisitor;
36  import org.apache.syncope.core.persistence.jpa.entity.JPARole;
37  import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
38  import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent;
39  import org.apache.syncope.core.spring.security.AuthContextUtils;
40  import org.identityconnectors.framework.common.objects.SyncDeltaType;
41  import org.springframework.context.ApplicationEventPublisher;
42  import org.springframework.transaction.annotation.Transactional;
43  
44  public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
45  
46      public static final String DYNMEMB_TABLE = "DynRoleMembers";
47  
48      protected final AnyMatchDAO anyMatchDAO;
49  
50      protected final ApplicationEventPublisher publisher;
51  
52      protected final AnySearchDAO anySearchDAO;
53  
54      protected final DelegationDAO delegationDAO;
55  
56      protected final SearchCondVisitor searchCondVisitor;
57  
58      public JPARoleDAO(
59              final AnyMatchDAO anyMatchDAO,
60              final ApplicationEventPublisher publisher,
61              final AnySearchDAO anySearchDAO,
62              final DelegationDAO delegationDAO,
63              final SearchCondVisitor searchCondVisitor) {
64  
65          this.anyMatchDAO = anyMatchDAO;
66          this.publisher = publisher;
67          this.anySearchDAO = anySearchDAO;
68          this.delegationDAO = delegationDAO;
69          this.searchCondVisitor = searchCondVisitor;
70      }
71  
72      @Override
73      public int count() {
74          Query query = entityManager().createQuery(
75                  "SELECT COUNT(e) FROM  " + JPARole.class.getSimpleName() + " e");
76          return ((Number) query.getSingleResult()).intValue();
77      }
78  
79      @Override
80      public Role find(final String key) {
81          return entityManager().find(JPARole.class, key);
82      }
83  
84      @Override
85      public List<Role> findByRealm(final Realm realm) {
86          TypedQuery<Role> query = entityManager().createQuery(
87                  "SELECT e FROM " + JPARole.class.getSimpleName() + " e WHERE :realm MEMBER OF e.realms", Role.class);
88          query.setParameter("realm", realm);
89          return query.getResultList();
90      }
91  
92      @Override
93      public List<Role> findByPrivilege(final Privilege privilege) {
94          TypedQuery<Role> query = entityManager().createQuery(
95                  "SELECT e FROM " + JPARole.class.getSimpleName() + " e WHERE :privilege MEMBER OF e.privileges",
96                  Role.class);
97          query.setParameter("privilege", privilege);
98          return query.getResultList();
99      }
100 
101     @Override
102     public List<Role> findAll() {
103         TypedQuery<Role> query = entityManager().createQuery(
104                 "SELECT e FROM " + JPARole.class.getSimpleName() + " e ", Role.class);
105         return query.getResultList();
106     }
107 
108     @Override
109     public Role save(final Role role) {
110         ((JPARole) role).list2json();
111         return entityManager().merge(role);
112     }
113 
114     @Override
115     public Role saveAndRefreshDynMemberships(final Role role) {
116         Role merged = save(role);
117 
118         // refresh dynamic memberships
119         clearDynMembers(merged);
120         if (merged.getDynMembership() != null) {
121             List<User> matching = anySearchDAO.search(
122                     SearchCondConverter.convert(searchCondVisitor, merged.getDynMembership().getFIQLCond()),
123                     AnyTypeKind.USER);
124 
125             matching.forEach(user -> {
126                 Query insert = entityManager().createNativeQuery("INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
127                 insert.setParameter(1, user.getKey());
128                 insert.setParameter(2, merged.getKey());
129                 insert.executeUpdate();
130 
131                 publisher.publishEvent(
132                         new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain()));
133             });
134         }
135 
136         return merged;
137     }
138 
139     @Override
140     public void delete(final Role role) {
141         TypedQuery<User> query = entityManager().createQuery(
142                 "SELECT e FROM " + JPAUser.class.getSimpleName() + " e WHERE :role MEMBER OF e.roles", User.class);
143         query.setParameter("role", role);
144 
145         query.getResultList().forEach(user -> {
146             user.getRoles().remove(role);
147             publisher.publishEvent(
148                     new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain()));
149         });
150 
151         clearDynMembers(role);
152 
153         delegationDAO.findByRole(role).forEach(delegation -> delegation.getRoles().remove(role));
154 
155         entityManager().remove(role);
156     }
157 
158     @Override
159     public void delete(final String key) {
160         Role role = find(key);
161         if (role == null) {
162             return;
163         }
164 
165         delete(role);
166     }
167 
168     @Override
169     @SuppressWarnings("unchecked")
170     public List<String> findDynMembers(final Role role) {
171         if (role.getDynMembership() == null) {
172             return List.of();
173         }
174 
175         Query query = entityManager().createNativeQuery("SELECT any_id FROM " + DYNMEMB_TABLE + " WHERE role_id=?");
176         query.setParameter(1, role.getKey());
177 
178         List<String> result = new ArrayList<>();
179         query.getResultList().stream().map(key -> key instanceof Object[]
180                 ? (String) ((Object[]) key)[0]
181                 : ((String) key)).
182                 forEach(user -> result.add((String) user));
183         return result;
184     }
185 
186     @Override
187     public void clearDynMembers(final Role role) {
188         Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE role_id=?");
189         delete.setParameter(1, role.getKey());
190         delete.executeUpdate();
191     }
192 
193     @Transactional
194     @Override
195     public void refreshDynMemberships(final User user) {
196         Query query = entityManager().createNativeQuery(
197                 "SELECT role_id FROM " + DYNMEMB_TABLE + " WHERE any_id=?");
198         query.setParameter(1, user.getKey());
199 
200         findAll().stream().filter(role -> role.getDynMembership() != null).forEach(role -> {
201             boolean matches = anyMatchDAO.matches(
202                     user, SearchCondConverter.convert(searchCondVisitor, role.getDynMembership().getFIQLCond()));
203 
204             Query find = entityManager().createNativeQuery(
205                     "SELECT any_id FROM " + DYNMEMB_TABLE + " WHERE role_id=?");
206             find.setParameter(1, role.getKey());
207             boolean existing = !find.getResultList().isEmpty();
208 
209             if (matches && !existing) {
210                 Query insert = entityManager().createNativeQuery(
211                         "INSERT INTO " + DYNMEMB_TABLE + " VALUES(?, ?)");
212                 insert.setParameter(1, user.getKey());
213                 insert.setParameter(2, role.getKey());
214                 insert.executeUpdate();
215             } else if (!matches && existing) {
216                 Query delete = entityManager().createNativeQuery(
217                         "DELETE FROM " + DYNMEMB_TABLE + " WHERE role_id=? AND any_id=?");
218                 delete.setParameter(1, role.getKey());
219                 delete.setParameter(2, user.getKey());
220                 delete.executeUpdate();
221             }
222         });
223     }
224 
225     @Override
226     public void removeDynMemberships(final String key) {
227         Query delete = entityManager().createNativeQuery("DELETE FROM " + DYNMEMB_TABLE + " WHERE any_id=?");
228         delete.setParameter(1, key);
229         delete.executeUpdate();
230     }
231 }