001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.geronimo.j2ee.deployment.annotation;
017:
018: import java.util.List;
019:
020: import javax.annotation.security.DeclareRoles;
021: import javax.annotation.security.RunAs;
022: import javax.servlet.Servlet;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.commons.logging.LogFactory;
026: import org.apache.geronimo.common.DeploymentException;
027: import org.apache.geronimo.xbeans.javaee.RoleNameType;
028: import org.apache.geronimo.xbeans.javaee.RunAsType;
029: import org.apache.geronimo.xbeans.javaee.SecurityRoleType;
030: import org.apache.geronimo.xbeans.javaee.ServletType;
031: import org.apache.geronimo.xbeans.javaee.ServletNameType;
032: import org.apache.geronimo.xbeans.javaee.FullyQualifiedClassType;
033: import org.apache.geronimo.xbeans.javaee.WebAppType;
034: import org.apache.xbean.finder.ClassFinder;
035:
036: /**
037: * Static helper class used to encapsulate all the functions related to the translation of
038: * <strong>@DeclareRoles</strong> and <strong>@RunAs</strong> annotations to deployment
039: * descriptor tags. The SecurityAnnotationHelper class can be used as part of the deployment of a
040: * module into the Geronimo server. It performs the following major functions:
041: *
042: * <ol>
043: * <li>Translates annotations into corresponding deployment descriptor elements (so that the
044: * actual deployment descriptor in the module can be updated or even created if necessary)
045: * </ol>
046: *
047: * <p><strong>Note(s):</strong>
048: * <ul>
049: * <li>Supports only servlets
050: * <li>The user is responsible for invoking change to metadata-complete
051: * <li>This helper class will validate any changes it makes to the deployment descriptor. An
052: * exception will be thrown if it fails to parse
053: * </ul>
054: *
055: * @version $Rev $Date
056: * @since 04-2007
057: */
058: public final class SecurityAnnotationHelper extends AnnotationHelper {
059:
060: // Private instance variables
061: private static final Log log = LogFactory
062: .getLog(SecurityAnnotationHelper.class);
063:
064: // Private constructor to prevent instantiation
065: private SecurityAnnotationHelper() {
066: }
067:
068: /**
069: * Update the deployment descriptor from the DeclareRoles and RunAs annotations
070: *
071: * @param webApp Access to the spec dd
072: * @param classFinder Access to the classes of interest
073: * @throws DeploymentException if parsing or validation error
074: */
075: public static void processAnnotations(WebAppType webApp,
076: ClassFinder classFinder) throws DeploymentException {
077: if (webApp != null && classFinder != null) {
078: if (classFinder.isAnnotationPresent(DeclareRoles.class)) {
079: processDeclareRoles(webApp, classFinder);
080: }
081: if (classFinder.isAnnotationPresent(RunAs.class)) {
082: processRunAs(webApp, classFinder);
083: }
084: }
085: }
086:
087: /**
088: * Process @DeclareRole annotations (for servlets only)
089: *
090: * @param webApp Access to the spec dd
091: * @param classFinder Access to the classes of interest
092: * @throws DeploymentException if parsing or validation error
093: */
094: private static void processDeclareRoles(WebAppType webApp,
095: ClassFinder classFinder) throws DeploymentException {
096: log.debug("processDeclareRoles(): Entry: webApp: "
097: + webApp.toString());
098:
099: List<Class> classesWithDeclareRoles = classFinder
100: .findAnnotatedClasses(DeclareRoles.class);
101:
102: // Class-level annotation
103: for (Class cls : classesWithDeclareRoles) {
104: DeclareRoles declareRoles = (DeclareRoles) cls
105: .getAnnotation(DeclareRoles.class);
106: if (declareRoles != null
107: && Servlet.class.isAssignableFrom(cls)) {
108: addDeclareRoles(webApp, declareRoles, cls);
109: }
110: }
111:
112: // Validate deployment descriptor to ensure it's still okay
113: validateDD(new AnnotatedWebApp(webApp));
114:
115: log.debug("processDeclareRoles(): Exit: webApp: "
116: + webApp.toString());
117: }
118:
119: /**
120: * Process @RunAs annotations (for servlets only)
121: *
122: * @param webApp Access to the spec dd
123: * @param classFinder Access to the classes of interest
124: * @throws DeploymentException if parsing or validation error
125: */
126: private static void processRunAs(WebAppType webApp,
127: ClassFinder classFinder) throws DeploymentException {
128: log
129: .debug("processRunAs(): Entry: webApp: "
130: + webApp.toString());
131:
132: List<Class> classesWithRunAs = classFinder
133: .findAnnotatedClasses(RunAs.class);
134:
135: // Class-level annotation
136: for (Class cls : classesWithRunAs) {
137: RunAs runAs = (RunAs) cls.getAnnotation(RunAs.class);
138: if (runAs != null && Servlet.class.isAssignableFrom(cls)) {
139: addRunAs(webApp, runAs, cls);
140: }
141: }
142:
143: // Validate deployment descriptor to ensure it's still okay
144: validateDD(new AnnotatedWebApp(webApp));
145:
146: log.debug("processRunAs(): Exit: webApp: " + webApp.toString());
147: }
148:
149: /**
150: * Add @DeclareRoles annotations to the deployment descriptor. XMLBeans are used to read and
151: * manipulate the deployment descriptor as necessary. The DeclareRoles annotation(s) will be
152: * converted to one of the following deployment descriptors:
153: *
154: * <ol>
155: * <li><security-role> -- Describes a single security role
156: * </ol>
157: *
158: * <p><strong>Note(s):</strong>
159: * <ul>
160: * <li>The deployment descriptor is the authoritative source so this method ensures that
161: * existing elements in it are not overwritten by annoations
162: * </ul>
163: *
164: * @param webApp Access to the spec dd
165: * @param annotation @DeclareRoles annotation
166: * @param cls Class name with the @DeclareRoles annoation
167: */
168: private static void addDeclareRoles(WebAppType webApp,
169: DeclareRoles annotation, Class cls) {
170: log.debug("addDeclareRoles( [webApp] " + webApp.toString()
171: + "," + '\n' + "[annotation] " + annotation.toString()
172: + "," + '\n' + "[cls] "
173: + (cls != null ? cls.getName() : null) + "): Entry");
174:
175: // Get all the <security-role> tags from the deployment descriptor
176: SecurityRoleType[] securityRoles = webApp
177: .getSecurityRoleArray();
178:
179: String[] annotationRoleNames = annotation.value();
180: for (String annotationRoleName : annotationRoleNames) {
181: if (!annotationRoleName.equals("")) {
182: boolean exists = false;
183: for (SecurityRoleType securityRole : securityRoles) {
184: if (securityRole.getRoleName().getStringValue()
185: .trim().equals(annotationRoleName)) {
186: exists = true;
187: break;
188: }
189: }
190: if (exists) {
191: log
192: .debug("addDeclareRoles: <security-role> entry found: "
193: + annotationRoleName);
194: } else {
195: log
196: .debug("addDeclareRoles: <security-role> entry NOT found: "
197: + annotationRoleName);
198: SecurityRoleType securityRole = webApp
199: .addNewSecurityRole();
200: RoleNameType roleName = securityRole
201: .addNewRoleName();
202: roleName.setStringValue(annotationRoleName);
203: }
204: }
205: }
206:
207: log.debug("addDeclareRoles(): Exit");
208: }
209:
210: /**
211: * Add @RunAs annotations to the deployment descriptor. XMLBeans are used to read and manipulate
212: * the deployment descriptor as necessary. The DeclareRoles annotation(s) will be converted to
213: * one of the following deployment descriptors:
214: *
215: * <ol>
216: * <li><run-as> -- Describes a run-as security identity to be used for the execution of a
217: * component
218: * </ol>
219: *
220: * <p><strong>Note(s):</strong>
221: * <ul>
222: * <li>The deployment descriptor is the authoritative source so this method ensures that
223: * existing elements in it are not overwritten by annoations
224: * </ul>
225: *
226: * @param webApp Access to the spec dd
227: * @param annotation @RunAs annotation
228: * @param cls Class name with the @RunAs annoation
229: */
230: private static void addRunAs(WebAppType webApp, RunAs annotation,
231: Class cls) {
232: log.debug("addRunAs( [webApp] " + webApp.toString() + ","
233: + '\n' + "[annotation] " + annotation.toString() + ","
234: + '\n' + "[cls] "
235: + (cls != null ? cls.getName() : null) + "): Entry");
236:
237: String annotationRunAs = annotation.value();
238: if (!annotationRunAs.equals("")) {
239: ServletType[] servlets = webApp.getServletArray();
240: boolean exists = false;
241: for (ServletType servlet : servlets) {
242: if (servlet.getServletClass().getStringValue().trim()
243: .equals(cls.getName())) {
244: if (!servlet.isSetRunAs()) {
245: RunAsType runAsType = servlet.addNewRunAs();
246: RoleNameType roleName = runAsType
247: .addNewRoleName();
248: roleName.setStringValue(annotationRunAs);
249: }
250: exists = true;
251: break;
252: }
253: }
254: if (!exists) {
255: log.warn("RunAs servlet not found in webApp: "
256: + cls.getName());
257: }
258: }
259:
260: log.debug("addRunAs(): Exit");
261: }
262:
263: }
|