001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 2004 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * Initial developer(s): Florent BENOIT
022: * --------------------------------------------------------------------------
023: * $Id: SecurityConstraintListDesc.java 4854 2004-06-01 11:24:01Z benoitf $
024: * --------------------------------------------------------------------------
025: */package org.objectweb.jonas_web.deployment.api;
026:
027: import java.security.Permission;
028: import java.security.PermissionCollection;
029: import java.security.Permissions;
030: import java.util.ArrayList;
031: import java.util.Collections;
032: import java.util.Enumeration;
033: import java.util.HashMap;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Map;
037:
038: import org.objectweb.util.monolog.api.BasicLevel;
039: import org.objectweb.util.monolog.api.Logger;
040:
041: import org.objectweb.jonas_lib.deployment.xml.SecurityRole;
042:
043: import org.objectweb.jonas_web.deployment.xml.AuthConstraint;
044: import org.objectweb.jonas_web.deployment.xml.SecurityConstraint;
045: import org.objectweb.jonas_web.deployment.xml.UserDataConstraint;
046: import org.objectweb.jonas_web.deployment.xml.WebApp;
047: import org.objectweb.jonas_web.deployment.xml.WebResourceCollection;
048:
049: import org.objectweb.common.TraceCore;
050:
051: /**
052: * This class is used to manage security constraint in web applications
053: * useful for JACC implementation as it returns set of JACC permissions
054: * @author Florent Benoit
055: */
056: public class SecurityConstraintListDesc {
057:
058: /**
059: * Default pattern
060: */
061: private static final String DEFAULT_PATTERN = "/";
062:
063: /**
064: * SecurityConstraint root element
065: */
066: private WebApp webApp = null;
067:
068: /**
069: * Map between pattern and its PatternEntry object
070: */
071: private Map mapPatterns = null;
072:
073: /**
074: * Excluded permissions
075: */
076: private PermissionCollection excludedPermissions = null;
077:
078: /**
079: * Unchecked permissions
080: */
081: private PermissionCollection uncheckedPermissions = null;
082:
083: /**
084: * Permissions by role
085: */
086: private Map permissionsByRole = null;
087:
088: /**
089: * Logger
090: */
091: private static Logger logger = null;
092:
093: /**
094: * Constructor
095: * @param webApp root element of security constraints
096: */
097: public SecurityConstraintListDesc(WebApp webApp) {
098: this .webApp = webApp;
099:
100: // init logger
101: logger = TraceCore.jacc;
102:
103: // Init patterns Map
104: mapPatterns = new HashMap();
105:
106: // Init permissions
107: excludedPermissions = new Permissions();
108: uncheckedPermissions = new Permissions();
109: permissionsByRole = new HashMap();
110: try {
111: // Transform DD to constraints
112: initConstraints();
113:
114: // Qualify the patterns
115: qualifyPatterns();
116:
117: // Build JACC permissions
118: buildPermissions();
119: } catch (Exception e) {
120: e.printStackTrace();
121: }
122: }
123:
124: /**
125: * Initialize the constraints
126: * @see JACC 3.1.3.1 for the rules
127: * Excluding auth-constraint = auth-constraint naming no roles
128: */
129: private void initConstraints() {
130:
131: // All defined security-role
132: // Used if role-name = "*" in auth-constraint
133: String[] securityRoles = new String[webApp
134: .getSecurityRoleList().size()];
135: int r = 0;
136: for (Iterator itSecurityRoles = webApp.getSecurityRoleList()
137: .iterator(); itSecurityRoles.hasNext(); r++) {
138: securityRoles[r] = ((SecurityRole) itSecurityRoles.next())
139: .getRoleName();
140: }
141:
142: SecurityConstraint securityConstraint = null;
143: for (Iterator it = webApp.getSecurityConstraintList()
144: .iterator(); it.hasNext();) {
145:
146: // Retrieve Security Constraint object if any
147: securityConstraint = (SecurityConstraint) it.next();
148:
149: // Get the resource collection list
150: List webResourceCollectionList = securityConstraint
151: .getWebResourceCollectionList();
152:
153: // Auth Constraint where role are defined
154: AuthConstraint authConstraint = securityConstraint
155: .getAuthConstraint();
156:
157: // User data constraint where role are defined
158: UserDataConstraint userDataConstraint = securityConstraint
159: .getUserDataConstraint();
160:
161: // Get roles if any
162: List rolesList = null;
163: boolean hasAuthConstraint = false;
164: boolean isExcludingAuthConstraint = false;
165: if (authConstraint != null) {
166: rolesList = authConstraint.getRoleNameList();
167: // Excluding if no roles
168: hasAuthConstraint = true;
169: isExcludingAuthConstraint = (rolesList.size() == 0);
170: }
171:
172: // Transport Guarantee
173: String transportGuarantee = null;
174: if (userDataConstraint != null) {
175: transportGuarantee = userDataConstraint
176: .getTransportGuarantee();
177: }
178:
179: // Now, build the structure of patterns
180: WebResourceCollection webRC = null;
181:
182: // For each web ressource collection
183: for (Iterator itWebRC = webResourceCollectionList
184: .iterator(); itWebRC.hasNext();) {
185: webRC = (WebResourceCollection) itWebRC.next();
186:
187: // Get the http-method
188: List methodList = webRC.getHttpMethodList();
189:
190: // For each pattern, add the http-method and set the transport guarantee
191: // If it is not an Excluding Auth constraint, add these values to each role
192:
193: // Get all the patterns and build objects
194: String urlPatternString = null;
195: for (Iterator itPattern = webRC.getUrlPatternList()
196: .iterator(); itPattern.hasNext();) {
197: urlPatternString = (String) itPattern.next();
198:
199: // Get existing if one ?
200: PatternEntry patternEntry = (PatternEntry) mapPatterns
201: .get(urlPatternString);
202: if (patternEntry == null) {
203: patternEntry = new PatternEntry(
204: urlPatternString);
205: mapPatterns.put(urlPatternString, patternEntry);
206: }
207: // Add for all or for all specified roles
208: String[] methods = null;
209: if (methodList.isEmpty()) {
210: // All the methods are applied
211: methods = MethodsDesc.METHODS;
212: } else {
213: methods = (String[]) methodList
214: .toArray(new String[methodList.size()]);
215: }
216: if (hasAuthConstraint) {
217: // Excluded or role based
218: if (isExcludingAuthConstraint) {
219: patternEntry.addExcludedMethods(methods,
220: transportGuarantee);
221: } else {
222: // role based
223: for (Iterator itRole = rolesList.iterator(); itRole
224: .hasNext();) {
225: String roleName = (String) itRole
226: .next();
227:
228: // Add methods to a specific or all existing roles
229: if (roleName.equals("*")) {
230: patternEntry.addMethodsOnRoles(
231: methods, securityRoles,
232: transportGuarantee);
233: } else {
234: patternEntry.addMethodsOnRole(
235: methods, roleName,
236: transportGuarantee);
237: }
238: }
239: }
240: } else {
241: // No auth Constraint --> unchecked
242: patternEntry.addUncheckedMethods(methods,
243: transportGuarantee);
244: }
245: }
246: }
247: }
248: }
249:
250: /**
251: * Qualify patterns
252: * @see JACC 3.1.3.1 subsection Qualified URL Pattern Names
253: */
254: private synchronized void qualifyPatterns() {
255:
256: // Add default pattern
257: PatternEntry defaultPatternEntry = (PatternEntry) mapPatterns
258: .get(DEFAULT_PATTERN);
259: if (defaultPatternEntry == null) {
260: defaultPatternEntry = new PatternEntry(DEFAULT_PATTERN);
261: // Last entry to unchecked
262: defaultPatternEntry.setUncheckedLastEntry();
263: mapPatterns.put(DEFAULT_PATTERN, defaultPatternEntry);
264: }
265:
266: // For each pattern, qualify this pattern by all patterns
267: PatternEntry patternEntry = null;
268: Pattern otherPattern = null;
269: String patternString = null;
270:
271: // Build list of patterns object
272: List patterns = new ArrayList();
273: for (Iterator it = mapPatterns.keySet().iterator(); it
274: .hasNext();) {
275: patternString = (String) it.next();
276: patterns.add(new Pattern(patternString));
277: }
278:
279: // Sort elements
280: Collections.sort(patterns);
281:
282: Pattern pattern = null;
283: for (Iterator it = mapPatterns.keySet().iterator(); it
284: .hasNext();) {
285: patternString = (String) it.next();
286: pattern = new Pattern(patternString);
287: patternEntry = (PatternEntry) mapPatterns
288: .get(patternString);
289:
290: // Loop on all patterns
291: for (Iterator itOther = patterns.iterator(); itOther
292: .hasNext();) {
293: otherPattern = (Pattern) itOther.next();
294:
295: if (pattern.isPathPrefix()
296: && pattern.isMatching(otherPattern)) {
297: /* first case (path prefix)
298: If the pattern is a path prefix pattern, it must be
299: qualified by every path-prefix pattern in the
300: deployment descriptor matched by and different from
301: the pattern being qualified. The pattern must also be
302: qualified by every exact pattern appearing in the
303: deployment descriptor that is matched by the pattern being
304: qualified.
305: */
306: if (otherPattern.isPathPrefix()
307: && !pattern.equals(otherPattern)) {
308: patternEntry.addQualifiedPattern(otherPattern);
309: } else if (otherPattern.isExactPattern()) {
310: patternEntry.addQualifiedPattern(otherPattern);
311: }
312: } else if (pattern.isExtensionPattern()) {
313: // Case two : Extension pattern
314: /*
315: If the pattern is an extension pattern, it must be
316: qualified by every path-prefix pattern appearing in
317: the deployment descriptor and every exact pattern in
318: the deployment descriptor that is matched by the
319: pattern being qualified.
320: */
321: if (otherPattern.isPathPrefix()
322: || (pattern.isMatching(otherPattern) && otherPattern
323: .isExactPattern())) {
324: patternEntry.addQualifiedPattern(otherPattern);
325: }
326: } else if (pattern.isDefaultPattern()) {
327: // Case three : Default pattern
328: /*
329: If the pattern is the default pattern, "/", it must
330: be qualified by every other pattern except the default
331: pattern appearing in the deployment descriptor.
332: */
333: if (!otherPattern.isDefaultPattern()) {
334: patternEntry.addQualifiedPattern(otherPattern);
335: }
336: /*
337: } else if (pattern.isExactPattern()) {
338: // case 4 : Exact Pattern
339: // Nothing : must not contain any qualifying pattern
340: If the pattern is an exact pattern,
341: its qualified form must not contain any qualifying
342: patterns.
343: */
344: }
345: }
346: }
347: }
348:
349: /**
350: * Build permissions
351: * @see JACC 3.1.3.1
352: */
353: private void buildPermissions() {
354:
355: PatternEntry patternEntry = null;
356: // For each pattern, build permissions (Exclude default pattern for now)
357: for (Iterator it = mapPatterns.values().iterator(); it
358: .hasNext();) {
359: patternEntry = (PatternEntry) it.next();
360: // No permissions if the pattern is irrelevant
361: if (!patternEntry.isIrrelevant()) {
362: if (patternEntry.isUncheckedLastEntry()) {
363: addUncheckedPermissions(patternEntry
364: .getUncheckedPermissions());
365: } else {
366: addExcludedPermissions(patternEntry
367: .getExcludedPermissions());
368: addUncheckedPermissions(patternEntry
369: .getUncheckedPermissions());
370: addRolePermissions(patternEntry
371: .getRolesPermissionsMap());
372: }
373: }
374: }
375: if (logger.isLoggable(BasicLevel.DEBUG)) {
376: logger.log(BasicLevel.DEBUG, "Excluded permissions = "
377: + excludedPermissions);
378: logger.log(BasicLevel.DEBUG, "Unchecked permissions = "
379: + uncheckedPermissions);
380: logger.log(BasicLevel.DEBUG, "Roles Permissions = ");
381:
382: String roleName = null;
383: for (Iterator it = permissionsByRole.keySet().iterator(); it
384: .hasNext();) {
385: roleName = (String) it.next();
386: logger.log(BasicLevel.DEBUG, "Permissions for role "
387: + roleName + " are "
388: + permissionsByRole.get(roleName));
389: }
390: }
391:
392: }
393:
394: /**
395: * Add Excluded permissions
396: * @param permissions permissions to add. if permissions are null,
397: * no permissions are added.
398: */
399: private void addExcludedPermissions(PermissionCollection permissions) {
400: if (permissions == null) {
401: return;
402: }
403:
404: for (Enumeration e = permissions.elements(); e
405: .hasMoreElements();) {
406: excludedPermissions.add((Permission) e.nextElement());
407: }
408: }
409:
410: /**
411: * Add Unchecked permissions
412: * @param permissions permissions to add. if permissions are null,
413: * no permissions are added.
414: */
415: private void addUncheckedPermissions(
416: PermissionCollection permissions) {
417: if (permissions == null) {
418: return;
419: }
420:
421: for (Enumeration e = permissions.elements(); e
422: .hasMoreElements();) {
423: uncheckedPermissions.add((Permission) e.nextElement());
424: }
425: }
426:
427: /**
428: * Add permissions on the roles
429: * @param rolePermissionsMap permissions to add.(Map between role and Permissions)
430: */
431: private void addRolePermissions(Map rolePermissionsMap) {
432: if (rolePermissionsMap == null) {
433: return;
434: }
435:
436: // For each role, build permissions on actions found on the role.
437: String roleName = null;
438: PermissionCollection permissions = null;
439: PermissionCollection existingRolePermissions = null;
440: for (Iterator it = rolePermissionsMap.keySet().iterator(); it
441: .hasNext();) {
442: roleName = (String) it.next();
443: permissions = (PermissionCollection) rolePermissionsMap
444: .get(roleName);
445: if (permissions != null) {
446: existingRolePermissions = (PermissionCollection) permissionsByRole
447: .get(roleName);
448: if (existingRolePermissions == null) {
449: existingRolePermissions = new Permissions();
450: permissionsByRole.put(roleName,
451: existingRolePermissions);
452: }
453: for (Enumeration e = permissions.elements(); e
454: .hasMoreElements();) {
455: existingRolePermissions.add((Permission) e
456: .nextElement());
457: }
458: }
459: }
460: }
461:
462: /**
463: * Gets the excluded permissions
464: * @return excluded permissions
465: */
466: public PermissionCollection getExcludedPermissions() {
467: return excludedPermissions;
468: }
469:
470: /**
471: * Gets the unchecked permissions
472: * @return unchecked permissions
473: */
474: public PermissionCollection getUncheckedPermissions() {
475: return uncheckedPermissions;
476: }
477:
478: /**
479: * Gets the permissions by role Map
480: * @return a Map containing permissions by role
481: */
482: public Map getPermissionsByRole() {
483: return permissionsByRole;
484: }
485:
486: }
|