/*
* Weblounge: Web Content Management System
* Copyright (c) 2003 - 2011 The Weblounge Team
* http://entwinemedia.com/weblounge
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ch.entwine.weblounge.common.impl.security;
import ch.entwine.weblounge.common.impl.language.LocalizableContent;
import ch.entwine.weblounge.common.language.Language;
import ch.entwine.weblounge.common.security.Authority;
import ch.entwine.weblounge.common.security.Role;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
/**
* A role models properties that can be applied to persons or groups of persons.
* If a person has a certain role, then he/she usually is allowed to do
* something others aren't allowed to do.
*/
public class RoleImpl extends LocalizableContent<String> implements Role {
/** Role identifier */
protected String identifier = null;
/** The role context */
protected String context = null;
/** Roles that are extended by this role */
protected Set<Role> ancestors = null;
/**
* Creates a new role from the parameter context::id.
*
* @param role
* the role
*/
public RoleImpl(String role) {
assert role != null;
this.context = extractContext(role);
this.identifier = extractIdentifier(role);
this.ancestors = new HashSet<Role>();
}
/**
* Creates a role in the given context with the specified role identifier.
*
* @param context
* the role context
* @param identifier
* the role identifier
*/
public RoleImpl(String context, String identifier) {
this.context = context;
this.identifier = identifier;
this.ancestors = new HashSet<Role>();
}
/**
* Creates a role in the given context with the specified role identifier that
* extends the <code>ancestor</code> role.
*
* @param context
* the role context
* @param identifier
* the role identifier
*/
public RoleImpl(String context, String identifier, Role ancestor) {
this(context, identifier);
extend(ancestor);
}
/**
* Returns the role identifier.
*
* @return the identifier
*/
public String getIdentifier() {
return identifier;
}
/**
* {@inheritDoc}
*
* @see java.security.Principal#getName()
*/
public String getName() {
return identifier;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Role#extend(ch.entwine.weblounge.common.security.Role)
*/
public void extend(Role ancestor) {
ancestors.add(ancestor);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Role#isExtensionOf(ch.entwine.weblounge.common.security.Role)
*/
public boolean isExtensionOf(Role ancestor) {
if (ancestors.contains(ancestor))
return true;
// Obviously, there is no direct extension and we never looked
// up this ancestor. Do an indirect ancestor search
for (Role role : ancestors) {
if (role.isExtensionOf(ancestor)) {
// Cache this lookup
extend(ancestor);
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Role#getExtendedRoles()
*/
public Role[] getExtendedRoles() {
return ancestors.toArray(new Role[ancestors.size()]);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Role#getClosure()
*/
public Role[] getClosure() {
HashSet<Role> roles = new HashSet<Role>();
Stack<Role> stack = new Stack<Role>();
stack.push(this);
while (!stack.empty()) {
Role r = stack.pop();
roles.add(r);
for (Role extendedRole : r.getExtendedRoles()) {
if (!stack.contains(extendedRole))
stack.push(extendedRole);
}
}
return roles.toArray(new Role[ancestors.size()]);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Role#getContext()
*/
public String getContext() {
return context;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Authority#getAuthorityType()
*/
public String getAuthorityType() {
return Role.class.getName();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Authority#getAuthorityId()
*/
public String getAuthorityId() {
return context + ":" + identifier;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Authority#isAuthorizedBy(ch.entwine.weblounge.common.security.Authority)
*/
public boolean isAuthorizedBy(Authority authority) {
if (authority != null && authority instanceof Role) {
Role r = (Role) authority;
return this.equals(r) || this.isExtensionOf(r);
} else if (authority != null && authority.getAuthorityType().equals(Role.class.getName())) {
String roleId = authority.getAuthorityId();
Role r = new RoleImpl(roleId);
return equals(r) || isExtensionOf(r);
}
return false;
}
/**
* Sets the role name in the given language.
*
* @param name
* the role name
* @param language
* the language
*/
public void setName(String name, Language language) {
if (originalLanguage == null)
setOriginalLanguage(language);
content.put(language, name);
}
/**
* Returns the context for a role identifier. For example, given the role
* <code>system:editor</code>, this method will return <code>editor</code>.
* <p>
* <b>Note:</b> This method will throw a <code>IllegalArgumentException</code>
* if the role string does not follow the format <code>context:id</code>.
*
* @param role
* the role
* @return the context part of the role
*/
protected static String extractContext(String role) {
assert role != null;
int divider = role.indexOf(':');
if (divider <= 0 || divider >= (role.length() - 1))
throw new IllegalArgumentException("Role must be of the form 'context:id'!");
return role.substring(0, divider);
}
/**
* Returns the context for a role identifier. For example, given the role
* <code>system:editor</code>, this method will return <code>editor</code>.
* <p>
* <b>Note:</b> This method will throw a <code>IllegalArgumentException</code>
* if the role string does not follow the format <code>context:id</code>.
*
* @param role
* the role
* @return the context part of the role
*/
protected static String extractIdentifier(String role) {
assert role != null;
int divider = role.indexOf(':');
if (divider <= 0 || divider >= (role.length() - 1))
throw new IllegalArgumentException("Role must be of the form 'context:id'!");
return role.substring(divider + 1);
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return context.hashCode() | identifier.hashCode() >> 16;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj != null && obj instanceof Role) {
Role r = (Role) obj;
return r.getIdentifier().equals(identifier) && r.getContext().equals(context);
}
return false;
}
/**
* Returns the string representation of this role in the form
* <code><context>:<identifier></code>.
*
* @return the role's string representation
*/
public String toString() {
return context + ":" + identifier;
}
}