blob: c25e291592eb3ba3ad2a26fcc73545199111d873 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.internal.useradmin;
import java.util.Enumeration;
import java.util.Vector;
import org.osgi.service.prefs.BackingStoreException;
/**
* A named grouping of roles.
* <p>
* Whether or not a given authorization context implies a Group role
* depends on the members of that role.
* <p>
* A Group role can have two kinds of member roles: <i>basic</i> and
* <i>required</i>.
* A Group role is implied by an authorization context if all of
* its required member roles are implied
* and at least one of its basic member roles is implied.
* <p>
* A Group role must contain at least one basic member role in order
* to be implied. In other words, a Group without any basic member
* roles is never implied by any authorization context.
* <p>
* A User role always implies itself.
* <p>
* No loop detection is performed when adding members to groups, which
* means that it is possible to create circular implications. Loop
* detection is instead done when roles are checked. The semantics is that
* if a role depends on itself (i.e., there is an implication loop), the
* role is not implied.
* <p>
* The rule that a group must have at least one basic member to be implied
* is motivated by the following example:
*
* <pre>
* group foo
* required members: marketing
* basic members: alice, bob
* </pre>
*
* Privileged operations that require membership in "foo" can be performed
* only by alice and bob, who are in marketing.
* <p>
* If alice and bob ever transfer to a different department, anybody in
* marketing will be able to assume the "foo" role, which certainly must be
* prevented.
* Requiring that "foo" (or any Group role for that matter) must have at least
* one basic member accomplishes that.
* <p>
* However, this would make it impossible for a group to be implied by just
* its required members. An example where this implication might be useful
* is the following declaration: "Any citizen who is an adult is allowed to
* vote."
* An intuitive configuration of "voter" would be:
*
* <pre>
* group voter
* required members: citizen, adult
* basic members:
* </pre>
*
* However, according to the above rule, the "voter" role could never be
* assumed by anybody, since it lacks any basic members.
* In order to address this deficiency a predefined role named
* "user.anyone" can be specified, which is always implied.
* The desired implication of the "voter" group can then be achieved by
* specifying "user.anyone" as its basic member, as follows:
*
* <pre>
* group voter
* required members: citizen, adult
* basic members: user.anyone
* </pre>
*/
public class Group extends User implements org.osgi.service.useradmin.Group {
protected Vector requiredMembers;
protected Vector basicMembers;
protected Group(String name, UserAdmin useradmin) {
super(name, useradmin);
this.useradmin = useradmin;
basicMembers = new Vector();
requiredMembers = new Vector();
}
/**
* Adds the specified role as a basic member to this Group.
*
* @param role The role to add as a basic member.
*
* @return <code>true</code> if the given role could be added as a basic
* member,
* and <code>false</code> if this Group already contains a role whose name
* matches that of the specified role.
*
* @throws SecurityException If a security manager exists and the caller
* does not have the <tt>UserAdminPermission</tt> with name <tt>admin</tt>.
*/
public boolean addMember(org.osgi.service.useradmin.Role role) {
useradmin.checkAlive();
useradmin.checkAdminPermission();
//only need to check for null for the public methods
if (role == null) {
return (false);
}
synchronized (useradmin) {
if (basicMembers.contains(role)) {
return (false);
}
return (addMember(role, true));
}
}
// When we are loading from storage this method is called directly. We
// do not want to write to storage when we are loading form storage.
protected boolean addMember(org.osgi.service.useradmin.Role role, boolean store) {
((org.eclipse.equinox.internal.useradmin.Role) role).addImpliedRole(this);
if (store) {
try {
useradmin.userAdminStore.addMember(this, (org.eclipse.equinox.internal.useradmin.Role) role);
} catch (BackingStoreException ex) {
return (false);
}
}
basicMembers.addElement(role);
return (true);
}
/**
* Adds the specified role as a required member to this Group.
*
* @param role The role to add as a required member.
*
* @return <code>true</code> if the given role could be added as a required
* member, and <code>false</code> if this Group already contains a role
* whose name matches that of the specified role.
*
* @throws SecurityException If a security manager exists and the caller
* does not have the <tt>UserAdminPermission</tt> with name <tt>admin</tt>.
*/
public boolean addRequiredMember(org.osgi.service.useradmin.Role role) {
useradmin.checkAlive();
useradmin.checkAdminPermission();
if (role == null) {
return (false);
}
synchronized (useradmin) {
if (requiredMembers.contains(role)) {
return (false);
}
return (addRequiredMember(role, true));
}
}
protected boolean addRequiredMember(org.osgi.service.useradmin.Role role, boolean store) {
((org.eclipse.equinox.internal.useradmin.Role) role).addImpliedRole(this);
if (store) {
try {
useradmin.userAdminStore.addRequiredMember(this, (org.eclipse.equinox.internal.useradmin.Role) role);
} catch (BackingStoreException ex) {
return (false);
}
}
requiredMembers.addElement(role);
return (true);
}
/**
* Removes the specified role from this Group.
*
* @param role The role to remove from this Group.
*
* @return <code>true</code> if the role could be removed,
* otherwise <code>false</code>.
*
* @throws SecurityException If a security manager exists and the caller
* does not have the <tt>UserAdminPermission</tt> with name <tt>admin</tt>.
*/
public boolean removeMember(org.osgi.service.useradmin.Role role) {
useradmin.checkAlive();
useradmin.checkAdminPermission();
if (role == null) {
return (false);
}
synchronized (useradmin) {
try {
useradmin.userAdminStore.removeMember(this, (org.eclipse.equinox.internal.useradmin.Role) role);
} catch (BackingStoreException ex) {
return (false);
}
//The role keeps track of which groups it is a member of so it can remove itself from
//the group if it is deleted. In this case, this group is being removed from the role's
//list.
((org.eclipse.equinox.internal.useradmin.Role) role).removeImpliedRole(this);
// We don't know if the Role to be removed is a basic orrequired member, or both. We
// simply try to remove it from both.
boolean removeRequired = requiredMembers.removeElement(role);
boolean removeBasic = basicMembers.removeElement(role);
return (removeRequired || removeBasic);
}
}
/**
* Gets the basic members of this Group.
*
* @return The basic members of this Group, or <code>null</code> if this
* Group does not contain any basic members.
*/
public org.osgi.service.useradmin.Role[] getMembers() {
useradmin.checkAlive();
synchronized (useradmin) {
if (basicMembers.isEmpty()) {
return (null);
}
Role[] roles = new Role[basicMembers.size()];
basicMembers.copyInto(roles);
return (roles);
}
}
/**
* Gets the required members of this Group.
*
* @return The required members of this Group, or <code>null</code> if this
* Group does not contain any required members.
*/
public org.osgi.service.useradmin.Role[] getRequiredMembers() {
useradmin.checkAlive();
synchronized (useradmin) {
if (requiredMembers.isEmpty()) {
return (null);
}
Role[] roles = new Role[requiredMembers.size()];
requiredMembers.copyInto(roles);
return (roles);
}
}
/**
* Returns the type of this role.
*
* @return The role's type.
*/
public int getType() {
useradmin.checkAlive();
return (org.osgi.service.useradmin.Role.GROUP);
}
protected boolean isImpliedBy(Role role, Vector checkLoop) {
if (checkLoop.contains(name)) {
//we have a circular dependency
return (false);
}
if (name.equals(role.getName())) //A User always implies itself. A Group is a User.
{
return (true);
}
checkLoop.addElement(name);
Vector requiredCheckLoop = (Vector) checkLoop.clone();
Vector basicCheckLoop = (Vector) checkLoop.clone();
Enumeration e = requiredMembers.elements();
//check to see if we imply all of the 0 or more required roles
Role requiredRole;
while (e.hasMoreElements()) {
requiredRole = (Role) e.nextElement();
if (!requiredRole.isImpliedBy(role, requiredCheckLoop)) {
return (false);
}
}
//check to see if we imply any of the basic roles (there must be at least one)
e = basicMembers.elements();
Role basicRole;
while (e.hasMoreElements()) {
basicRole = (Role) e.nextElement();
if (basicRole.isImpliedBy(role, basicCheckLoop)) {
return (true);
}
}
return (false);
}
}