Clover icon

sunshower-core

  1. Project Clover database Fri Apr 6 2018 03:27:42 UTC
  2. Package io.sunshower.service.security

File UUIDJdbcMutableAclService.java

 

Coverage histogram

../../../../img/srcFileCovDistChart0.png
60% of files have more coverage

Code metrics

30
113
29
1
488
282
45
0.4
3.9
29
1.55

Classes

Class Line # Actions
UUIDJdbcMutableAclService 48 113 45
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10    * Unless required by applicable law or agreed to in writing, software
11    * distributed under the License is distributed on an "AS IS" BASIS,
12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    * See the License for the specific language governing permissions and
14    * limitations under the License.
15    */
16    package io.sunshower.service.security;
17   
18    import java.sql.PreparedStatement;
19    import java.sql.SQLException;
20    import java.util.List;
21    import java.util.UUID;
22    import javax.sql.DataSource;
23    import org.springframework.dao.DataAccessException;
24    import org.springframework.jdbc.core.BatchPreparedStatementSetter;
25    import org.springframework.security.acls.domain.AccessControlEntryImpl;
26    import org.springframework.security.acls.domain.GrantedAuthoritySid;
27    import org.springframework.security.acls.domain.ObjectIdentityImpl;
28    import org.springframework.security.acls.domain.PrincipalSid;
29    import org.springframework.security.acls.jdbc.JdbcAclService;
30    import org.springframework.security.acls.jdbc.LookupStrategy;
31    import org.springframework.security.acls.model.AccessControlEntry;
32    import org.springframework.security.acls.model.Acl;
33    import org.springframework.security.acls.model.AclCache;
34    import org.springframework.security.acls.model.AlreadyExistsException;
35    import org.springframework.security.acls.model.ChildrenExistException;
36    import org.springframework.security.acls.model.MutableAcl;
37    import org.springframework.security.acls.model.MutableAclService;
38    import org.springframework.security.acls.model.NotFoundException;
39    import org.springframework.security.acls.model.ObjectIdentity;
40    import org.springframework.security.acls.model.Sid;
41    import org.springframework.security.core.Authentication;
42    import org.springframework.security.core.context.SecurityContextHolder;
43    import org.springframework.transaction.annotation.Transactional;
44    import org.springframework.transaction.support.TransactionSynchronizationManager;
45    import org.springframework.util.Assert;
46   
47    @Transactional
 
48    public class UUIDJdbcMutableAclService extends JdbcAclService implements MutableAclService {
49    // ~ Instance fields
50    // ================================================================================================
51   
52    private boolean foreignKeysInDatabase = true;
53    private final AclCache aclCache;
54    private String deleteEntryByObjectIdentityForeignKey =
55    "DELETE FROM acl_entry WHERE acl_object_identity=?";
56    private String deleteObjectIdentityByPrimaryKey = "DELETE FROM acl_object_identity WHERE id=?";
57    // private String classIdentityQuery = "call identity()";
58    // private String sidIdentityQuery = "call identity()";
59    private String insertClass = "INSERT INTO acl_class (id, class) VALUES (?, ?)";
60    private String insertEntry =
61    "INSERT INTO acl_entry "
62    + "(id, acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
63    + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
64    private String insertObjectIdentity =
65    "INSERT INTO acl_object_identity "
66    + "(id, object_id_class, object_id_identity, owner_sid, entries_inheriting) "
67    + "VALUES (?, ?, ?, ?, ?)";
68    private String insertSid = "INSERT INTO acl_sid (id, principal, sid) VALUES (?, ?, ?)";
69    private String selectClassPrimaryKey = "SELECT id FROM acl_class WHERE class=?";
70    private String selectObjectIdentityPrimaryKey =
71    "SELECT acl_object_identity.id FROM acl_object_identity, acl_class "
72    + "WHERE acl_object_identity.object_id_class = acl_class.id AND acl_class.class=? "
73    + "AND acl_object_identity.object_id_identity = ?";
74    private String selectSidPrimaryKey = "SELECT id FROM acl_sid WHERE principal=? AND sid=?";
75    private String updateObjectIdentity =
76    "UPDATE acl_object_identity SET "
77    + "parent_object = ?, owner_sid = ?, entries_inheriting = ?"
78    + " WHERE id = ?";
79   
80    // ~ Constructors
81    // ===================================================================================================
82   
 
83  0 toggle public UUIDJdbcMutableAclService(
84    DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
85  0 super(dataSource, lookupStrategy);
86  0 Assert.notNull(aclCache, "AclCache required");
87  0 this.aclCache = aclCache;
88    }
89   
90    // ~ Methods
91    // ========================================================================================================
92   
 
93  0 toggle public MutableAcl createAcl(ObjectIdentity objectIdentity) throws AlreadyExistsException {
94  0 Assert.notNull(objectIdentity, "Object Identity required");
95   
96    // Check this object identity hasn't already been persisted
97  0 if (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) {
98  0 throw new AlreadyExistsException("Object identity '" + objectIdentity + "' already exists");
99    }
100   
101    // Need to retrieve the current principal, in order to know who "owns" this ACL
102    // (can be changed later on)
103  0 Authentication auth = SecurityContextHolder.getContext().getAuthentication();
104  0 PrincipalSid sid = new PrincipalSid(auth);
105   
106    // Create the acl_object_identity row
107  0 createObjectIdentity(objectIdentity, sid);
108   
109    // Retrieve the ACL via superclass (ensures cache registration, proper retrieval
110    // etc)
111  0 Acl acl = readAclById(objectIdentity);
112  0 Assert.isInstanceOf(MutableAcl.class, acl, "MutableAcl should be been returned");
113   
114  0 return (MutableAcl) acl;
115    }
116   
117    /**
118    * Creates a new row in acl_entry for every ACE defined in the passed MutableAcl object.
119    *
120    * @param acl containing the ACEs to insert
121    */
 
122  0 toggle protected void createEntries(final MutableAcl acl) {
123  0 if (acl.getEntries().isEmpty()) {
124  0 return;
125    }
126  0 jdbcTemplate.batchUpdate(
127    insertEntry,
128    new BatchPreparedStatementSetter() {
 
129  0 toggle public int getBatchSize() {
130  0 return acl.getEntries().size();
131    }
132   
 
133  0 toggle public void setValues(PreparedStatement stmt, int i) throws SQLException {
134  0 AccessControlEntry entry_ = acl.getEntries().get(i);
135  0 Assert.isTrue(entry_ instanceof AccessControlEntryImpl, "Unknown ACE class");
136  0 AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
137   
138  0 stmt.setObject(1, UUID.randomUUID());
139  0 stmt.setObject(2, acl.getId());
140  0 stmt.setInt(3, i);
141  0 stmt.setObject(4, createOrRetrieveSidPrimaryKey(entry.getSid(), true));
142  0 stmt.setInt(5, entry.getPermission().getMask());
143  0 stmt.setBoolean(6, entry.isGranting());
144  0 stmt.setBoolean(7, entry.isAuditSuccess());
145  0 stmt.setBoolean(8, entry.isAuditFailure());
146    }
147    });
148    }
149   
150    /**
151    * Creates an entry in the acl_object_identity table for the passed ObjectIdentity. The Sid is
152    * also necessary, as acl_object_identity has defined the sid column as non-null.
153    *
154    * @param object to represent an acl_object_identity for
155    * @param owner for the SID column (will be created if there is no acl_sid entry for this
156    * particular Sid already)
157    */
 
158  0 toggle @Transactional
159    protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
160  0 UUID sidId = createOrRetrieveSidPrimaryKey(owner, true);
161  0 UUID classId = createOrRetrieveClassPrimaryKey(object.getType(), true);
162  0 jdbcTemplate.update(
163    insertObjectIdentity,
164    object.getIdentifier(),
165    classId,
166    object.getIdentifier(),
167    sidId,
168    Boolean.TRUE);
169    }
170   
171    /**
172    * Retrieves the primary key from {@code acl_class}, creating a new row if needed and the {@code
173    * allowCreate} property is {@code true}.
174    *
175    * @param type to find or create an entry for (often the fully-qualified class name)
176    * @param allowCreate true if creation is permitted if not found
177    * @return the primary key or null if not found
178    */
 
179  0 toggle protected UUID createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) {
180  0 List<UUID> classIds =
181    jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {type}, UUID.class);
182   
183  0 if (!classIds.isEmpty()) {
184  0 return classIds.get(0);
185    }
186   
187  0 if (allowCreate) {
188  0 final UUID id = UUID.randomUUID();
189  0 jdbcTemplate.update(insertClass, id, type);
190  0 Assert.isTrue(
191    TransactionSynchronizationManager.isSynchronizationActive(),
192    "Transaction must be running");
193  0 return id;
194    // return jdbcTemplate.queryForObject(classIdentityQuery, UUID.class);
195    }
196   
197  0 return null;
198    }
199   
200    /**
201    * Retrieves the primary key from acl_sid, creating a new row if needed and the allowCreate
202    * property is true.
203    *
204    * @param sid to find or create
205    * @param allowCreate true if creation is permitted if not found
206    * @return the primary key or null if not found
207    * @throws IllegalArgumentException if the <tt>Sid</tt> is not a recognized implementation.
208    */
 
209  0 toggle protected UUID createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
210  0 Assert.notNull(sid, "Sid required");
211   
212  0 String sidName;
213  0 boolean sidIsPrincipal = true;
214   
215  0 if (sid instanceof PrincipalSid) {
216  0 sidName = ((PrincipalSid) sid).getPrincipal();
217  0 } else if (sid instanceof GrantedAuthoritySid) {
218  0 sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
219  0 sidIsPrincipal = false;
220    } else {
221  0 throw new IllegalArgumentException("Unsupported implementation of Sid");
222    }
223   
224  0 return createOrRetrieveSidPrimaryKey(sidName, sidIsPrincipal, allowCreate);
225    }
226   
227    /**
228    * Retrieves the primary key from acl_sid, creating a new row if needed and the allowCreate
229    * property is true.
230    *
231    * @param sidName name of Sid to find or to create
232    * @param sidIsPrincipal whether it's a user or granted authority like role
233    * @param allowCreate true if creation is permitted if not found
234    * @return the primary key or null if not found
235    */
 
236  0 toggle protected UUID createOrRetrieveSidPrimaryKey(
237    String sidName, boolean sidIsPrincipal, boolean allowCreate) {
238   
239  0 List<UUID> sidIds =
240    jdbcTemplate.queryForList(
241    selectSidPrimaryKey,
242    new Object[] {Boolean.valueOf(sidIsPrincipal), sidName},
243    UUID.class);
244   
245  0 if (!sidIds.isEmpty()) {
246  0 return sidIds.get(0);
247    }
248   
249  0 if (allowCreate) {
250  0 final UUID id = UUID.randomUUID();
251  0 jdbcTemplate.update(insertSid, id, Boolean.valueOf(sidIsPrincipal), sidName);
252  0 Assert.isTrue(
253    TransactionSynchronizationManager.isSynchronizationActive(),
254    "Transaction must be running");
255  0 return id;
256    // return jdbcTemplate.queryForObject(sidIdentityQuery, UUID.class);
257    }
258   
259  0 return null;
260    }
261   
 
262  0 toggle public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
263    throws ChildrenExistException {
264  0 Assert.notNull(objectIdentity, "Object Identity required");
265  0 Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier");
266   
267  0 if (deleteChildren) {
268  0 List<ObjectIdentity> children = findChildren(objectIdentity);
269  0 if (children != null) {
270  0 for (ObjectIdentity child : children) {
271  0 deleteAcl(child, true);
272    }
273    }
274    } else {
275  0 if (!foreignKeysInDatabase) {
276    // We need to perform a manual verification for what a FK would normally
277    // do
278    // We generally don't do this, in the interests of deadlock management
279  0 List<ObjectIdentity> children = findChildren(objectIdentity);
280  0 if (children != null) {
281  0 throw new ChildrenExistException(
282    "Cannot delete '" + objectIdentity + "' (has " + children.size() + " children)");
283    }
284    }
285    }
286   
287  0 UUID oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
288   
289    // Delete this ACL's ACEs in the acl_entry table
290  0 deleteEntries(oidPrimaryKey);
291   
292    // Delete this ACL's acl_object_identity row
293  0 deleteObjectIdentity(oidPrimaryKey);
294   
295    // Clear the cache
296  0 aclCache.evictFromCache(objectIdentity);
297    }
298   
299    /**
300    * Deletes all ACEs defined in the acl_entry table belonging to the presented ObjectIdentity
301    * primary key.
302    *
303    * @param oidPrimaryKey the rows in acl_entry to delete
304    */
 
305  0 toggle protected void deleteEntries(UUID oidPrimaryKey) {
306  0 jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey, oidPrimaryKey);
307    }
308   
309    /**
310    * Deletes a single row from acl_object_identity that is associated with the presented
311    * ObjectIdentity primary key.
312    *
313    * <p>We do not delete any entries from acl_class, even if no classes are using that class any
314    * longer. This is a deadlock avoidance approach.
315    *
316    * @param oidPrimaryKey to delete the acl_object_identity
317    */
 
318  0 toggle protected void deleteObjectIdentity(UUID oidPrimaryKey) {
319    // Delete the acl_object_identity row
320  0 jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, oidPrimaryKey);
321    }
322   
323    /**
324    * Retrieves the primary key from the acl_object_identity table for the passed ObjectIdentity.
325    * Unlike some other methods in this implementation, this method will NOT create a row (use {@link
326    * #createObjectIdentity(ObjectIdentity, Sid)} instead).
327    *
328    * @param oid to find
329    * @return the object identity or null if not found
330    */
 
331  0 toggle protected UUID retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
332   
333  0 try {
334  0 return jdbcTemplate.queryForObject(
335    selectObjectIdentityPrimaryKey, UUID.class, oid.getType(), oid.getIdentifier());
336    } catch (DataAccessException notFound) {
337  0 return null;
338    }
339    }
340   
341    /**
342    * This implementation will simply delete all ACEs in the database and recreate them on each
343    * invocation of this method. A more comprehensive implementation might use dirty state checking,
344    * or more likely use ORM capabilities for create, update and delete operations of {@link
345    * MutableAcl}.
346    */
 
347  0 toggle public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {
348  0 Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
349   
350    // Delete this ACL's ACEs in the acl_entry table
351  0 deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
352   
353    // Create this ACL's ACEs in the acl_entry table
354  0 createEntries(acl);
355   
356    // Change the mutable columns in acl_object_identity
357  0 updateObjectIdentity(acl);
358   
359    // Clear the cache, including children
360  0 clearCacheIncludingChildren(acl.getObjectIdentity());
361   
362    // Retrieve the ACL via superclass (ensures cache registration, proper retrieval
363    // etc)
364  0 return (MutableAcl) super.readAclById(acl.getObjectIdentity());
365    }
366   
 
367  0 toggle private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
368  0 Assert.notNull(objectIdentity, "ObjectIdentity required");
369  0 List<ObjectIdentity> children = findChildren(objectIdentity);
370  0 if (children != null) {
371  0 for (ObjectIdentity child : children) {
372  0 clearCacheIncludingChildren(child);
373    }
374    }
375  0 aclCache.evictFromCache(objectIdentity);
376    }
377   
378    /**
379    * Updates an existing acl_object_identity row, with new information presented in the passed
380    * MutableAcl object. Also will create an acl_sid entry if needed for the Sid that owns the
381    * MutableAcl.
382    *
383    * @param acl to modify (a row must already exist in acl_object_identity)
384    * @throws NotFoundException if the ACL could not be found to update.
385    */
 
386  0 toggle protected void updateObjectIdentity(MutableAcl acl) {
387  0 UUID parentId = null;
388   
389  0 if (acl.getParentAcl() != null) {
390  0 Assert.isInstanceOf(
391    ObjectIdentityImpl.class,
392    acl.getParentAcl().getObjectIdentity(),
393    "Implementation only supports ObjectIdentityImpl");
394   
395  0 ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity();
396  0 parentId = retrieveObjectIdentityPrimaryKey(oii);
397    }
398   
399  0 Assert.notNull(acl.getOwner(), "Owner is required in this implementation");
400   
401  0 UUID ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
402  0 int count =
403    jdbcTemplate.update(
404    updateObjectIdentity,
405    parentId,
406    ownerSid,
407    Boolean.valueOf(acl.isEntriesInheriting()),
408    acl.getId());
409   
410  0 if (count != 1) {
411  0 throw new NotFoundException("Unable to locate ACL to update");
412    }
413    }
414   
415    /**
416    * Sets the query that will be used to retrieve the identity of a newly created row in the
417    * <tt>acl_class</tt> table.
418    *
419    * @param classIdentityQuery the query, which should return the identifier. Defaults to <tt>call
420    * identity()</tt>
421    */
 
422  0 toggle public void setClassIdentityQuery(String classIdentityQuery) {
423  0 Assert.hasText(classIdentityQuery, "New classIdentityQuery query is required");
424    // this.classIdentityQuery = classIdentityQuery;
425    }
426   
427    /**
428    * Sets the query that will be used to retrieve the identity of a newly created row in the
429    * <tt>acl_sid</tt> table.
430    *
431    * @param sidIdentityQuery the query, which should return the identifier. Defaults to <tt>call
432    * identity()</tt>
433    */
 
434  0 toggle public void setSidIdentityQuery(String sidIdentityQuery) {
435  0 Assert.hasText(sidIdentityQuery, "New sidIdentityQuery query is required");
436    // this.sidIdentityQuery = sidIdentityQuery;
437    }
438   
 
439  0 toggle public void setDeleteEntryByObjectIdentityForeignKeySql(
440    String deleteEntryByObjectIdentityForeignKey) {
441  0 this.deleteEntryByObjectIdentityForeignKey = deleteEntryByObjectIdentityForeignKey;
442    }
443   
 
444  0 toggle public void setDeleteObjectIdentityByPrimaryKeySql(String deleteObjectIdentityByPrimaryKey) {
445  0 this.deleteObjectIdentityByPrimaryKey = deleteObjectIdentityByPrimaryKey;
446    }
447   
 
448  0 toggle public void setInsertClassSql(String insertClass) {
449  0 this.insertClass = insertClass;
450    }
451   
 
452  0 toggle public void setInsertEntrySql(String insertEntry) {
453  0 this.insertEntry = insertEntry;
454    }
455   
 
456  0 toggle public void setInsertObjectIdentitySql(String insertObjectIdentity) {
457  0 this.insertObjectIdentity = insertObjectIdentity;
458    }
459   
 
460  0 toggle public void setInsertSidSql(String insertSid) {
461  0 this.insertSid = insertSid;
462    }
463   
 
464  0 toggle public void setClassPrimaryKeyQuery(String selectClassPrimaryKey) {
465  0 this.selectClassPrimaryKey = selectClassPrimaryKey;
466    }
467   
 
468  0 toggle public void setObjectIdentityPrimaryKeyQuery(String selectObjectIdentityPrimaryKey) {
469  0 this.selectObjectIdentityPrimaryKey = selectObjectIdentityPrimaryKey;
470    }
471   
 
472  0 toggle public void setSidPrimaryKeyQuery(String selectSidPrimaryKey) {
473  0 this.selectSidPrimaryKey = selectSidPrimaryKey;
474    }
475   
 
476  0 toggle public void setUpdateObjectIdentity(String updateObjectIdentity) {
477  0 this.updateObjectIdentity = updateObjectIdentity;
478    }
479   
480    /**
481    * @param foreignKeysInDatabase if false this class will perform additional FK constrain checking,
482    * which may cause deadlocks (the default is true, so deadlocks are avoided but the database
483    * is expected to enforce FKs)
484    */
 
485  0 toggle public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
486  0 this.foreignKeysInDatabase = foreignKeysInDatabase;
487    }
488    }