/* * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at . * * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT * WARRANTY OF ANY KIND, either express or implied. See the License for the specific * language governing rights and limitations under the License. * * The Original Code is the Venice Web Communities System. * * The Initial Developer of the Original Code is Eric J. Bowersox , * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * * Contributor(s): */ package com.silverwrist.venice.security; import java.util.*; import org.apache.log4j.*; import org.w3c.dom.*; import com.silverwrist.util.DOMElementHelper; import com.silverwrist.venice.core.AccessError; import com.silverwrist.venice.core.ConfigException; public class StaticSecurityMonitor implements SecurityMonitor { /*-------------------------------------------------------------------------------- * Internal class for evaluating static permissions *-------------------------------------------------------------------------------- */ final class StaticPermission { private Role role; private String message; StaticPermission(Role role, String message) { this.role = role; this.message = message; } // end constructor final void test(int level, String errormessage) throws AccessError { if (!(role.isSatisfiedBy(level))) { // the static permission test failed! logger.warn("Static permission test (level " + level + " vs. role " + role + ") failed"); if (errormessage==null) errormessage = message; if (errormessage==null) errormessage = "Operation not permitted."; throw new AccessError(errormessage); } // end if } // end test final boolean test(int level) { return role.isSatisfiedBy(level); } // end test } // end class StaticPermission /*-------------------------------------------------------------------------------- * Static data members *-------------------------------------------------------------------------------- */ private static Category logger = Category.getInstance(StaticSecurityMonitor.class); private static SecurityMonitor root_monitor = null; private static Map known_monitors = Collections.synchronizedMap(new HashMap()); private static int DEFAULT_SCOPE_OFFSET = 3; /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- */ private String id; // the identity of this security monitor private ScopeInfo scope; // the scope of this security monitor private SecurityMonitor parent; // the parent of this security monitor private Map sym_to_role; // mapping of role symbols to roles private Map level_to_role; // mapping of role levels to roles private Map lists; // mapping of list symbols to lists private Map default_roles; // mapping of symbols to default values private Map static_permissions; // mapping of symbols to static permissions private Set dynamic_permissions; // set of defined dynamic permission names /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ public StaticSecurityMonitor(Element cfg) throws ConfigException { boolean set_root_monitor = false; if (!(cfg.getTagName().equals("security-definition"))) { // not the right kind of element! logger.fatal("security monitor config is not a element"); throw new ConfigException("configuration must be a ",cfg); } // end if DOMElementHelper root_h = new DOMElementHelper(cfg); if (root_h.hasAttribute("id")) id = cfg.getAttribute("id"); else { // no id= attribute? that's bad! logger.fatal("security monitor has no id= attribute"); throw new ConfigException(" must have an id= attribute",cfg); } // end else if (logger.isDebugEnabled()) logger.debug("defining new StaticSecurityMonitor with id=" + id); if (known_monitors.containsKey(id)) { // the monitor with this ID has already been defined! logger.fatal("security monitor with id=" + id + " is already defined!"); throw new ConfigException("security monitor id=" + id + " is already defined!"); } // end if if (root_h.hasAttribute("parent")) { // find our parent String parent_id = cfg.getAttribute("parent"); parent = (SecurityMonitor)(known_monitors.get(parent_id)); if (parent==null) { // no parent! that's bogus! logger.fatal("parent security monitor with id=" + parent_id + " does not exist!"); throw new ConfigException("parent security monitor with id=" + parent_id + " does not exist!"); } // end if int my_scope = parent.getScopeInfo().getScope(); int my_offset = DEFAULT_SCOPE_OFFSET; if (root_h.hasAttribute("offset")) { // get the offset value and compare it Integer tmp = root_h.getAttributeInt("offset"); if (tmp==null) { // the offset was not an integer value - bye now! logger.fatal("offset= value was not an integer"); throw new ConfigException("offset= attribute of must be an integer"); } // end if my_offset = tmp.intValue(); if (my_offset<1) { // the offset must be greater than or equal to 1! logger.fatal("offset= value (" + my_offset + ") was out of range"); throw new ConfigException("offset= attribute of must be >= 1"); } // end if } // end if my_scope += my_offset; if (!(ScopeInfo.isValidScope(my_scope))) { // resulting scope is out of range! logger.fatal("scope for id=" + id + " comes out to " + my_scope + ", and that's not in range"); throw new ConfigException("scope for security monitor id=" + id + " is out of range!"); } // end if // allocate a scope info object with the new scope scope = new ScopeInfo(my_scope); } // end if else { // this must be the root security monitor! if (root_monitor!=null) { // but we already have a root - can't be two roots! logger.fatal("trying to define root security monitor but we already have one"); throw new ConfigException("root security monitor is already defined!"); } // end if // we are the root security monitor...we live at scope 0, our parent is the primordial monitor set_root_monitor = true; scope = new ScopeInfo(0); parent = PrimordialSecurityMonitor.get(); } // end else // get the defined roles Element sect = root_h.getSubElement("defined-roles"); NodeList nl; int i; if (sect!=null) { // we need to define some roles here... HashMap tmp_sym_to_role = new HashMap(); HashMap tmp_level_to_role = new HashMap(); nl = sect.getChildNodes(); for (i=0; i Node n = nl.item(i); if ((n.getNodeType()==Node.ELEMENT_NODE) && (n.getNodeName().equals("role"))) { // create the role and add it to the temporary Role r = createRole((Element)n); tmp_sym_to_role.put(r.getSymbol(),r); tmp_level_to_role.put(new Integer(r.getLevel()),r); } // end if } // end for if (tmp_sym_to_role.size()>0) { // save these off as unmodifiable maps sym_to_role = Collections.unmodifiableMap(tmp_sym_to_role); level_to_role = Collections.unmodifiableMap(tmp_level_to_role); } // end if else { // nothing defined here! sym_to_role = Collections.EMPTY_MAP; level_to_role = Collections.EMPTY_MAP; } // end else } // end if else { // I guess we don't define any roles! sym_to_role = Collections.EMPTY_MAP; level_to_role = Collections.EMPTY_MAP; } // end else // since lists may indirectly define default roles and permissions, create storage space for them HashMap tmp_default_roles = new HashMap(); HashMap tmp_static_permissions = new HashMap(); HashSet tmp_dynamic_permissions = new HashSet(); // get the defined role lists sect = root_h.getSubElement("defined-lists"); if (sect!=null) { // we need to define some role lists here! HashMap tmp_lists = new HashMap(); nl = sect.getChildNodes(); for (i=0; i Node n = nl.item(i); if ((n.getNodeType()==Node.ELEMENT_NODE) && (n.getNodeName().equals("list"))) { // create the role list and add it to the temporary map // but first, get the ID DOMElementHelper hn = new DOMElementHelper((Element)n); String list_id; if (hn.hasAttribute("id")) list_id = id + "." + hn.getElement().getAttribute("id"); else { // no id= attribute - can't do anything with this logger.fatal(" element found with no id= attribute!"); throw new ConfigException("no id= attribute on defined element",hn.getElement()); } // end else // now actually build the list and insert it List rlist = buildList(hn.getElement(),list_id,tmp_default_roles,tmp_static_permissions, tmp_dynamic_permissions); tmp_lists.put(list_id,rlist); } // end if } // end for if (tmp_lists.size()>0) lists = Collections.unmodifiableMap(tmp_lists); else lists = Collections.EMPTY_MAP; } // end if else // no lists defined here! lists = Collections.EMPTY_MAP; // Get the additional defined default roles. sect = root_h.getSubElement("defaults"); if (sect!=null) { // get the nodes in the defaults section nl = sect.getChildNodes(); for (i=0; i Node n = nl.item(i); if ((n.getNodeType()==Node.ELEMENT_NODE) && (n.getNodeName().equals("default"))) processDefault((Element)n,tmp_default_roles); } // end for } // end if // else no more defined defaults // Since that's it for the defaults, freeze the defaults list. if (tmp_default_roles.size()>0) default_roles = Collections.unmodifiableMap(tmp_default_roles); else default_roles = Collections.EMPTY_MAP; // Get the defined permissions. sect = root_h.getSubElement("permissions"); if (sect!=null) { // get the nodes in the permissions section nl = sect.getChildNodes(); for (i=0; i Node n = nl.item(i); if ((n.getNodeType()==Node.ELEMENT_NODE) && (n.getNodeName().equals("permission"))) processPermission((Element)n,tmp_static_permissions,tmp_dynamic_permissions); } // end for } // end if // else no more defined permissions // That's now it for the permissions, so freeze those elements. if (tmp_static_permissions.size()>0) static_permissions = Collections.unmodifiableMap(tmp_static_permissions); else static_permissions = Collections.EMPTY_MAP; if (tmp_dynamic_permissions.size()>0) dynamic_permissions = Collections.unmodifiableSet(tmp_dynamic_permissions); else dynamic_permissions = Collections.EMPTY_SET; // Finish up by adding ourselves to the known monitors list. known_monitors.put(id,this); if (set_root_monitor) root_monitor = this; } // end constructor /*-------------------------------------------------------------------------------- * Internal operations *-------------------------------------------------------------------------------- */ private Role createRole(Element e) throws ConfigException { String symbol, text; int level; DOMElementHelper h = new DOMElementHelper(e); if (h.hasAttribute("id")) symbol = id + "." + e.getAttribute("id"); // symbols get automagically scoped else { // no role defined logger.fatal(" defined with no id= attribute!"); throw new ConfigException("no id= attribute for a ",e); } // end else if (h.hasAttribute("value")) { // get the value and parse it out String value_str = e.getAttribute("value").trim().toUpperCase(); if (value_str.equals("LMIN")) level = scope.getLowBandLow(); else if (value_str.equals("LMAX")) level = scope.getLowBandHigh(); else if (value_str.equals("HMIN")) level = scope.getHighBandLow(); else if (value_str.equals("HMAX")) level = scope.getHighBandHigh(); else if ( value_str.startsWith("L+") || value_str.startsWith("L-") || value_str.startsWith("H+") || value_str.startsWith("H-")) { // take the characters following the 2-character prefix and convert them to an integer int offset; try { // convert the value and make sure it's not less than 0 offset = Integer.parseInt(value_str.substring(2)); if (offset<0) { // don't want it less than zero here! logger.fatal("offset value " + offset + " was out of range"); throw new ConfigException("offset value= attribute for was out of range",e); } // end if } // end try catch (NumberFormatException nfe) { // not a numeric offset value logger.fatal("offset value \"" + value_str + "\" was not numeric"); throw new ConfigException("offset value= attribute for was not properly numeric",e); } // end catch if (value_str.charAt(1)=='-') offset = -offset; // compute as negative offset try { // now use the scope to compute the level! level = scope.getLevel((value_str.charAt(0)=='H'),offset); } // end try catch (IllegalArgumentException iae) { // we landed with a value outside the scope! logger.fatal("offset value \"" + value_str + "\" was not in the scope"); throw new ConfigException("offset value= attribute for was not within the scope",e); } // end catch } // end else if else { // just a straight numeric level try { // parse it out and give it a scope check level = Integer.parseInt(value_str); if (!(scope.isInScope(level))) { // not in the right scope - can't help you, pal! logger.fatal("level value \"" + level + "\" was not in the scope"); throw new ConfigException("level value= attribute for was not within the scope",e); } // end if } // end try catch (NumberFormatException nfe) { // the level was not numeric logger.fatal("level value \"" + value_str + "\" was not numeric"); throw new ConfigException("level value= attribute for was not properly numeric",e); } // end catch } // end else } // end if else { // no value defined for this role! logger.fatal(" defined with no value= attribute!"); throw new ConfigException("no value= attribute for a ",e); } // end else // Get the text; default to the symbol name if it doesn't exist. text = h.getElementText(); if (text==null) text = symbol; // create the resulting role! return Role.create(level,text,symbol); } // end createRole private List buildList(Element elem, String listid, Map defaultrole, Map static_perm, Set dynamic_perm) throws ConfigException { DOMElementHelper h = new DOMElementHelper(elem); Element perm = h.getSubElement("permission"); if (perm!=null) { // there's a permission associated with this list, find out what it is DOMElementHelper ph = new DOMElementHelper(perm); if (ph.hasAttribute("role")) { // look up the role and make sure it corresponds to one we know Role role = this.getRole(perm.getAttribute("role")); if (role==null) { // role not present! logger.fatal("list role (" + perm.getAttribute("role") + ") not defined"); throw new ConfigException(" inside of did not use defined role!",perm); } // end if // create a new StaticPermission and add it to the mapping StaticPermission sp = new StaticPermission(role,ph.getElementText()); static_perm.put(listid,sp); } // end if else // this is a dynamic permission, add it to the set dynamic_perm.add(listid); } // end if // else there's no problem NodeList nl = elem.getChildNodes(); ArrayList rc = new ArrayList(nl.getLength()); boolean have_default = false; for (int i=0; i role (" + itmh.getElement().getAttribute("role") + ") not defined"); throw new ConfigException(" inside of did not use defined role!", itmh.getElement()); } // end if } // end if else { // no attribute present logger.fatal(" defined with no role= attribute!"); throw new ConfigException("no role= attribute for a list ",itmh.getElement()); } // end else rc.add(r); // add element to defining list if (itmh.hasAttribute("default")) { // this is a default item... if (have_default) { // but there can't be two defaults! logger.fatal("duplicate default= attributes in list nodes!"); throw new ConfigException("duplicate default= attribute in list ",itmh.getElement()); } // end if else { // we have a default for the list now! defaultrole.put(listid,r); have_default = true; } // end else } // end if } // end if } // end for // Final prep on the list prior to returning it. Collections.sort(rc); rc.trimToSize(); return Collections.unmodifiableList(rc); } // end buildlist private void processDefault(Element elem, Map defaultrole) throws ConfigException { // Start by getting the default ID. DOMElementHelper h = new DOMElementHelper(elem); String def_id = null; if (h.hasAttribute("id")) def_id = id + "." + elem.getAttribute("id"); else { // no id defined! logger.fatal(" defined with no id= attribute!"); throw new ConfigException("no id= attribute for a ",elem); } // end else Role r = null; if (h.hasAttribute("role")) { // get the role associated with the item r = this.getRole(elem.getAttribute("role")); if (r==null) { // no role found - this is an error! logger.fatal(" role (" + elem.getAttribute("role") + ") not defined"); throw new ConfigException(" did not use defined role!",elem); } // end if } // end if else { // no role defined! logger.fatal(" defined with no id= attribute!"); throw new ConfigException("no id= attribute for a ",elem); } // end else defaultrole.put(def_id,r); } // end processDefault private void processPermission(Element elem, Map static_perm, Set dynamic_perm) throws ConfigException { // Start by getting the permission ID. DOMElementHelper h = new DOMElementHelper(elem); String perm_id = null; if (h.hasAttribute("id")) perm_id = id + "." + elem.getAttribute("id"); else { // no id defined! logger.fatal(" defined with no id= attribute!"); throw new ConfigException("no id= attribute for a ",elem); } // end else if (h.hasAttribute("role")) { // this is a static permission; try and get the associated role Role r = this.getRole(elem.getAttribute("role")); if (r==null) { // no role found - this is an error! logger.fatal(" role (" + elem.getAttribute("role") + ") not defined"); throw new ConfigException(" did not use defined role!",elem); } // end if // create static permission and add it StaticPermission sp = new StaticPermission(r,h.getElementText()); static_perm.put(perm_id,sp); } // end if else // this is a dynamic permission; just add to our set dynamic_perm.add(perm_id); } // end processPermission /*-------------------------------------------------------------------------------- * Implementations from interface SecurityMonitor *-------------------------------------------------------------------------------- */ public boolean testPermission(String symbol, int level, String errormsg) throws AccessError { if (symbol==null) throw new NullPointerException("testPermission() got null symbol"); StaticPermission sp = (StaticPermission)(static_permissions.get(symbol)); if (sp==null) { // permission not found here - NOTE! Do not call to parent unless we are at the root level, as // permission tests always follow the DYNAMIC chain, not the static one! if (scope.getScope()==0) return parent.testPermission(symbol,level,errormsg); else return false; } // end if sp.test(level,errormsg); // will throw AccessError on failure return true; } // end testPermission public boolean testPermission(String symbol, int level) { if (symbol==null) throw new NullPointerException("testPermission() got null symbol"); StaticPermission sp = (StaticPermission)(static_permissions.get(symbol)); if (sp==null) { // permission not found here - NOTE! Do not call to parent unless we are at the root level, as // permission tests always follow the DYNAMIC chain, not the static one! if (scope.getScope()==0) return parent.testPermission(symbol,level); else return false; } // end if return sp.test(level); } // end testPermission public boolean permissionDefined(String symbol, boolean no_follow) { if (symbol==null) throw new NullPointerException("permissionDefined() got null symbol"); if (static_permissions.containsKey(symbol) || dynamic_permissions.contains(symbol)) return true; if (no_follow) return false; return parent.permissionDefined(symbol,false); } // end permissionDefined public List getRoleList(String symbol) { if (symbol==null) throw new NullPointerException("getRoleList() got null symbol"); List rc = (List)(lists.get(symbol)); if (rc==null) rc = parent.getRoleList(symbol); return rc; } // end getRoleList public Role getRole(String symbol) { if (symbol==null) throw new NullPointerException("getRole() got null symbol"); Role rc = (Role)(sym_to_role.get(symbol)); if (rc==null) rc = parent.getRole(symbol); return rc; } // end getRole public Role getRoleForLevel(int level) { Role rc = (Role)(level_to_role.get(new Integer(level))); if (rc==null) rc = parent.getRoleForLevel(level); return rc; } // end getRoleForLevel public Role getDefaultRole(String symbol) { if (symbol==null) throw new NullPointerException("getRole() got null symbol"); Role rc = (Role)(default_roles.get(symbol)); if (rc==null) rc = parent.getDefaultRole(symbol); return rc; } // end getDefaultRole public ScopeInfo getScopeInfo() { return scope; } // end getScopeInfo public String getID() { return id; } // end getID } // end class StaticSecurityMonitor