001: /*
002: jGuard is a security framework based on top of jaas (java authentication and authorization security).
003: it is written for web applications, to resolve simply, access control problems.
004: version $Name$
005: http://sourceforge.net/projects/jguard/
006:
007: Copyright (C) 2004 Charles GAY
008:
009: This library is free software; you can redistribute it and/or
010: modify it under the terms of the GNU Lesser General Public
011: License as published by the Free Software Foundation; either
012: version 2.1 of the License, or (at your option) any later version.
013:
014: This library is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018:
019: You should have received a copy of the GNU Lesser General Public
020: License along with this library; if not, write to the Free Software
021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022:
023:
024: jGuard project home page:
025: http://sourceforge.net/projects/jguard/
026:
027: */
028: package net.sf.jguard.ext.authorization.manager;
029:
030: import java.io.FileWriter;
031: import java.io.IOException;
032: import java.io.OutputStream;
033: import java.security.Permission;
034: import java.security.Principal;
035: import java.util.ArrayList;
036: import java.util.Arrays;
037: import java.util.Collection;
038: import java.util.HashSet;
039: import java.util.Iterator;
040: import java.util.List;
041: import java.util.Map;
042: import java.util.Set;
043: import java.util.logging.Level;
044: import java.util.logging.Logger;
045:
046: import net.sf.jguard.core.CoreConstants;
047: import net.sf.jguard.core.authorization.permissions.Domain;
048: import net.sf.jguard.core.authorization.permissions.JGPermissionCollection;
049: import net.sf.jguard.core.authorization.permissions.PermissionUtils;
050: import net.sf.jguard.core.principals.RolePrincipal;
051: import net.sf.jguard.ext.SecurityConstants;
052: import net.sf.jguard.ext.authorization.AuthorizationException;
053: import net.sf.jguard.ext.principals.PrincipalUtils;
054: import net.sf.jguard.ext.util.XMLUtils;
055:
056: import org.dom4j.Attribute;
057: import org.dom4j.Document;
058: import org.dom4j.Element;
059: import org.dom4j.QName;
060: import org.dom4j.io.HTMLWriter;
061: import org.dom4j.io.OutputFormat;
062: import org.dom4j.io.XMLWriter;
063: import org.dom4j.util.UserDataAttribute;
064:
065: /**
066: * AuthorizationManager implementation which enable Permission Management with an XML backend.
067: * @author <a href="mailto:diabolo512@users.sourceforge.net">Charles Gay</a>
068: * @author <a href="mailto:vinipitta@users.sourceforge.net">Vinicius Pitta Lima de Araujo</a>
069: */
070: public class XmlAuthorizationManager extends
071: AbstractAuthorizationManager implements AuthorizationManager {
072: /** Logger for this class */
073: private static final Logger logger = Logger
074: .getLogger(XmlAuthorizationManager.class.getName());
075:
076: private Element root;
077: private Document document = null;
078: private String fileLocation;
079:
080: /**
081: * constructor.
082: */
083: public XmlAuthorizationManager() {
084: super ();
085: }
086:
087: /**
088: * initialize this XML AuthorizationManager.
089: * @param options
090: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#init(java.util.Properties)
091: */
092: public void init(Map options) {
093: super .init(options);
094: String applicationName = (String) options
095: .get(CoreConstants.APPLICATION_NAME);
096: this .setApplicationName(applicationName);
097: super .options = options;
098: fileLocation = (String) options
099: .get(SecurityConstants.AUTHORIZATION_XML_FILE_LOCATION);
100: if (fileLocation == null || "".equals(fileLocation)) {
101: throw new IllegalArgumentException(
102: SecurityConstants.AUTHORIZATION_XML_FILE_LOCATION
103: + " argument for XMLAuthorizationManager is null or empty "
104: + fileLocation);
105: }
106: init();
107:
108: }
109:
110: /**
111: * initialize permissions and Principals.
112: */
113: private void init() {
114: //remove white spaces on both ends
115: fileLocation = fileLocation.trim();
116: //replace the white space remaining in the internal string structure
117: // by the '%20' pattern
118: fileLocation = fileLocation.replaceAll(" ", "%20");
119:
120: if (logger.isLoggable(Level.FINEST)) {
121: logger.finest("fileLocation=" + fileLocation);
122: }
123: document = XMLUtils.read(fileLocation);
124: root = document.getRootElement();
125:
126: initPermissions();
127: initPrincipals();
128: }
129:
130: /**
131: * build Principals and associate to them their permissions.
132: */
133: private void initPrincipals() {
134:
135: Element principalsElement = root.element("principals");
136: //convert a List into a Set because it is a functional requirement
137: //=> you can't have two same principals
138: List principalsElementList = principalsElement
139: .elements("principal");
140: Iterator itPrincipals = principalsElementList.iterator();
141:
142: while (itPrincipals.hasNext()) {
143: Element principalElement = (Element) itPrincipals.next();
144: String className = principalElement.element("class")
145: .getStringValue();
146: String name = null;
147:
148: if (className.equals(RolePrincipal.class.getName())) {
149: name = RolePrincipal.getName(principalElement.element(
150: "name").getStringValue(), applicationName);
151:
152: } else {
153: name = principalElement.element("name")
154: .getStringValue();
155: }
156: Principal ppal = PrincipalUtils.getPrincipal(className,
157: name);
158: if (className.equals(RolePrincipal.class.getName())) {
159: buildJGuardPrincipal(principalElement, ppal);
160: }
161: //add principal created to the Principals Set
162: principalsSet.add(ppal);
163: //add principal created to the principals map
164: principals.put(getLocalName(ppal), ppal);
165: }
166:
167: assemblyHierarchy();
168: }
169:
170: /**
171: * build permissions and domain maps.
172: */
173: private void initPermissions() {
174:
175: Element domainsElement = root.element("permissions");
176: List domainsElementList = domainsElement.elements("domain");
177: Iterator itDomains = domainsElementList.iterator();
178:
179: while (itDomains.hasNext()) {
180: Element domainElement = (Element) itDomains.next();
181: String id = domainElement.element("name").getStringValue();
182: JGPermissionCollection domain = new Domain(id);
183: //add the new domain to the set
184: domainsSet.add(domain);
185: // add the new domain to the map
186: domains.put(id, domain);
187: //permissions domain's Set
188: Set permissionsDomain = new HashSet();
189: //dom4j elements list
190: List permissionsElementList = domainElement
191: .elements("permission");
192: Iterator itPermissions = permissionsElementList.iterator();
193:
194: //iterate over domain's permissions
195: while (itPermissions.hasNext()) {
196: Element permissionElement = (Element) itPermissions
197: .next();
198: Element actionsElement = permissionElement
199: .element("actions");
200: List actionsList = actionsElement.elements();
201: Iterator itActions = actionsList.iterator();
202: StringBuffer sbActions = new StringBuffer();
203: int i = 0;
204: while (itActions.hasNext()) {
205: String actionTemp = ((Element) itActions.next())
206: .getText();
207: if (i != 0) {
208: sbActions.append(',');
209: }
210: sbActions.append(actionTemp);
211: i++;
212: }
213: String actions = sbActions.toString();
214: String permissionName = permissionElement.element(
215: "name").getTextTrim();
216:
217: String className = ((Element) permissionElement
218: .element("class")).getTextTrim();
219: Permission perm = null;
220: try {
221: perm = PermissionUtils.getPermission(className,
222: permissionName, actions);
223: } catch (ClassNotFoundException e) {
224: logger.warning(e.getMessage());
225: continue;
226: }
227: //add the permission to her domain
228: domain.add(perm);
229:
230: //add the permission to the global map
231: permissions.put(perm.getName(), perm);
232: permissionsSet.add(perm);
233: //add the permission to the domain' permissions list
234: permissionsDomain.add(perm);
235:
236: }
237: //add to the map the domain's id and his permissions
238: domainsPermissions.put(id, permissionsDomain);
239: }
240: super .urlp.addAll(permissionsSet);
241: }
242:
243: /**
244: * return needed initialization parameters.
245: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#getInitParameters()
246: */
247: public List getInitParameters() {
248: String[] authorizationParams = { "fileLocation" };
249: return Arrays.asList(authorizationParams);
250: }
251:
252: /**
253: * create an URLPermission int the corresponding backend.
254: * @param permission Permission
255: * @throws AuthorizationException
256: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#createPermission(java.security.Permission, java.lang.String)
257: */
258: public void createPermission(Permission permission,
259: String domainName) throws AuthorizationException {
260: String[] actions = permission.getActions().split(",");
261:
262: Element domainElement = (Element) root
263: .selectSingleNode("//domain[name='" + domainName + "']");
264: //add the permissionElement reference to the domainElement
265: Element permissionElement = domainElement
266: .addElement("permission");
267: Element nameElement = permissionElement.addElement("name");
268: nameElement.setText(permission.getName());
269: Element classElement = permissionElement.addElement("class");
270: classElement.setText(permission.getClass().getName());
271: Element actionsElement = permissionElement
272: .addElement("actions");
273: for (int i = 0; i < actions.length; i++) {
274: Element actionElement = actionsElement.addElement("action");
275: actionElement.setText(actions[i]);
276: }
277:
278: //we retrieve the domain corresponding to the domainName
279: //and linking together the URLPermission newly created
280: //and it
281: permissions.put(permission.getName(), permission);
282: permissionsSet.add(permission);
283: urlp.add(permission);
284: //add the permission to the Domain
285: ((JGPermissionCollection) domains.get(domainName))
286: .add(permission);
287:
288: try {
289: XMLUtils.write(fileLocation, document);
290: } catch (IOException e) {
291: logger.log(Level.SEVERE, "error when create permission "
292: + permission, e);
293: }
294:
295: }
296:
297: /**
298: * create a new domain.
299: * @param domainName
300: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#createDomain(java.lang.String)
301: */
302: public void createDomain(String domainName)
303: throws AuthorizationException {
304:
305: Element domainsElement = (Element) root
306: .selectSingleNode("//permissions");
307: //add the permissionElement reference to the domainElement
308: Element domainElement = domainsElement.addElement("domain");
309: Element nameElement = domainElement.addElement("name");
310: nameElement.setText(domainName);
311: JGPermissionCollection domain = new Domain(domainName);
312: domains.put(domainName, domain);
313: domainsSet.add(domain);
314: try {
315: XMLUtils.write(fileLocation, document);
316: } catch (IOException e) {
317: logger.log(Level.SEVERE, "createDomain(String)", e);
318: }
319:
320: }
321:
322: /**
323: * replace the inital permission with the new one.
324: * @param oldPermissionName old permission name
325: * @param permission URLPermission updated
326: * @param newDomainName
327: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#updatePermission(java.lang.String, java.security.Permission, java.lang.String)
328: */
329: public void updatePermission(String oldPermissionName,
330: Permission permission, String newDomainName)
331: throws AuthorizationException {
332: //we set the real domain to the updated permission and not a dummy one
333: deletePermission(oldPermissionName);
334: createPermission(permission, newDomainName);
335: }
336:
337: /**
338: * remove the permission.
339: * @param permissionName
340: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#deletePermission(java.lang.String)
341: */
342: public void deletePermission(String permissionName)
343: throws AuthorizationException {
344: Element permissionElement = (Element) root
345: .selectSingleNode("//permission[name='"
346: + permissionName + "']");
347: Element domainElement = (Element) root
348: .selectSingleNode("//permission[name='"
349: + permissionName + "']/..");
350: domainElement.remove(permissionElement);
351: Permission oldPermission = (Permission) permissions
352: .remove(permissionName);
353: Domain domain = getDomain(oldPermission);
354: domain.removePermission(oldPermission);
355: permissions.remove(oldPermission.getName());
356: permissionsSet.remove(oldPermission);
357: urlp.removePermission(oldPermission);
358: removePermissionFromPrincipals(permissionName);
359: updatePrincipals(domain);
360:
361: try {
362: XMLUtils.write(fileLocation, document);
363: } catch (IOException e) {
364: logger.log(Level.SEVERE, "deletePermission(String)", e);
365: }
366: }
367:
368: /**
369: * delete domain
370: * @param domainName name to delete
371: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#deleteDomain(java.lang.String)
372: */
373: public void deleteDomain(String domainName)
374: throws AuthorizationException {
375: domains.remove(domainName);
376: domainsSet.remove(new Domain(domainName));
377: Element domainsElement = (Element) root
378: .selectSingleNode("//permissions");
379: Element domainElement = (Element) domainsElement
380: .selectSingleNode("//domain[name='" + domainName + "']");
381: domainsElement.remove(domainElement);
382: super .removeDomainFromPrincipals(domainName);
383: try {
384: XMLUtils.write(fileLocation, document);
385: } catch (IOException e) {
386: logger.log(Level.SEVERE, "deleteDomain(String)", e);
387: }
388:
389: }
390:
391: /**
392: * create a new Role/principal
393: * @param principal principal/role to create
394: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#createPrincipal(net.sf.jguard.core.principals.RolePrincipal)
395: */
396: public void createPrincipal(Principal principal)
397: throws AuthorizationException {
398: Element principalsElement = root.element("principals");
399: //add the permissionElement reference to the domainElement
400: Element principalElement = principalsElement
401: .addElement("principal");
402: Element nameElement = principalElement.addElement("name");
403: //add 'class' Element
404: Element classElement = principalElement.addElement("class");
405: classElement.setText(principal.getClass().getName());
406:
407: nameElement.setText(getLocalName(principal));
408: principals.put(getLocalName(principal), principal);
409: principalsSet.add(principal);
410: if (principal.getClass().equals(RolePrincipal.class)) {
411: RolePrincipal ppal = (RolePrincipal) principal;
412: insertPermissionsAndInheritance(principalElement, ppal);
413: }
414:
415: try {
416: XMLUtils.write(fileLocation, document);
417: } catch (IOException e) {
418: logger.log(Level.SEVERE, "createRole(RolePrincipal)", e);
419: }
420:
421: }
422:
423: private void insertPermissionsAndInheritance(
424: Element principalElement, RolePrincipal ppal) {
425: //add the orphaned permissions to the XML file
426: Element permsRefElement = principalElement
427: .addElement("permissionsRef");
428: Set orphanedPerms = ppal.getOrphanedPermissions();
429: Iterator orphanedPermsIterator = orphanedPerms.iterator();
430: while (orphanedPermsIterator.hasNext()) {
431: Permission perm = (Permission) orphanedPermsIterator.next();
432: Element permRef = permsRefElement
433: .addElement("permissionRef");
434: //add the name attribute
435: Attribute nameAttribute = new UserDataAttribute(new QName(
436: "name"));
437: nameAttribute.setValue(perm.getName());
438: permRef.add(nameAttribute);
439: }
440:
441: //add the permissions from domains to the XML file
442: Set doms = ppal.getDomains();
443: Iterator PermsFromDomainsIterator = doms.iterator();
444: while (PermsFromDomainsIterator.hasNext()) {
445: Domain dom = (Domain) PermsFromDomainsIterator.next();
446: Element permRef = permsRefElement.addElement("domainRef");
447: //add the name attribute
448: Attribute nameAttribute = new UserDataAttribute(new QName(
449: "name"));
450: nameAttribute.setValue(dom.getName());
451: permRef.add(nameAttribute);
452: }
453:
454: //role inheritance is only supported by RolePrincipal
455: if (ppal.getDescendants().size() > 0) {
456: Element descendants = principalElement
457: .addElement("descendants");
458:
459: //add the descendants of this role
460: for (Iterator descendantsIterator = ppal.getDescendants()
461: .iterator(); descendantsIterator.hasNext();) {
462: Element principalRef = descendants
463: .addElement("principalRef");
464:
465: Attribute nameAttribute = new UserDataAttribute(
466: new QName("name"));
467: nameAttribute
468: .setValue(((RolePrincipal) descendantsIterator
469: .next()).getLocalName());
470: principalRef.add(nameAttribute);
471: }
472: }
473: }
474:
475: /**
476: * remove the corrspoding principal/role
477: * @param principal name
478: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#deletePrincipal(java.security.Principal)
479: */
480: public void deletePrincipal(Principal principal)
481: throws AuthorizationException {
482: if (principal == null) {
483: throw new IllegalArgumentException(
484: "principal parameter is null ");
485: }
486: Principal ppalReference = (Principal) principals
487: .remove(getLocalName(principal));
488: if ((ppalReference == null)) {
489: logger.warning(" there is no principal intitled "
490: + principal.getName() + " to delete");
491: return;
492: }
493: principalsSet.remove(ppalReference);
494: Element principalsElement = root.element("principals");
495: Element principalElement = (Element) principalsElement
496: .selectSingleNode("//principal[name='"
497: + getLocalName(principal) + "']");
498: principalsElement.remove(principalElement);
499: if (ppalReference.getClass().equals(RolePrincipal.class)) {
500: deleteReferenceInHierarchy((RolePrincipal) ppalReference);
501: //delete all the references of this principal
502: XMLUtils.deletePrincipalRefs(root,
503: (RolePrincipal) ppalReference);
504: }
505: try {
506: XMLUtils.write(fileLocation, document);
507: } catch (IOException e) {
508: logger.log(Level.SEVERE, "deleteRole(String)", e);
509: }
510: }
511:
512: /**
513: * update the specified Domain.
514: * @param newName new name for the corresponding Domain
515: * @param oldName old name for the corresponding Domain
516: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#updateDomain(java.lang.String, java.lang.String)
517: */
518: public void updateDomain(String newName, String oldName)
519: throws AuthorizationException {
520:
521: Domain domain = (Domain) domains.get(oldName);
522: domains.remove(oldName);
523: domainsSet.remove(domain);
524: domain.setName(newName);
525: domains.put(domain.getName(), domain);
526: domainsSet.add(domain);
527: this .updatePrincipals(domain, oldName);
528: Element domainsElement = (Element) root
529: .selectSingleNode("//permissions");
530: Element domainElement = (Element) domainsElement
531: .selectSingleNode("//domain[name='" + oldName + "']");
532: Element name = domainElement.element("name");
533: name.setText(newName);
534: try {
535: XMLUtils.write(fileLocation, document);
536: } catch (IOException e) {
537: logger.log(Level.SEVERE, "updateDomain(String, String)", e);
538: }
539: }
540:
541: /**
542: * update a principal
543: * @param oldPrincipalName name of the principal to be replaced
544: * @param principal new principal
545: * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#updatePrincipal(java.lang.String, net.sf.jguard.core.principals.RolePrincipal)
546: */
547: public void updatePrincipal(String oldPrincipalName,
548: Principal principal) throws AuthorizationException {
549: Principal oldPal = (Principal) principals
550: .remove(oldPrincipalName);
551: if (oldPal == null) {
552: logger.log(Level.WARNING, " principal " + oldPrincipalName
553: + " cannot be updated because it does not exists ");
554: return;
555: }
556: principalsSet.remove(oldPal);
557: principals.put(getLocalName(principal), principal);
558: principalsSet.add(principal);
559:
560: try {
561: XMLUtils.write(fileLocation, document);
562: } catch (IOException e) {
563: logger.log(Level.SEVERE,
564: "updateRole(String, RolePrincipal)", e);
565: }
566: }
567:
568: /**
569: * add specific object to RolePrincipal (permissions and domains references).
570: * @param principalElement
571: * @param ppal
572: */
573: private void buildJGuardPrincipal(Element principalElement,
574: Principal ppal) {
575: RolePrincipal jp = (RolePrincipal) ppal;
576: Element pel = principalElement.element("permissionsRef");
577: Collection domainsPrincipal = pel.elements("domainRef");
578: Iterator itDomainsPrincipal = domainsPrincipal.iterator();
579: //domains names owned by this principal
580: Set domainNames = new HashSet();
581:
582: //iterate over principal's domains
583: while (itDomainsPrincipal.hasNext()) {
584: Element domainElement = (Element) itDomainsPrincipal.next();
585: String domainName = domainElement.attributeValue("name");
586: JGPermissionCollection domain = (JGPermissionCollection) domains
587: .get(domainName);
588:
589: if (domain == null) {
590: if (logger.isLoggable(Level.WARNING)) {
591: logger.warning("initPrincipals() - principal "
592: + jp.getLocalName()
593: + " refers to a unknown domain name :"
594: + domainName);
595: }
596: }
597: if (!domainNames.contains(domainName)) {
598: domainNames.add(domainName);
599: permissionsSet.addAll(domain.getPermissions());
600: urlp.addAll(domain.getPermissions());
601: jp.addDomain(domain);
602: }
603: }
604:
605: Collection permissionsPrincipal = pel.elements("permissionRef");
606: Iterator itPermissionsPrincipal = permissionsPrincipal
607: .iterator();
608:
609: //iterate over permissions defined and add them to the principal permissions Set
610: while (itPermissionsPrincipal.hasNext()) {
611: Element perm = (Element) itPermissionsPrincipal.next();
612: String permissionName = perm.attributeValue("name");
613: permissionsSet.add(permissions.get(permissionName));
614: Permission permission = (Permission) permissions
615: .get(permissionName);
616: urlp.add(permission);
617: jp.addPermission(permission);
618: if (null == permission) {
619: if (logger.isLoggable(Level.WARNING)) {
620: logger.warning("initPrincipals() - principal "
621: + jp.getName()
622: + " refers to a unknown permission name :"
623: + permissionName);
624: }
625: }
626: }
627: //store the links between ascendants
628: Element descendants = principalElement.element("descendants");
629: if (descendants != null) {
630: List descendantsElements = descendants
631: .elements("principalRef");
632: Iterator itDescendantsElements = descendantsElements
633: .iterator();
634: Collection descendantsNames = new ArrayList();
635: while (itDescendantsElements.hasNext()) {
636: Element descentantItem = (Element) itDescendantsElements
637: .next();
638: descendantsNames.add(principals.get(descentantItem
639: .attributeValue("name")));
640: }
641:
642: hierarchyMap.put(getLocalName(jp), descendantsNames);
643: }
644: }
645:
646: /**
647: * @return <i>true</i> if there is no principals and no permissions.
648: * <i>false</i> otherwise.
649: */
650: public boolean isEmpty() {
651: List principalsList = root.selectNodes("//principal");
652: List permissions = root.selectNodes("//permissions");
653: if (!principalsList.isEmpty() && !permissions.isEmpty()) {
654: return false;
655: }
656: return true;
657: }
658:
659: public String exportAsXMLString() {
660: return this .document.asXML();
661: }
662:
663: public void writeAsHTML(OutputStream outputStream)
664: throws IOException {
665: HTMLWriter writer = new HTMLWriter(outputStream, OutputFormat
666: .createPrettyPrint());
667: writer.write(this .document);
668: writer.flush();
669:
670: }
671:
672: public void writeAsXML(OutputStream outputStream,
673: String encodingScheme) throws IOException {
674: OutputFormat outformat = OutputFormat.createPrettyPrint();
675: outformat.setEncoding(encodingScheme);
676: XMLWriter writer = new XMLWriter(outputStream, outformat);
677: writer.write(this .document);
678: writer.flush();
679: }
680:
681: public void exportAsXMLFile(String fileName) throws IOException {
682: XMLWriter xmlWriter = new XMLWriter(new FileWriter(fileName),
683: OutputFormat.createPrettyPrint());
684: xmlWriter.write(document);
685: xmlWriter.close();
686: }
687:
688: }
|