1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.springframework.security.acls.jdbc;
16
17 import org.springframework.security.Authentication;
18
19 import org.springframework.security.acls.AccessControlEntry;
20 import org.springframework.security.acls.Acl;
21 import org.springframework.security.acls.AlreadyExistsException;
22 import org.springframework.security.acls.ChildrenExistException;
23 import org.springframework.security.acls.MutableAcl;
24 import org.springframework.security.acls.MutableAclService;
25 import org.springframework.security.acls.NotFoundException;
26 import org.springframework.security.acls.domain.AccessControlEntryImpl;
27 import org.springframework.security.acls.objectidentity.ObjectIdentity;
28 import org.springframework.security.acls.objectidentity.ObjectIdentityImpl;
29 import org.springframework.security.acls.sid.GrantedAuthoritySid;
30 import org.springframework.security.acls.sid.PrincipalSid;
31 import org.springframework.security.acls.sid.Sid;
32
33 import org.springframework.security.context.SecurityContextHolder;
34
35 import org.springframework.dao.DataAccessException;
36
37 import org.springframework.jdbc.core.BatchPreparedStatementSetter;
38
39 import org.springframework.transaction.support.TransactionSynchronizationManager;
40
41 import org.springframework.util.Assert;
42
43 import java.lang.reflect.Array;
44
45 import java.sql.PreparedStatement;
46 import java.sql.SQLException;
47
48 import java.util.List;
49
50 import javax.sql.DataSource;
51
52
53
54
55
56
57
58
59
60 public class JdbcMutableAclService extends JdbcAclService implements MutableAclService {
61
62
63 private boolean foreignKeysInDatabase = true;
64 private AclCache aclCache;
65 private String deleteEntryByObjectIdentityForeignKey = "delete from acl_entry where acl_object_identity=?";
66 private String deleteObjectIdentityByPrimaryKey = "delete from acl_object_identity where id=?";
67 private String classIdentityQuery = "call identity()";
68 private String sidIdentityQuery = "call identity()";
69 private String insertClass = "insert into acl_class (class) values (?)";
70 private String insertEntry = "insert into acl_entry "
71 + "(acl_object_identity, ace_order, sid, mask, granting, audit_success, audit_failure)"
72 + "values (?, ?, ?, ?, ?, ?, ?)";
73 private String insertObjectIdentity = "insert into acl_object_identity "
74 + "(object_id_class, object_id_identity, owner_sid, entries_inheriting) " + "values (?, ?, ?, ?)";
75 private String insertSid = "insert into acl_sid (principal, sid) values (?, ?)";
76 private String selectClassPrimaryKey = "select id from acl_class where class=?";
77 private String selectObjectIdentityPrimaryKey = "select acl_object_identity.id from acl_object_identity, acl_class "
78 + "where acl_object_identity.object_id_class = acl_class.id and acl_class.class=? "
79 + "and acl_object_identity.object_id_identity = ?";
80 private String selectSidPrimaryKey = "select id from acl_sid where principal=? and sid=?";
81 private String updateObjectIdentity = "update acl_object_identity set "
82 + "parent_object = ?, owner_sid = ?, entries_inheriting = ?" + " where id = ?";
83
84
85
86 public JdbcMutableAclService(DataSource dataSource, LookupStrategy lookupStrategy, AclCache aclCache) {
87 super(dataSource, lookupStrategy);
88 Assert.notNull(aclCache, "AclCache required");
89 this.aclCache = aclCache;
90 }
91
92
93
94 public MutableAcl createAcl(ObjectIdentity objectIdentity)
95 throws AlreadyExistsException {
96 Assert.notNull(objectIdentity, "Object Identity required");
97
98
99 if (retrieveObjectIdentityPrimaryKey(objectIdentity) != null) {
100 throw new AlreadyExistsException("Object identity '" + objectIdentity + "' already exists");
101 }
102
103
104 Authentication auth = SecurityContextHolder.getContext().getAuthentication();
105 PrincipalSid sid = new PrincipalSid(auth);
106
107
108 createObjectIdentity(objectIdentity, sid);
109
110
111 Acl acl = readAclById(objectIdentity);
112 Assert.isInstanceOf(MutableAcl.class, acl, "MutableAcl should be been returned");
113
114 return (MutableAcl) acl;
115 }
116
117
118
119
120
121
122 protected void createEntries(final MutableAcl acl) {
123 jdbcTemplate.batchUpdate(insertEntry,
124 new BatchPreparedStatementSetter() {
125 public int getBatchSize() {
126 return acl.getEntries().length;
127 }
128
129 public void setValues(PreparedStatement stmt, int i)
130 throws SQLException {
131 AccessControlEntry entry_ = (AccessControlEntry) Array.get(acl.getEntries(), i);
132 Assert.isTrue(entry_ instanceof AccessControlEntryImpl, "Unknown ACE class");
133
134 AccessControlEntryImpl entry = (AccessControlEntryImpl) entry_;
135
136 stmt.setLong(1, ((Long) acl.getId()).longValue());
137 stmt.setInt(2, i);
138 stmt.setLong(3, createOrRetrieveSidPrimaryKey(entry.getSid(), true).longValue());
139 stmt.setInt(4, entry.getPermission().getMask());
140 stmt.setBoolean(5, entry.isGranting());
141 stmt.setBoolean(6, entry.isAuditSuccess());
142 stmt.setBoolean(7, entry.isAuditFailure());
143 }
144 });
145 }
146
147
148
149
150
151
152
153
154 protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
155 Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
156 Long classId = createOrRetrieveClassPrimaryKey(object.getJavaType(), true);
157 jdbcTemplate.update(insertObjectIdentity,
158 new Object[] {classId, object.getIdentifier().toString(), sidId, new Boolean(true)});
159 }
160
161
162
163
164
165
166
167
168
169
170 protected Long createOrRetrieveClassPrimaryKey(Class clazz, boolean allowCreate) {
171 List classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {clazz.getName()}, Long.class);
172 Long classId = null;
173
174 if (classIds.isEmpty()) {
175 if (allowCreate) {
176 classId = null;
177 jdbcTemplate.update(insertClass, new Object[] {clazz.getName()});
178 Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
179 "Transaction must be running");
180 classId = new Long(jdbcTemplate.queryForLong(classIdentityQuery));
181 }
182 } else {
183 classId = (Long) classIds.iterator().next();
184 }
185
186 return classId;
187 }
188
189
190
191
192
193
194
195
196
197
198
199
200 protected Long createOrRetrieveSidPrimaryKey(Sid sid, boolean allowCreate) {
201 Assert.notNull(sid, "Sid required");
202
203 String sidName = null;
204 boolean principal = true;
205
206 if (sid instanceof PrincipalSid) {
207 sidName = ((PrincipalSid) sid).getPrincipal();
208 } else if (sid instanceof GrantedAuthoritySid) {
209 sidName = ((GrantedAuthoritySid) sid).getGrantedAuthority();
210 principal = false;
211 } else {
212 throw new IllegalArgumentException("Unsupported implementation of Sid");
213 }
214
215 List sidIds = jdbcTemplate.queryForList(selectSidPrimaryKey, new Object[] {new Boolean(principal), sidName},
216 Long.class);
217 Long sidId = null;
218
219 if (sidIds.isEmpty()) {
220 if (allowCreate) {
221 sidId = null;
222 jdbcTemplate.update(insertSid, new Object[] {new Boolean(principal), sidName});
223 Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
224 "Transaction must be running");
225 sidId = new Long(jdbcTemplate.queryForLong(sidIdentityQuery));
226 }
227 } else {
228 sidId = (Long) sidIds.iterator().next();
229 }
230
231 return sidId;
232 }
233
234 public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
235 throws ChildrenExistException {
236 Assert.notNull(objectIdentity, "Object Identity required");
237 Assert.notNull(objectIdentity.getIdentifier(), "Object Identity doesn't provide an identifier");
238
239 if (deleteChildren) {
240 ObjectIdentity[] children = findChildren(objectIdentity);
241 if (children != null) {
242 for (int i = 0; i < children.length; i++) {
243 deleteAcl(children[i], true);
244 }
245 }
246 } else {
247 if (!foreignKeysInDatabase) {
248
249
250 ObjectIdentity[] children = findChildren(objectIdentity);
251 if (children != null) {
252 throw new ChildrenExistException("Cannot delete '" + objectIdentity + "' (has " + children.length
253 + " children)");
254 }
255 }
256 }
257
258 Long oidPrimaryKey = retrieveObjectIdentityPrimaryKey(objectIdentity);
259
260
261 deleteEntries(oidPrimaryKey);
262
263
264 deleteObjectIdentity(oidPrimaryKey);
265
266
267 aclCache.evictFromCache(objectIdentity);
268 }
269
270
271
272
273
274
275 protected void deleteEntries(Long oidPrimaryKey) {
276 jdbcTemplate.update(deleteEntryByObjectIdentityForeignKey,
277 new Object[] {oidPrimaryKey});
278 }
279
280
281
282
283
284
285
286
287
288
289
290 protected void deleteObjectIdentity(Long oidPrimaryKey) {
291
292 jdbcTemplate.update(deleteObjectIdentityByPrimaryKey, new Object[] {oidPrimaryKey});
293 }
294
295
296
297
298
299
300
301
302
303
304 protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
305 try {
306 return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey,
307 new Object[] {oid.getJavaType().getName(), oid.getIdentifier()}));
308 } catch (DataAccessException notFound) {
309 return null;
310 }
311 }
312
313
314
315
316
317
318
319
320
321
322
323
324 public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException {
325 Assert.notNull(acl.getId(), "Object Identity doesn't provide an identifier");
326
327
328 deleteEntries(retrieveObjectIdentityPrimaryKey(acl.getObjectIdentity()));
329
330
331 createEntries(acl);
332
333
334 updateObjectIdentity(acl);
335
336
337 clearCacheIncludingChildren(acl.getObjectIdentity());
338
339
340 return (MutableAcl) super.readAclById(acl.getObjectIdentity());
341 }
342
343 private void clearCacheIncludingChildren(ObjectIdentity objectIdentity) {
344 Assert.notNull(objectIdentity, "ObjectIdentity required");
345 ObjectIdentity[] children = findChildren(objectIdentity);
346 if (children != null) {
347 for (int i = 0; i < children.length; i++) {
348 clearCacheIncludingChildren(children[i]);
349 }
350 }
351 aclCache.evictFromCache(objectIdentity);
352 }
353
354
355
356
357
358
359
360
361
362 protected void updateObjectIdentity(MutableAcl acl) {
363 Long parentId = null;
364
365 if (acl.getParentAcl() != null) {
366 Assert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl().getObjectIdentity(),
367 "Implementation only supports ObjectIdentityImpl");
368
369 ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity();
370 parentId = retrieveObjectIdentityPrimaryKey(oii);
371 }
372
373 Assert.notNull(acl.getOwner(), "Owner is required in this implementation");
374
375 Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
376 int count = jdbcTemplate.update(updateObjectIdentity,
377 new Object[] {parentId, ownerSid, new Boolean(acl.isEntriesInheriting()), acl.getId()});
378
379 if (count != 1) {
380 throw new NotFoundException("Unable to locate ACL to update");
381 }
382 }
383
384 public void setClassIdentityQuery(String identityQuery) {
385 Assert.hasText(identityQuery, "New identity query is required");
386 this.classIdentityQuery = identityQuery;
387 }
388
389 public void setSidIdentityQuery(String identityQuery) {
390 Assert.hasText(identityQuery, "New identity query is required");
391 this.sidIdentityQuery = identityQuery;
392 }
393
394
395
396
397 public void setForeignKeysInDatabase(boolean foreignKeysInDatabase) {
398 this.foreignKeysInDatabase = foreignKeysInDatabase;
399 }
400 }