KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > repository > serverimpl > acl > AclEvaluator


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
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 org.outerj.daisy.repository.serverimpl.acl;
17
18 import org.outerj.daisy.repository.RepositoryException;
19 import org.outerj.daisy.repository.Document;
20 import org.outerj.daisy.repository.query.QueryException;
21 import org.outerj.daisy.repository.user.Role;
22 import org.outerj.daisy.repository.acl.*;
23 import org.outerj.daisy.repository.commonimpl.acl.AclObjectImpl;
24 import org.outerj.daisy.repository.commonimpl.acl.AclImpl;
25 import org.outerj.daisy.repository.commonimpl.acl.AclStrategy;
26 import org.outerj.daisy.repository.commonimpl.acl.AclEvaluationContext;
27 import org.outerj.daisy.query.model.Tristate;
28 import org.outerj.daisy.query.model.PredicateExpr;
29
30 import java.util.Iterator JavaDoc;
31
32 /**
33  * Evaluates ACL's. This code has not directly been put into the AclImpl/AclObjectImpl
34  * classes because those would otherwise have been dependent on code only existing
35  * in the server implementation.
36  */

37 public class AclEvaluator {
38     private AclStrategy aclStrategy;
39     private AclImpl.IntimateAccess aclInt;
40     private AclEvaluationContext aclEvaluationContext;
41
42     public AclEvaluator(AclImpl acl, AclStrategy aclStrategy, AclEvaluationContext aclEvaluationContext) {
43         this.aclStrategy = aclStrategy;
44         this.aclInt = acl.getIntimateAccess(aclStrategy);
45         this.aclEvaluationContext = aclEvaluationContext;
46     }
47
48     public boolean hasPotentialWriteAccess(long userId, long[] roleIds, long documentTypeId, long collectionId) throws RepositoryException {
49         if (roleIds.length < 1)
50             throw new RepositoryException("Checking of potential write access requires at least one role.");
51
52         try {
53             if (hasRole(roleIds, Role.ADMINISTRATOR)) {
54                 return true;
55             }
56
57             // the results array will contain the ACL evaluation result for each of the given roles
58
boolean[] results = new boolean[roleIds.length]; // initialized to false
59
Iterator JavaDoc objectsIt = aclInt.getObjects().iterator();
60             while (objectsIt.hasNext()) {
61                 AclObjectImpl object = (AclObjectImpl) objectsIt.next();
62                 // Note: later rules overwrite earlier ones
63
checkPotentialWriteAccess(object, results, userId, roleIds, documentTypeId, collectionId);
64             }
65
66             // if the result for at least one role is true, then return true
67
for (int i = 0; i < results.length; i++)
68                 if (results[i])
69                     return true;
70             return false;
71         } catch (Throwable JavaDoc e) {
72             throw new RepositoryException("Error evaluating ACL.", e);
73         }
74     }
75
76     private void checkPotentialWriteAccess(AclObjectImpl aclObject, boolean[] results, long userId, long[] roleIds, long documentTypeId, long collectionId) throws RepositoryException {
77         AclObjectImpl.IntimateAccess aclObjectInt = aclObject.getIntimateAccess(aclStrategy);
78         assureExpressionCompiled(aclObject, aclObjectInt);
79         Tristate appliesTo;
80         try {
81             PredicateExpr predicateExpr = (PredicateExpr)aclObjectInt.getCompiledExpression();
82             appliesTo = predicateExpr.appliesTo(new DummyDocForAppliesToTest(documentTypeId, collectionId));
83         } catch (QueryException e) {
84             throw new RepositoryException("Exception evaluating ACL object expression.", e);
85         }
86
87         if (appliesTo == Tristate.NO)
88             return;
89
90         // NOTE: the logic below is similar to that in completeAclInfo(), so if you adjust
91
// it here, keep it in sync over there
92
Iterator JavaDoc entryIt = aclObjectInt.getEntries().iterator();
93         while (entryIt.hasNext()) {
94             AclEntry entry = (AclEntry)entryIt.next();
95
96             for (int r = 0; r < roleIds.length; r++) {
97                 boolean relevant = false;
98                 if (entry.getSubjectType() == AclSubjectType.EVERYONE) {
99                     relevant = true;
100                 } else if (entry.getSubjectType() == AclSubjectType.ROLE) {
101                     if (roleIds[r] == entry.getSubjectValue())
102                         relevant = true;
103                 } else if (entry.getSubjectType() == AclSubjectType.USER) {
104                     if (userId != -1 && userId == entry.getSubjectValue())
105                         relevant = true;
106                 }
107
108                 if (relevant) {
109                     AclActionType entryAction = entry.get(AclPermission.WRITE);
110                     // granting applies always, denying only if the object expression surely applies (and not 'maybe')
111
if (entryAction == AclActionType.GRANT) {
112                         results[r] = true;
113                     } else if (entryAction == AclActionType.DENY && appliesTo == Tristate.YES) {
114                         results[r] = false;
115                     }
116                 }
117             }
118         }
119     }
120
121     public AclResultInfo getAclInfo(long userId, long[] roleIds, Document document) throws RepositoryException {
122         if (roleIds.length < 1)
123             throw new RepositoryException("Evaluation of ACL requires at least one role");
124
125         try {
126             AclResultInfo result = new AclResultInfo(userId, roleIds, document.getId(), document.getBranchId(), document.getLanguageId());
127
128             if (hasRole(roleIds, Role.ADMINISTRATOR)) {
129                 final String JavaDoc message = "granted because role is Administrator (role id " + Role.ADMINISTRATOR + ")";
130                 for (int i = 0; i < AclPermission.ENUM.length; i++)
131                     result.set(AclPermission.ENUM[i], AclActionType.GRANT, message, message);
132                 return result;
133             }
134
135             if ((document.isPrivate() && userId == -1) || (document.isPrivate() && userId != -1 && document.getOwner() != userId)) {
136                 final String JavaDoc message = "denied because document is marked as private";
137                 for (int i = 0; i < AclPermission.ENUM.length; i++)
138                     result.set(AclPermission.ENUM[i], AclActionType.DENY, message, message);
139                 return result;
140             }
141
142             for (int i = 0; i < AclPermission.ENUM.length; i++)
143                 result.set(AclPermission.ENUM[i], AclActionType.DENY, "denied by default", "denied by default");
144
145             // For the actual ACL evaluation itself, we evaluate it for each role individually and
146
// afterwards merge the results to take the most permissive ones (i.e. if at least one
147
// role allows it, it is allowed, or vice versa, a permission is only denied if all
148
// roles deny it)
149
AclResultInfo[] results = new AclResultInfo[roleIds.length];
150             for (int i = 0; i < results.length; i++)
151                 results[i] = (AclResultInfo)result.clone();
152
153             Iterator JavaDoc objectsIt = aclInt.getObjects().iterator();
154             while (objectsIt.hasNext()) {
155                 AclObjectImpl object = (AclObjectImpl) objectsIt.next();
156                 completeAclInfo(object, results, userId, roleIds, document);
157             }
158
159             result = merge(results);
160
161             if (!result.isAllowed(AclPermission.READ_LIVE)) {
162                 if (result.isAllowed(AclPermission.READ)) {
163                     final String JavaDoc message = "cannot have read access if no 'read live' access";
164                     result.set(AclPermission.READ, AclActionType.DENY, message, message);
165                 }
166             }
167
168             if (!result.isAllowed(AclPermission.READ) && result.isAllowed(AclPermission.READ_LIVE) && document.isRetired()) {
169                 final String JavaDoc message = "cannot read a retired document if only 'read live' access";
170                 result.set(AclPermission.READ_LIVE, AclActionType.DENY, message, message);
171             }
172
173             if (!result.isAllowed(AclPermission.READ_LIVE) || !result.isAllowed(AclPermission.READ)) {
174                 if (result.isAllowed(AclPermission.WRITE)) {
175                     final String JavaDoc message = "cannot have write access if no read access";
176                     result.set(AclPermission.WRITE, AclActionType.DENY, message, message);
177                 }
178                 if (result.isAllowed(AclPermission.PUBLISH)) {
179                     final String JavaDoc message = "cannot have publish access if no read access";
180                     result.set(AclPermission.PUBLISH, AclActionType.DENY, message, message);
181                 }
182                 if (result.isAllowed(AclPermission.DELETE)) {
183                     final String JavaDoc message = "cannot have delete access if no write access";
184                     result.set(AclPermission.DELETE, AclActionType.DENY, message, message);
185                 }
186             }
187
188             // Note: the document.getId() == -1 check below is because:
189
// - new documents have id -1
190
// - for a new document, one is always the owner
191
// - so one would always be allowed write access for new documents
192
if (document.getId() != -1 && userId != -1 && document.getOwner() == userId) {
193                 final String JavaDoc message = "granted because user is owner of the document";
194                 result.set(AclPermission.READ_LIVE, AclActionType.GRANT, message, message);
195                 result.set(AclPermission.READ, AclActionType.GRANT, message, message);
196                 result.set(AclPermission.WRITE, AclActionType.GRANT, message, message);
197                 result.set(AclPermission.DELETE, AclActionType.GRANT, message, message);
198                 // Note: an owner of a document does not automatically get publish rights, instead
199
// these are determined by the ACL
200
}
201
202             return result;
203         } catch (Throwable JavaDoc e) {
204             throw new RepositoryException("Error evaluating ACL.", e);
205         }
206     }
207
208     private void assureExpressionCompiled(AclObject aclObject, AclObjectImpl.IntimateAccess aclObjectInt) throws RepositoryException {
209         if (aclObjectInt.getCompiledExpression() == null) {
210             Object JavaDoc compiledExpr = aclEvaluationContext.compileObjectExpression(aclObject.getObjectExpr());
211             aclObjectInt.setCompiledExpression(compiledExpr);
212         }
213     }
214
215     private boolean appliesTo(AclObjectImpl aclObject, AclObjectImpl.IntimateAccess aclObjectInt, Document document) throws RepositoryException {
216         assureExpressionCompiled(aclObject, aclObjectInt);
217         return aclEvaluationContext.checkObjectExpression(aclObjectInt.getCompiledExpression(), document);
218     }
219
220     private void completeAclInfo(AclObjectImpl aclObject, AclResultInfo[] results, long userId, long[] roleIds, Document document) throws RepositoryException {
221         AclObjectImpl.IntimateAccess aclObjectInt = aclObject.getIntimateAccess(aclStrategy);
222         // NOTE: the logic below is similar to that in hasPotentialWriteAccess(), so if you adjust
223
// it here, keep it in sync over there
224
if (appliesTo(aclObject, aclObjectInt, document)) {
225             Iterator JavaDoc entryIt = aclObjectInt.getEntries().iterator();
226             while (entryIt.hasNext()) {
227                 AclEntry entry = (AclEntry)entryIt.next();
228
229                 for (int r = 0; r < roleIds.length; r++) {
230                     String JavaDoc subjectReason = null;
231                     if (entry.getSubjectType() == AclSubjectType.EVERYONE) {
232                         subjectReason = "everyone";
233                     } else if (entry.getSubjectType() == AclSubjectType.ROLE) {
234                         if (roleIds[r] == entry.getSubjectValue())
235                             subjectReason = "role is " + entry.getSubjectValue();
236                     } else if (entry.getSubjectType() == AclSubjectType.USER) {
237                         if (userId != -1 && userId == entry.getSubjectValue())
238                             subjectReason = "user is " + userId;
239                     }
240
241                     if (subjectReason != null) {
242                         for (int i = 0; i < AclPermission.ENUM.length; i++) {
243                             AclActionType entryAction = entry.get(AclPermission.ENUM[i]);
244                             if (entryAction != AclActionType.DO_NOTHING) {
245                                 results[r].set(AclPermission.ENUM[i], entryAction, aclObject.getObjectExpr(), subjectReason);
246                             }
247                         }
248                     }
249                 }
250             }
251         }
252     }
253
254     private boolean hasRole(long[] availableRoles, long roleId) {
255         for (int i = 0; i < availableRoles.length; i++)
256             if (availableRoles[i] == roleId)
257                 return true;
258         return false;
259     }
260
261     /**
262      * Merge the given AclResultInfo's so that the most permissive result is obtained.
263      * @param results array with at least one entry
264      */

265     private AclResultInfo merge(AclResultInfo[] results) {
266         if (results.length == 1)
267             return results[0];
268
269         AclResultInfo result = results[0];
270
271         for (int i = 0; i < AclPermission.ENUM.length; i++) {
272             AclPermission permission = AclPermission.ENUM[i];
273             if (!result.isAllowed(permission)) {
274                 for (int k = 1; k < results.length; k++) {
275                     if (results[k].isAllowed(permission)) {
276                         result.set(permission, AclActionType.GRANT, results[k].getObjectExpr(permission), results[k].getSubjectReason(permission));
277                         break;
278                     }
279                 }
280             }
281         }
282
283         return result;
284     }
285
286 }
287
Popular Tags