package org.ripla.useradmin.admin;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Iterator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.PreferencesService;
import org.osgi.service.useradmin.Authorization;
import org.osgi.service.useradmin.Group;
import org.osgi.service.useradmin.Role;
import org.osgi.service.useradmin.User;
import org.osgi.service.useradmin.UserAdmin;
import org.osgi.service.useradmin.UserAdminEvent;
import org.osgi.service.useradmin.UserAdminPermission;
import org.ripla.useradmin.interfaces.IUserAdminStore;
import org.ripla.useradmin.internal.UserAdminEventProducer;
import org.ripla.useradmin.internal.UserAdminStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provider of the OSGi <code>UserAdmin</code> service.
*
* <p>
* This interface is used to manage a database of named <tt>Role</tt> objects,
* which can be used for authentication and authorization purposes.
* </p>
* <p>
* This version of the User Admin service defines two types of <tt>Role</tt>
* objects: "User" and "Group". Each type of role is represented by an
* <tt>int</tt> constant and an interface. The range of positive integers is
* reserved for new types of roles that may be added in the future. When
* defining proprietary role types, negative constant values must be used.
*
* </p>
* <p>
* Every role has a name and a type.
*
* </p>
* <p>
* A {@link User} object can be configured with credentials (e.g., a password)
* and properties (e.g., a street address, phone number, etc.).
* </p>
* <p>
* A {@link Group} object represents an aggregation of {@link User} and
* {@link Group} objects. In other words, the members of a <tt>Group</tt> object
* are roles themselves.
* </p>
* <p>
* Every User Admin service manages and maintains its own namespace of
* <tt>Role</tt> objects, in which each <tt>Role</tt> object has a unique name.
* </p>
*
* <p>
* This implementation of the <code>UserAdmin</code> service uses the OSGi
* preferences to persist user and authorization information.
* </p>
* <p>
* Subclasses may override {@link #createUserAdminStore()} to provide their own
* store for persisting user and authorization information.
* </p>
*
* @author Luthiger
* @see http
* ://eclipsesrc.appspot.com/jsrcs/org.eclipse.equinox.internal.useradmin
* .package.html
*/
public class RiplaUserAdmin implements UserAdmin { // NOPMD by Luthiger
private static final Logger LOG = LoggerFactory
.getLogger(RiplaUserAdmin.class);
protected transient final Collection<Role> roles;
private transient final Collection<User> users;
protected transient boolean alive;
private transient UserAdminPermission adminPermission;
private transient IUserAdminStore userAdminStore;
private transient PreferencesService preferences;
private transient BundleContext context;
private transient UserAdminEventProducer eventProducer;
/**
* RiplaUserAdmin constructor.
*
* @throws Exception
*/
public RiplaUserAdmin() throws Exception { // NOPMD by Luthiger
roles = new ArrayList<Role>();
users = new ArrayList<User>();
alive = true;
}
/**
* Creates an instance of the class that is responsible for storing the
* entities managed by the user admin.<br />
* Subclasses may override to provide their own store.
*
* @return {@link IUserAdminStore}
*/
protected IUserAdminStore createUserAdminStore() {
return new UserAdminStore(preferences, this);
}
/**
* Retrieve the configured user admin store.
*
* @return {@link IUserAdminStore}
* @throws BackingStoreException
*/
public IUserAdminStore getUserAdminStore() throws BackingStoreException {
// we do lazy initialization
if (userAdminStore == null) {
if (preferences == null) {
throw new BackingStoreException(
"No preferences set! Cant't create an instance of UserAdminStore!");
}
try {
userAdminStore = createUserAdminStore();
userAdminStore.initialize();
}
catch (final BackingStoreException exc) {
LOG.error("Could not initialize the Ripla user admin store!",
exc);
throw exc;
}
}
return userAdminStore;
}
/**
* Needed for internal uses.
*
* @return {@link UserAdminEventProducer} this admin service's event
* producer
*/
public UserAdminEventProducer getEventProducer() {
return eventProducer;
}
/*
* (non-Javadoc)
*
* @see org.osgi.service.useradmin.UserAdmin#createRole(java.lang.String,
* int)
*/
@Override
public Role createRole(final String inName, final int inType) {
checkAlive();
checkAdminPermission();
if (inName == null) {
throw new IllegalArgumentException(
"The user name must not be null!");
}
if (inType != Role.GROUP && inType != Role.USER) {
throw new IllegalArgumentException(
"The type of the new role is illegal!");
}
// if the role already exists, return null
if (getRole(inName) != null) {
return null;
}
synchronized (this) {
return createRole(inName, inType, true);
}
}
/**
* Creates a Role object with the given name and of the given type. <br />
* Needed for internal uses.
*
* @param inName
* String The <code>name</code> of the <code>Role</code> object
* to create.
* @param inType
* int The type of the <code>Role</code> object to create. Must
* be either a {@link Role.USER} type or {@link Role.GROUP} type.
* @param inStore
* boolean <code>true</code> if the newly created
* <code>Role</code> instance has to be stored.
* @return {@link Role} The newly created <code>Role</code> object, or
* <code>null</code> if a role with the given name already exists.
*/
public Role createRole(final String inName, final int inType,
final boolean inStore) {
RiplaRole out = null;
if (inType == Role.ROLE) {
out = new RiplaRole(inName, this);
} else if (inType == Role.USER) {
out = new RiplaUser(inName, this);
} else if (inType == Role.GROUP) {
out = new RiplaGroup(inName, this);
} else {
return null;
}
if (inStore) {
try {
userAdminStore.addRole(out);
}
catch (final BackingStoreException exc) {
return null;
}
if (eventProducer != null) {
eventProducer.generateEvent(UserAdminEvent.ROLE_CREATED, out);
}
}
if (inType == Role.USER || inType == Role.GROUP) {
users.add((RiplaUser) out);
}
roles.add(out);
return out;
}
/*
* (non-Javadoc)
*
* @see org.osgi.service.useradmin.UserAdmin#removeRole(java.lang.String)
*/
@Override
public boolean removeRole(final String inName) {
checkAlive();
checkAdminPermission();
if (inName.equals(Role.USER_ANYONE)) {
// silently ignore
return true;
}
synchronized (this) {
RiplaRole lRole = (RiplaRole) getRole(inName);
if (lRole != null) {
try {
userAdminStore.removeRole(lRole);
}
catch (final BackingStoreException exc) {
return false;
}
users.remove(lRole);
roles.remove(lRole);
lRole.destroy();
if (eventProducer != null) {
eventProducer.generateEvent(UserAdminEvent.ROLE_REMOVED,
lRole);
}
lRole = null; // NOPMD by Luthiger on 07.09.12 00:21
return true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see org.osgi.service.useradmin.UserAdmin#getRole(java.lang.String)
*/
@Override
public Role getRole(final String inName) {
checkAlive();
if (inName == null) {
return null;
}
synchronized (this) {
final Iterator<Role> lRoles = roles.iterator();
while (lRoles.hasNext()) {
final Role outRole = lRoles.next();
if (outRole.getName().equals(inName)) {
return outRole;
}
}
return null;
}
}
/*
* (non-Javadoc)
*
* @see org.osgi.service.useradmin.UserAdmin#getRoles(java.lang.String)
*/
@SuppressWarnings("unchecked")
@Override
public Role[] getRoles(final String inFilter) throws InvalidSyntaxException {
checkAlive();
Collection<Role> outRoles;
synchronized (this) {
if (inFilter == null) {
outRoles = roles;
} else {
final Filter lFilter = context.createFilter(inFilter);
outRoles = new ArrayList<Role>();
for (final Role lRole : roles) {
if (lFilter.match(lRole.getProperties())) {
outRoles.add(lRole);
}
}
}
final int lSize = outRoles.size();
if (lSize == 0) {
return null;
}
final Role[] out = new Role[lSize];
int i = 0; // NOPMD by Luthiger on 07.09.12 00:21
for (final Role lRole : outRoles) {
out[i++] = lRole;
}
return out;
}
}
/*
* (non-Javadoc)
*
* @see org.osgi.service.useradmin.UserAdmin#getUser(java.lang.String,
* java.lang.String)
*/
@SuppressWarnings("rawtypes")
@Override
public User getUser(final String inKey, final String inValue) {
checkAlive();
if (inKey == null) {
return null;
}
User lUser;
User outUser = null;
Dictionary lProperties;
String lKeyValue;
synchronized (this) {
final Iterator<User> lUsers = users.iterator();
while (lUsers.hasNext()) {
lUser = lUsers.next();
lProperties = lUser.getProperties();
lKeyValue = (String) lProperties.get(inKey);
if (lKeyValue != null && lKeyValue.equals(inValue)) {
if (outUser != null) {
return null;
}
outUser = lUser;
}
}
return outUser;
}
}
/*
* (non-Javadoc)
*
* @see
* org.osgi.service.useradmin.UserAdmin#getAuthorization(org.osgi.service
* .useradmin.User)
*/
@Override
public Authorization getAuthorization(final User inUser) {
checkAlive();
return new RiplaAuthorization((RiplaUser) inUser, this);
}
/**
* Destroys this user admin instance and releases the resources.<br />
* Needed for internal uses.
*/
protected synchronized void destroy() { // NOPMD by Luthiger on 07.09.12
// 00:21
alive = false;
eventProducer.close();
userAdminStore.destroy();
}
/**
* Needed for internal uses.
*/
protected void checkAlive() {
if (!alive) {
throw new IllegalStateException(
"The user admin instance has gone out of operation!");
}
}
/**
* Needed for internal uses.
*/
protected void checkAdminPermission() {
final SecurityManager sm = System.getSecurityManager(); // NOPMD
if (sm != null) {
if (adminPermission == null) {
adminPermission = new UserAdminPermission(
UserAdminPermission.ADMIN, null);
}
sm.checkPermission(adminPermission);
}
}
/**
* Needed for internal uses.
*
* @param inCredential
* String
*/
public void checkGetCredentialPermission(final String inCredential) {
final SecurityManager sm = System.getSecurityManager(); // NOPMD
if (sm != null) {
sm.checkPermission(new UserAdminPermission(inCredential,
UserAdminPermission.GET_CREDENTIAL));
}
}
/**
* Needed for internal uses.
*
* @param inCredential
* String
*/
public void checkChangeCredentialPermission(final String inCredential) {
final SecurityManager sm = System.getSecurityManager(); // NOPMD
if (sm != null) {
sm.checkPermission(new UserAdminPermission(inCredential,
UserAdminPermission.CHANGE_CREDENTIAL));
}
}
/**
* Needed for internal uses.
*
* @param inProperty
* String
*/
public void checkChangePropertyPermission(final String inProperty) {
final SecurityManager sm = System.getSecurityManager(); // NOPMD
if (sm != null) {
sm.checkPermission(new UserAdminPermission(inProperty,
UserAdminPermission.CHANGE_PROPERTY));
}
}
// --- OSGi binding and activation methods ---
public void setPreferences(final PreferencesService inPreferences) {
preferences = inPreferences;
if (userAdminStore == null) {
try {
userAdminStore = createUserAdminStore();
userAdminStore.initialize();
}
catch (final BackingStoreException exc) {
LOG.error("Could not initialize the Ripla user admin store!",
exc);
}
}
}
public void unsetPreferences(final PreferencesService inPreferences) {
preferences = null; // NOPMD by Luthiger on 07.09.12 00:22
}
@SuppressWarnings("unchecked")
public void activate(final ComponentContext inComponentContext,
final BundleContext inContext) {
context = inContext;
if (eventProducer == null) {
eventProducer = new UserAdminEventProducer(
inComponentContext.getServiceReference(), context);
}
}
public void deactivate(final BundleContext inContext) {
context = null; // NOPMD by Luthiger on 07.09.12 00:22
eventProducer = null; // NOPMD by Luthiger on 07.09.12 00:22
}
}