/*
* 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