new utility for updated cached values in a multi-threaded environment.
[utils] / security / impl / src / main / java / org / wamblee / security / authorization / UrlAuthorizationRule.java
1 /*
2  * Copyright 2005-2010 the original author or authors.
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.wamblee.security.authorization;
17
18 import static org.wamblee.security.authorization.AuthorizationResult.*;
19
20 import javax.enterprise.inject.Typed;
21 import javax.persistence.Access;
22 import javax.persistence.AccessType;
23 import javax.persistence.CascadeType;
24 import javax.persistence.Column;
25 import javax.persistence.DiscriminatorValue;
26 import javax.persistence.Entity;
27 import javax.persistence.JoinColumn;
28 import javax.persistence.ManyToOne;
29 import javax.persistence.OneToOne;
30 import javax.persistence.Transient;
31
32 import org.apache.log4j.Logger;
33 import org.wamblee.security.authentication.User;
34 import org.wamblee.security.authentication.UserAdministration;
35
36 /**
37  * Utility base class for implementation of authentication rules based on the
38  * <ul>
39  * <li>The path of the resource. To obtain the path of a resource, subclasses
40  * must implement {@link #getResourcePath(Object)}. Whether a path is
41  * appropriate is determined by a
42  * {@link org.wamblee.security.authorization.AbstractPathCondition}.</li>
43  * <li>The user identity with which the resource is accessed. Whether a user is
44  * appropriate is determined by a
45  * {@link org.wamblee.security.authorization.AbstractUserCondition}.</li>
46  * <li>The operation that is requested. Whether the operation is appropriate is
47  * determined by a {@link org.wamblee.security.authorization.AbstractOperationCondition}
48  * .</li>
49  * </ul>
50  * In case all three conditions match, the condition returns the configured
51  * result passed at construction (GRANTED or DENIED). If the resource is not of
52  * the specified type, the result is UNSUPPORTED_RESOURCE, otherwise, the result
53  * is UNDECIDED.
54  */
55 @Entity
56 @Access(AccessType.PROPERTY)
57 public abstract class UrlAuthorizationRule extends AbstractAuthorizationRule {
58     private static final Logger LOGGER = Logger
59         .getLogger(UrlAuthorizationRule.class);
60
61     /**
62      * Result that the rule will return in case there is a match.
63      */
64     private AuthorizationResult result;
65
66     /**
67      * A condition which specifies which users the rule is for.
68      */
69     private UserCondition userCondition;
70
71     /**
72      * Path the rule applies for.
73      */
74     private PathCondition pathCondition;
75
76     /**
77      * Resource class that the rule applies for.
78      */
79     private Class resourceClass;
80
81     /**
82      * Operation that this rule is for.
83      */
84     
85     private OperationCondition operationCondition;
86
87     /**
88      * Constructs an authorization rule. IF the group and path match, then the
89      * provided result will be returned.
90      * 
91      * @param aResult
92      *            Result of the authorization when the path and group match.
93      * @param aUserCondition
94      *            Condition to match users.
95      * @param aPathCondition
96      *            Condition to match paths with.
97      * @param aResourceClass
98      *            Supported resource class this is for.
99      * @param aOperationCondition
100      *            Condition to match the operation with.
101      */
102     protected UrlAuthorizationRule(AuthorizationResult aResult,
103         UserCondition aUserCondition, PathCondition aPathCondition,
104         Class aResourceClass, OperationCondition aOperationCondition) {
105         if (!aResult.equals(GRANTED) && !aResult.equals(DENIED)) {
106             throw new IllegalArgumentException(
107                 "Only GRANTED or DENIED may be used: " + aResult);
108         }
109
110         result = aResult;
111         userCondition = aUserCondition;
112         pathCondition = aPathCondition;
113         resourceClass = aResourceClass;
114         operationCondition = aOperationCondition;
115     }
116
117     /**
118      * For OR mapping.
119      * 
120      */
121     protected UrlAuthorizationRule(Class aResourceClass) {
122         result = null;
123         userCondition = null;
124         pathCondition = null;
125         resourceClass = aResourceClass;
126         operationCondition = null;
127     }
128
129     /**
130      * For OR mapping.
131      * 
132      */
133     protected UrlAuthorizationRule() {
134         result = null;
135         userCondition = null;
136         pathCondition = null;
137         resourceClass = null;
138         operationCondition = null;
139     }
140
141     /*
142      * (non-Javadoc)
143      * 
144      * @see
145      * org.wamblee.security.authorization.AuthorizationRule#getSupportedTypes()
146      */
147     @Transient
148     public Class[] getSupportedTypes() {
149         return new Class[] { resourceClass };
150     }
151
152     /*
153      * (non-Javadoc)
154      * 
155      * @see
156      * org.wamblee.security.authorization.AuthorizationRule#isAllowed(java.lang
157      * .Object, org.wamblee.security.authorization.Operation)
158      */
159     public AuthorizationResult isAllowed(Object aResource,
160         Operation aOperation, String aUser) {
161         if (!resourceClass.isInstance(aResource)) {
162             return UNSUPPORTED_RESOURCE;
163         }
164
165         String path = getResourcePath(aResource);
166
167         return isAllowedWithPath(path, aOperation, aUser);
168     }
169
170     /**
171      * Determines if the operation is allowed on the resource.
172      * 
173      * @param aPath
174      *            Path of the resource.
175      * @param aOperation
176      *            Operation to be done.
177      * @param aUser
178      *            Currently logged in user or null if no user is logged in.
179      * 
180      * @return Authorization result,
181      */
182     protected AuthorizationResult isAllowedWithPath(String aPath, Operation aOperation,
183         String aUser) {
184         if (!pathCondition.matches(aPath)) {
185             return UNDECIDED;
186         }
187
188         if (!operationCondition.matches(aOperation)) {
189             return UNDECIDED;
190         }
191
192         if (!userCondition.matches(aUser)) {
193             return UNDECIDED;
194         }
195
196         return result;
197     }
198
199     /**
200      * Gets the path of the resource.
201      * 
202      * @param aResource
203      *            Resource, guaranteed to be an instance of
204      *            {@link #resourceClass}.
205      * 
206      * @return Path of the resource.
207      */
208     protected abstract String getResourcePath(Object aResource);
209
210     /*
211      * (non-Javadoc)
212      * 
213      * @see java.lang.Object#toString()
214      */
215     @Override
216     public String toString() {
217         return "UrlAUthorizationRule(result = " + result +
218             ", pathCondition = " + pathCondition + ", userCondition = " +
219             userCondition + ", resourceClass = " + resourceClass + ")";
220     }
221
222     /**
223      * Gets the authorization result for OR mapping.
224      * 
225      * @return Result.
226      */
227     @Column(name = "AUTH_RESULT", nullable = false)
228     protected String getAuthorizationResultString() {
229         if (result == null) {
230             return null;
231         }
232
233         return result.toString();
234     }
235
236     /**
237      * Sets the authorization result, for OR mapping.
238      * 
239      * @param aResult
240      *            Result.
241      */
242     protected void setAuthorizationResultString(String aResult) {
243         result = AuthorizationResult.valueOf(aResult);
244     }
245
246     @Column(name = "RES_CLASSNAME", nullable = false)
247     protected String getResourceClassName() {
248         if (resourceClass == null) {
249             return "";
250         }
251
252         return resourceClass.getName();
253     }
254
255     protected void setResourceClassName(String aResourceClass) {
256         try {
257             resourceClass = Class.forName(aResourceClass);
258         } catch (ClassNotFoundException e) {
259             LOGGER.error("Cannot find resource class '" + aResourceClass + "'",
260                 e);
261             throw new IllegalArgumentException(e.getMessage(), e);
262         }
263     }
264
265     /**
266      * 
267      * @return Returns the operationCondition.
268      */
269     @OneToOne(cascade = CascadeType.ALL, targetEntity = AbstractOperationCondition.class, orphanRemoval = true)
270     @JoinColumn(name = "OPER_COND_PK")
271     public OperationCondition getOperationCondition() {
272         return operationCondition;
273     }
274
275     /**
276      * 
277      * @param aOperationCondition
278      *            The operationCondition to set.
279      */
280     protected void setOperationCondition(OperationCondition aOperationCondition) {
281         operationCondition = aOperationCondition;
282     }
283
284     /**
285      * 
286      * @return Returns the pathCondition.
287      */
288     @OneToOne(cascade = CascadeType.ALL, targetEntity = AbstractPathCondition.class, orphanRemoval = true)
289     @JoinColumn(name = "PATH_COND_PK")
290     public PathCondition getPathCondition() {
291         return pathCondition;
292     }
293
294     /**
295      * 
296      * @param aPathCondition
297      *            The pathCondition to set.
298      */
299     protected void setPathCondition(PathCondition aPathCondition) {
300         pathCondition = aPathCondition;
301     }
302
303     /**
304      * 
305      * @return Returns the userCondition.
306      */
307     @OneToOne(cascade = CascadeType.ALL, targetEntity = AbstractUserCondition.class, orphanRemoval = true)
308     @JoinColumn(name = "USER_COND_PK")
309     public UserCondition getUserCondition() {
310         return userCondition;
311     }
312
313     /**
314      * 
315      * @param aUserCondition
316      *            The userCondition to set.
317      */
318     protected void setUserCondition(UserCondition aUserCondition) {
319         userCondition = aUserCondition;
320     }
321     
322     @Override
323     public void setUserAdministration(UserAdministration aAdmin) {
324         userCondition.setUserAdmin(aAdmin);   
325     }
326
327 }