View Javadoc

1   /*
2   jGuard is a security framework based on top of jaas (java authentication and authorization security).
3   it is written for web applications, to resolve simply, access control problems.
4   version $Name$
5   http://sourceforge.net/projects/jguard/
6   
7   Copyright (C) 2004  Charles GAY
8   
9   This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Lesser General Public
11  License as published by the Free Software Foundation; either
12  version 2.1 of the License, or (at your option) any later version.
13  
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  Lesser General Public License for more details.
18  
19  You should have received a copy of the GNU Lesser General Public
20  License along with this library; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  
23  
24  jGuard project home page:
25  http://sourceforge.net/projects/jguard/
26  
27  */
28  package net.sf.jguard.ext.authorization.manager;
29  
30  import net.sf.jguard.core.authorization.manager.AuthorizationManager;
31  import java.io.FileWriter;
32  import java.io.IOException;
33  import java.io.OutputStream;
34  import java.security.Permission;
35  import java.security.Principal;
36  import java.util.ArrayList;
37  import java.util.Arrays;
38  import java.util.Collection;
39  import java.util.HashSet;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.Set;
44  
45  import java.util.logging.Level;
46  import net.sf.jguard.core.CoreConstants;
47  import net.sf.jguard.core.PolicyEnforcementPointOptions;
48  import net.sf.jguard.core.authorization.permissions.Domain;
49  import net.sf.jguard.core.authorization.permissions.JGPermissionCollection;
50  import net.sf.jguard.core.authorization.permissions.PermissionUtils;
51  import net.sf.jguard.core.principals.RolePrincipal;
52  import net.sf.jguard.core.authorization.AuthorizationException;
53  import net.sf.jguard.core.principals.PrincipalUtils;
54  import net.sf.jguard.core.util.XMLUtils;
55  
56  import org.dom4j.Attribute;
57  import org.dom4j.Document;
58  import org.dom4j.Element;
59  import org.dom4j.QName;
60  import org.dom4j.io.HTMLWriter;
61  import org.dom4j.io.OutputFormat;
62  import org.dom4j.io.XMLWriter;
63  import org.dom4j.util.UserDataAttribute;
64  import org.slf4j.Logger;
65  import org.slf4j.LoggerFactory;
66  
67  
68  /**
69   * AuthorizationManager implementation which enable Permission Management with an XML backend.
70   * @author <a href="mailto:diabolo512@users.sourceforge.net">Charles Gay</a>
71   * @author <a href="mailto:vinipitta@users.sourceforge.net">Vinicius Pitta Lima de Araujo</a>
72   */
73  public class XmlAuthorizationManager extends AbstractAuthorizationManager implements AuthorizationManager{
74  	/** Logger for this class */
75  	private static final Logger logger = LoggerFactory.getLogger(XmlAuthorizationManager.class.getName());
76  
77      private Element root;
78      private Document document = null;
79      private String fileLocation;
80  
81  
82  
83      /**
84       * initialize this XML AuthorizationManager.
85       * @param options
86       * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#init(java.util.Properties)
87       */
88      public XmlAuthorizationManager(Map options) {
89      	super(options);
90      	String applicationName= (String)options.get(PolicyEnforcementPointOptions.APPLICATION_NAME.getLabel());
91      	this.setApplicationName(applicationName);
92      	super.options = options;
93          fileLocation = (String)options.get(CoreConstants.AUTHORIZATION_XML_FILE_LOCATION);
94          if(fileLocation ==null ||"".equals(fileLocation)){
95          	throw new IllegalArgumentException(CoreConstants.AUTHORIZATION_XML_FILE_LOCATION+" argument for XMLAuthorizationManager is null or empty "+fileLocation);
96          }
97          init();
98  
99      }
100 
101 
102 	/**
103 	 * initialize permissions and Principals.
104 	 */
105 	private void init()  {
106         //remove white spaces on both ends
107         fileLocation = fileLocation.trim();
108         //replace the white space remaining in the internal string structure
109         // by the '%20' pattern
110         fileLocation = fileLocation.replaceAll(" ","%20");
111 
112         if(logger.isDebugEnabled()){
113         	logger.debug("fileLocation="+fileLocation);
114         }
115 		document =  XMLUtils.read(fileLocation);
116 		root = document.getRootElement();
117 
118 		initPermissions();
119 		initPrincipals();
120 	}
121 
122 
123 	/**
124      * build Principals and associate to them their permissions.
125 	 */
126 	private void initPrincipals() {
127 
128 		 Element principalsElement  = root.element("principals");
129             //convert a List into a Set because it is a functional requirement
130             //=> you can't have two same principals
131 		    List principalsElementList = principalsElement.elements("principal");
132 		    Iterator itPrincipals = principalsElementList.iterator();
133 
134 		    while(itPrincipals.hasNext()){
135 		        Element principalElement = (Element)itPrincipals.next();
136 		        String className = principalElement.element("class").getStringValue();
137 		        String name = null;
138 		        
139 		        if(className.equals(RolePrincipal.class.getName())){
140 		        	name = RolePrincipal.getName(principalElement.element("name").getStringValue(), applicationName);
141 		         
142 		        }else{
143 		        	name = principalElement.element("name").getStringValue();
144 		        }
145 		        Principal ppal = PrincipalUtils.getPrincipal(className,name);
146 		        if(className.equals(RolePrincipal.class.getName())){
147 		        	buildJGuardPrincipal(principalElement, ppal);	
148 		        }
149 		        //add principal created to the Principals Set
150 		        principalsSet.add(ppal);
151                 //add principal created to the principals map
152                 principals.put(getLocalName(ppal),ppal);
153 		    }
154 
155 		    assemblyHierarchy();
156 	}
157 
158 
159 	/**
160      * build permissions and domain maps.
161 	 */
162 	private void initPermissions() {
163 
164          Element domainsElement = root.element("permissions");
165          List domainsElementList = domainsElement.elements("domain");
166 		Iterator itDomains = domainsElementList.iterator();
167 
168 		while(itDomains.hasNext()){
169 		    Element domainElement = (Element)itDomains.next();
170 		    String id = domainElement.element("name").getStringValue();
171 		    JGPermissionCollection domain = new Domain(id);
172                     //add the new domain to the set
173                     domainsSet.add(domain);
174                     // add the new domain to the map
175                     domains.put(id,domain);
176                     
177 		    //permissions domain's Set
178 		    Set permissionsDomain = new HashSet();
179 		    //dom4j elements list
180 		    List permissionsElementList = domainElement.elements("permission");
181 		    Iterator itPermissions = permissionsElementList.iterator();
182 
183 		    //iterate over domain's permissions
184 		    while(itPermissions.hasNext()){
185                       Element permissionElement = (Element)itPermissions.next();
186                       Element actionsElement = permissionElement.element("actions");
187                       List actionsList = actionsElement.elements();
188                       Iterator itActions = actionsList.iterator();
189                       StringBuffer sbActions = new StringBuffer();
190                       int i = 0;
191                       while(itActions.hasNext()){
192                 	 String actionTemp = ((Element)itActions.next()).getText();
193                 	 if(i!=0){
194                 	   sbActions.append(',');
195                 	 }
196                 	 sbActions.append(actionTemp);
197                 	 i++;
198                       }
199                       String actions = sbActions.toString();
200 		      String permissionName= permissionElement.element("name").getTextTrim();
201 
202 		        String className = ((Element)permissionElement.element("class")).getTextTrim();
203 		        Permission perm = null;
204 				try {
205 					perm = PermissionUtils.getPermission(className,permissionName,actions);
206 				} catch (ClassNotFoundException e) {
207 					logger.warn(e.getMessage());
208 					continue;
209 				}
210                 //add the permission to her domain
211                 domain.add(perm);
212 
213 		        //add the permission to the global map
214 		        permissions.put(perm.getName(),perm);
215                 permissionsSet.add(perm);
216 		        //add the permission to the domain' permissions list
217 		        permissionsDomain.add(perm);
218 
219 		    }
220 		    //add to the map the domain's id and his permissions
221 		    domainsPermissions.put(id,permissionsDomain);
222 		}
223 		super.urlp.addAll(permissionsSet);
224 	}
225 
226     /**
227      * return needed initialization parameters.
228 	 * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#getInitParameters()
229 	 */
230 	public List getInitParameters() {
231         String[] authorizationParams = {"fileLocation"};
232        return Arrays.asList(authorizationParams);
233 	}
234 
235     /**
236      * create an URLPermission int the corresponding backend.
237      * @param permission Permission
238      * @throws AuthorizationException
239      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#createPermission(java.security.Permission, java.lang.String)
240      */
241      public void createPermission(Permission permission,String domainName) throws AuthorizationException {
242 	     String[] actions = permission.getActions().split(",");
243 
244          Element domainElement = (Element)root.selectSingleNode("//domain[name='"+domainName+"']");
245          //add the permissionElement reference to the domainElement
246          Element permissionElement = domainElement.addElement("permission");
247          Element nameElement = permissionElement.addElement("name");
248          nameElement.setText(permission.getName());
249          Element classElement = permissionElement.addElement("class");
250          classElement.setText(permission.getClass().getName());
251          Element actionsElement = permissionElement.addElement("actions");
252          for(int i= 0;i<actions.length;i++){
253         	 Element actionElement = actionsElement.addElement("action");
254         	 actionElement.setText(actions[i]);
255          }
256 
257          //we retrieve the domain corresponding to the domainName
258          //and linking together the URLPermission newly created
259          //and it
260          permissions.put(permission.getName(),permission);
261          permissionsSet.add(permission);
262          urlp.add(permission);
263          //add the permission to the Domain
264          ((JGPermissionCollection)domains.get(domainName)).add(permission);
265 
266          try {
267             XMLUtils.write(fileLocation,document);
268         } catch (IOException e) {
269             logger.error( "error when create permission "+permission,e);
270         }
271 
272     }
273 
274      /**
275       * create a new domain.
276       * @param domainName
277       * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#createDomain(java.lang.String)
278       */
279      public void createDomain(String  domainName) throws AuthorizationException {
280 
281          Element domainsElement = (Element)root.selectSingleNode("//permissions");
282           //add the permissionElement reference to the domainElement
283           Element domainElement = domainsElement.addElement("domain");
284           Element nameElement = domainElement.addElement("name");
285           nameElement.setText(domainName);
286           JGPermissionCollection domain = new Domain(domainName);
287           domains.put(domainName,domain);
288           domainsSet.add(domain);
289          try {
290         	 XMLUtils.write(fileLocation,document);
291         } catch (IOException e) {
292 			logger.error( "createDomain(String)", e);
293         }
294 
295      }
296     /**
297      * replace the inital permission with the new one.
298      * @param oldPermissionName old permission name
299      * @param permission URLPermission updated
300      * @param newDomainName
301      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#updatePermission(java.lang.String, java.security.Permission, java.lang.String)
302      */
303     public void updatePermission(String oldPermissionName, Permission permission,String newDomainName) throws AuthorizationException {
304         //we set the real domain to the updated permission and not a dummy one
305         deletePermission(oldPermissionName);
306         createPermission(permission,newDomainName);
307     }
308 
309 
310     /**
311      * remove the permission.
312      * @param permissionName
313      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#deletePermission(java.lang.String)
314      */
315     public void deletePermission(String permissionName) throws AuthorizationException {
316         Element permissionElement = (Element)root.selectSingleNode("//permission[name='"+permissionName+"']");
317         Element domainElement = (Element)root.selectSingleNode("//permission[name='"+permissionName+"']/..");
318         domainElement.remove(permissionElement);
319         Permission oldPermission = (Permission)permissions.remove(permissionName);
320         Domain domain = getDomain(oldPermission);
321         domain.removePermission(oldPermission);
322         permissions.remove(oldPermission.getName());
323         permissionsSet.remove(oldPermission);
324         urlp.removePermission(oldPermission);
325         removePermissionFromPrincipals(permissionName);
326         updatePrincipals(domain);
327 
328         try {
329         	XMLUtils.write(fileLocation,document);
330         } catch (IOException e) {
331 			logger.error( "deletePermission(String)", e);
332         }
333     }
334 
335 
336     /**
337      * delete domain
338      * @param domainName name to delete
339      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#deleteDomain(java.lang.String)
340      */
341     public void deleteDomain(String domainName) throws AuthorizationException {
342         domains.remove(domainName);
343         domainsSet.remove(new Domain(domainName));
344         Element domainsElement = (Element)root.selectSingleNode("//permissions");
345         Element domainElement = (Element)domainsElement.selectSingleNode("//domain[name='"+domainName+"']");
346         domainsElement.remove(domainElement);
347         super.removeDomainFromPrincipals(domainName);
348         try {
349         	XMLUtils.write(fileLocation,document);
350         } catch (IOException e) {
351 			logger.error( "deleteDomain(String)", e);
352         }
353 
354     }
355 
356 
357     /**
358      * create a new Role/principal
359      * @param principal principal/role to create
360      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#createPrincipal(net.sf.jguard.core.principals.RolePrincipal)
361      */
362     public void createPrincipal(Principal principal) throws AuthorizationException {
363         Element principalsElement = root.element("principals");
364         //add the permissionElement reference to the domainElement
365         Element principalElement = principalsElement.addElement("principal");
366         Element nameElement = principalElement.addElement("name");
367         //add 'class' Element
368         Element classElement = principalElement.addElement("class");
369         classElement.setText(principal.getClass().getName());
370 
371         nameElement.setText(getLocalName(principal));
372         principals.put(getLocalName(principal),principal);
373         principalsSet.add(principal);
374         if (principal.getClass().equals(RolePrincipal.class)){
375           RolePrincipal ppal = (RolePrincipal)principal;
376 	      insertPermissionsAndInheritance(principalElement, ppal);
377         }
378 
379        try {
380     	   XMLUtils.write(fileLocation,document);
381       } catch (IOException e) {
382 			logger.error( "createRole(RolePrincipal)", e);
383       }
384 
385     }
386 
387 	private void insertPermissionsAndInheritance(Element principalElement, RolePrincipal ppal) {
388 		//add the orphaned permissions to the XML file
389 		Element permsRefElement = principalElement.addElement("permissionsRef");
390 		Set orphanedPerms = ppal.getOrphanedPermissions();
391 		Iterator orphanedPermsIterator = orphanedPerms.iterator();
392 		while(orphanedPermsIterator.hasNext()){
393 		    Permission perm = (Permission)orphanedPermsIterator.next();
394 		    Element permRef = permsRefElement.addElement("permissionRef");
395 		    //add the name attribute
396 		    Attribute nameAttribute = new UserDataAttribute(new QName("name"));
397 		    nameAttribute.setValue(perm.getName());
398 		    permRef.add(nameAttribute);
399 		}
400 
401 		//add the permissions from domains to the XML file
402 		Set doms = ppal.getDomains();
403 		Iterator PermsFromDomainsIterator = doms.iterator();
404 		while(PermsFromDomainsIterator.hasNext()){
405 		    Domain dom = (Domain)PermsFromDomainsIterator.next();
406 		    Element permRef = permsRefElement.addElement("domainRef");
407 		    //add the name attribute
408 		    Attribute nameAttribute = new UserDataAttribute(new QName("name"));
409 		    nameAttribute.setValue(dom.getName());
410 		    permRef.add(nameAttribute);
411 		}
412 
413 		//role inheritance is only supported by RolePrincipal
414 		if(ppal.getDescendants().size() > 0) {
415 		        Element descendants = principalElement.addElement("descendants");
416 
417 		        //add the descendants of this role
418 		        for (Iterator descendantsIterator = ppal.getDescendants().iterator();
419 		            descendantsIterator.hasNext(); ) {
420 		            Element principalRef = descendants.addElement("principalRef");
421 
422 		            Attribute nameAttribute = new UserDataAttribute(new QName("name"));
423 		            nameAttribute.setValue(((RolePrincipal) descendantsIterator.next()).getLocalName());
424 		            principalRef.add(nameAttribute);
425 		        }
426 		}
427 	}
428 
429     /**
430      * remove the corrspoding principal/role
431      * @param principal name
432      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#deletePrincipal(java.security.Principal)
433      */
434     public void deletePrincipal(Principal principal) throws AuthorizationException {
435     	if(principal==null){
436     		throw new IllegalArgumentException("principal parameter is null ");
437     	}
438         Principal ppalReference = (Principal)principals.remove(getLocalName(principal));
439         if((ppalReference==null)){
440         	logger.warn(" there is no principal intitled "+principal.getName()+" to delete");
441         	return;
442         }
443         principalsSet.remove(ppalReference);
444         Element principalsElement = root.element("principals");
445         Element principalElement = (Element)principalsElement.selectSingleNode("//principal[name='"+getLocalName(principal)+"']");
446         principalsElement.remove(principalElement);
447         if(ppalReference.getClass().equals(RolePrincipal.class)){
448           deleteReferenceInHierarchy((RolePrincipal)ppalReference);
449           //delete all the references of this principal
450           XMLUtils.deletePrincipalRefs(root,(RolePrincipal)ppalReference);
451         }
452         try {
453         	XMLUtils.write(fileLocation,document);
454         } catch (IOException e) {
455 			logger.error( "deleteRole(String)", e);
456         }
457     }
458 
459 
460 
461     /**
462      * update the specified Domain.
463      * @param newName new name for the corresponding Domain
464      * @param oldName old name for the corresponding Domain
465      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#updateDomain(java.lang.String, java.lang.String)
466      */
467     public void updateDomain(String newName, String oldName) throws AuthorizationException {
468 
469         Domain domain = (Domain)domains.get(oldName);
470         domains.remove(oldName);
471         domainsSet.remove(domain);
472         domain.setName(newName);
473         domains.put(domain.getName(),domain);
474         domainsSet.add(domain);
475         this.updatePrincipals(domain, oldName);
476         Element domainsElement = (Element)root.selectSingleNode("//permissions");
477         Element domainElement = (Element)domainsElement.selectSingleNode("//domain[name='"+oldName+"']");
478         Element name =domainElement.element("name");
479         name.setText(newName);
480         try {
481         	XMLUtils.write(fileLocation,document);
482         } catch (IOException e) {
483 			logger.error( "updateDomain(String, String)", e);
484         }
485     }
486 
487     /**
488      * update a principal
489      * @param oldPrincipalName name of the principal to be replaced
490      * @param principal new principal
491      * @see net.sf.jguard.ext.authorization.manager.AuthorizationManager#updatePrincipal(java.lang.String, net.sf.jguard.core.principals.RolePrincipal)
492      */
493     public void updatePrincipal(String oldPrincipalName, Principal principal) throws AuthorizationException {
494         Principal oldPal = (Principal)principals.remove(oldPrincipalName);
495         if(oldPal==null){
496         	logger.warn(" principal "+oldPrincipalName+" cannot be updated because it does not exists ");
497         	return;
498         }
499         principalsSet.remove(oldPal);
500         principals.put(getLocalName(principal),principal);
501         principalsSet.add(principal);
502 
503         try {
504         	XMLUtils.write(fileLocation,document);
505         } catch (IOException e) {
506 			logger.error( "updateRole(String, RolePrincipal)", e);
507         }
508     }
509 
510 
511 
512 
513 
514         /**
515 	 * add permissions and domains references to RolePrincipal.
516 	 * @param principalElement
517 	 * @param ppal
518 	 */
519 	private void buildJGuardPrincipal(Element principalElement, Principal ppal) {
520 		RolePrincipal rolePrincipal = (RolePrincipal)ppal;
521 		Element pel = principalElement.element("permissionsRef");
522 		Collection domainsPrincipal = pel.elements("domainRef");
523 		Iterator itDomainsPrincipal = domainsPrincipal.iterator();
524         //domains names owned by this principal
525         Set domainNames = new HashSet();
526 
527 		//iterate over principal's domains
528 		while(itDomainsPrincipal.hasNext()){
529 		    Element domainElement = (Element)itDomainsPrincipal.next();
530 		    String domainName = domainElement.attributeValue("name");
531 		    JGPermissionCollection domain = (JGPermissionCollection)domains.get(domainName);
532 
533 		    if(domain==null){
534                         logger.warn("initPrincipals() - principal "
535                                 + rolePrincipal.getLocalName()
536                                 + " refers to a unknown domain name :"
537                                 + domainName);
538 		    }
539                     
540 		    if(!domainNames.contains(domainName)){
541 			    domainNames.add(domainName);
542 			    permissionsSet.addAll(domain.getPermissions());
543 			    urlp.addAll(domain.getPermissions());
544 			    rolePrincipal.addDomain(domain);
545 		    }
546 		}
547 
548 
549 		Collection permissionsPrincipal = pel.elements("permissionRef");
550 		Iterator itPermissionsPrincipal = permissionsPrincipal.iterator();
551 
552 		//iterate over permissions defined and add them to the principal permissions Set
553 		while(itPermissionsPrincipal.hasNext()){
554 		    Element perm = (Element)itPermissionsPrincipal.next();
555 		    String permissionName=perm.attributeValue("name");
556                     Permission permission = (Permission)permissions.get(permissionName);
557                     if(permission==null){
558                         logger.warn("initPrincipals() - principal "
559 							+ rolePrincipal.getName()
560 							+ " refers to a unknown permission name :"
561 							+ permissionName);
562                         
563                         continue;
564                     }
565 		    permissionsSet.add(permission);
566 		    urlp.add(permission);
567 		    rolePrincipal.addPermission(permission);
568 		    
569 		}
570 		//store the links between ascendants
571         Element descendants = principalElement.element("descendants");
572         if (descendants != null) {
573         	  List descendantsElements = descendants.elements("principalRef");
574         	  Iterator itDescendantsElements = descendantsElements.iterator();
575         	  Collection descendantsNames = new ArrayList();
576               while(itDescendantsElements.hasNext()){
577                     Element descentantItem = (Element)itDescendantsElements.next();
578                     descendantsNames.add(principals.get(descentantItem.attributeValue("name")));
579               }
580 
581               hierarchyMap.put(getLocalName(rolePrincipal),descendantsNames);
582         }
583 	}
584 
585 	/**
586 	 * @return <i>true</i> if there is no principals and no permissions.
587 	 * <i>false</i> otherwise.
588 	 */
589 	public boolean isEmpty() {
590         List principalsList = root.selectNodes("//principal");
591         List permissions = root.selectNodes("//permissions");
592         if(!principalsList.isEmpty()&&!permissions.isEmpty()){
593             return false;
594         }
595 		return true;
596 	}
597 	
598 	public String exportAsXMLString(){
599 		return this.document.asXML();
600 	}
601 
602 	public void writeAsHTML(OutputStream outputStream) throws IOException {
603 		 HTMLWriter writer = new HTMLWriter(outputStream,OutputFormat.createPrettyPrint());
604 		 writer.write(this.document);
605 		 writer.flush();
606 		
607 	}
608 	
609 	public void writeAsXML(OutputStream outputStream, String encodingScheme) throws IOException  {
610 		   OutputFormat outformat = OutputFormat.createPrettyPrint();
611 		   outformat.setEncoding(encodingScheme);
612 		   XMLWriter writer = new XMLWriter(outputStream, outformat);
613 		   writer.write(this.document);
614 		   writer.flush();
615 	}
616 	public void exportAsXMLFile(String fileName) throws IOException {
617 		XMLWriter xmlWriter = new XMLWriter(new FileWriter(fileName), OutputFormat.createPrettyPrint());
618 		xmlWriter.write(document);
619 		xmlWriter.close();
620 	}
621 
622     public void refresh() {
623         
624     }
625 
626 }