diff options
author | jfogell | 2005-12-15 20:26:41 +0000 |
---|---|---|
committer | jfogell | 2005-12-15 20:26:41 +0000 |
commit | 66912d73ac981e71e819588f89f28fd0ec74049a (patch) | |
tree | f5ca7b4ee57a966e63d973e5d2e6e7de2ca6d55f /bundles/org.eclipse.equinox.useradmin/src | |
parent | 83fbd37f24c5aa8b0931b62d6da9cfb838970d62 (diff) | |
download | rt.equinox.bundles-66912d73ac981e71e819588f89f28fd0ec74049a.tar.gz rt.equinox.bundles-66912d73ac981e71e819588f89f28fd0ec74049a.tar.xz rt.equinox.bundles-66912d73ac981e71e819588f89f28fd0ec74049a.zip |
initial checkin for OSGi User Admin Service
Diffstat (limited to 'bundles/org.eclipse.equinox.useradmin/src')
13 files changed, 2098 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Activator.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Activator.java new file mode 100644 index 000000000..d29ce6efd --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Activator.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import java.util.Hashtable; +import org.osgi.framework.*; +import org.osgi.service.prefs.PreferencesService; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; + +/** This is the bundle activator for the UserAdmin bundle. + */ + +public class Activator implements BundleActivator, ServiceFactory, ServiceTrackerCustomizer { + /* + * ---------------------------------------------------------------------- + * BundleActivator Interface implementation + * ---------------------------------------------------------------------- + */ + + protected ServiceRegistration registration; + protected UserAdmin userAdmin; + protected static String userAdminClazz = "org.osgi.service.useradmin.UserAdmin"; //$NON-NLS-1$ + protected PreferencesService prefs; + protected BundleContext context; + protected ServiceTracker prefsTracker; + + /** Need to have a public default constructor so that the BundleActivator + * can be instantiated by Class.newInstance(). + */ + public Activator() { + } + + /** + * Required by BundleActivator Interface. + */ + public void start(BundleContext context) throws Exception { + this.context = context; + prefsTracker = new ServiceTracker(context, PreferencesService.class.getName(), this); + prefsTracker.open(); + } + + /** + * Required by BundleActivator Interface. + */ + public void stop(BundleContext context) throws Exception { + prefsTracker.close(); + registration.unregister(); + userAdmin.destroy(); + userAdmin = null; + } + + /** + * Register the UserAdmin service. + */ + protected void registerUserAdminService() throws Exception { + Hashtable properties = new Hashtable(7); + + properties.put(Constants.SERVICE_VENDOR, UserAdminMsg.Service_Vendor); + properties.put(Constants.SERVICE_DESCRIPTION, UserAdminMsg.OSGi_User_Admin_service_IBM_Implementation_3); + properties.put(Constants.SERVICE_PID, getClass().getName()); + + userAdmin = new UserAdmin(prefs, context); + registration = context.registerService(userAdminClazz, this, properties); + userAdmin.setServiceReference(registration.getReference()); + } + + public Object getService(Bundle bundle, ServiceRegistration registration) { + userAdmin.setServiceReference(registration.getReference()); + + return userAdmin; + } + + public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { + } + + public Object addingService(ServiceReference reference) { + if (prefs == null) { + prefs = (PreferencesService) context.getService(reference); + try { + registerUserAdminService(); + } catch (Exception ex) { + return null; + } + return prefs; + } + return null; //we don't want to track a service we are not using + } + + public void modifiedService(ServiceReference reference, Object service) { + + } + + public void removedService(ServiceReference reference, Object service) { + if (service == prefs) { + prefs = null; + } + registration.unregister(); + } + +} diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Authorization.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Authorization.java new file mode 100644 index 000000000..614db7d20 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Authorization.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import java.util.Vector; + +/** + * This interface encapsulates an authorization context on which bundles + * can base authorization decisions where appropriate. + * <p> + * Bundles associate the privilege to access restricted resources or + * operations with roles. Before granting access to a restricted resource + * or operation, a bundle will check if the Authorization object passed + * to it possesses the required role, by calling its hasRole method. + * <p> + * Authorization contexts are instantiated by calling + * {@link UserAdmin#getAuthorization} + * <p> + * <font size="+1">Trusting Authorization objects.</font> + * <p> + * There are no restrictions regarding the creation of Authorization objects. + * Hence, a service must only accept Authorization objects from bundles that + * has been authorized to use the service using code based (or Java 2) + * permissions. + * <p> + * In some cases it is useful to use ServicePermissions to do the code based + * access control. A service basing user access control on Authorization + * objects passed to it, will then require that a calling bundle has the + * ServicePermission to get the service in question. This is the most + * convenient way. The framework will do the code based permission check + * when the calling bundle attempts to get the service from the service + * registry. + * <p> + * Example: A servlet using a service on a user's behalf. The bundle with the + * servlet must be given the ServicePermission to get the Service. + * <p> + * However, in some cases the code based permission checks need to be more + * fine-grained. A service might allow all bundles to get it, but + * require certain code based permissions for some of its methods. + * <p> + * Example: A servlet using a service on a user's behalf, where some + * service functionality is open to anyone, and some is restricted by code + * based permissions. When a restricted method is called + * (e.g., one handing over + * an Authorization object), the service explicitly checks that the calling + * bundle has permission to make the call. + */ +public class Authorization implements org.osgi.service.useradmin.Authorization { + + protected UserAdmin useradmin; + protected Role user; + protected String name; //user to distinguish between the anonymous user and user.anyone + + protected Authorization(User user, UserAdmin useradmin) { + this.useradmin = useradmin; + if (user != null) { + this.user = user; + name = user.getName(); + } else { + //anonymous user + this.user = (Role) useradmin.getRole(Role.anyoneString); + name = null; + } + } + + /** + * Gets the name of the {@link User} that this Authorization + * context was created for. + * + * @return The name of the {@link User} that this Authorization + * context was created for, or <code>null</code> if no user was specified + * when this Authorization context was created. + */ + public String getName() { + useradmin.checkAlive(); + return (name); + } + + /** + * Checks if the role with the specified name is implied by this + * Authorization context. + * <p> + + * Bundles must define globally unique role names that are associated with + * the privilege of accessing restricted resources or operations. + * System administrators will grant users access to these resources, by + * creating a {@link Group} for each role and adding {@link User}s to it. + * + * @param name The name of the role to check for. + * + * @return <code>true</code> if this Authorization context implies the + * specified role, otherwise <code>false</code>. + */ + public boolean hasRole(String name) { + useradmin.checkAlive(); + synchronized (useradmin) { + Role checkRole = (org.eclipse.equinox.useradmin.Role) useradmin.getRole(name); + if (checkRole == null) { + return (false); + } + return (checkRole.isImpliedBy(user, new Vector())); + } + } + + /** + * Gets the names of all roles encapsulated by this Authorization context. + * + * @return The names of all roles encapsulated by this Authorization + * context, or <code>null</code> if no roles are in the context. + */ + public String[] getRoles() { + useradmin.checkAlive(); + + // go through all of the roles and find out which ones are implied by this + // authorization context. + synchronized (useradmin) //we don't want anything changing while we get the list + { + int length = useradmin.roles.size(); + Vector result = new Vector(length); + for (int i = 0; i < length; i++) { + Role role = (Role) useradmin.roles.elementAt(i); + if (role.isImpliedBy(user, new Vector())) { + String name = role.getName(); + //exclude user.anyone from the list + if (!name.equals(Role.anyoneString)) + { + result.addElement(name); + } + } + } + int size = result.size(); + if (size == 0) { + return (null); + } + String[] copyrole = new String[size]; + result.copyInto(copyrole); + return (copyrole); + } + } +} diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/ExternalMessages.properties b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/ExternalMessages.properties new file mode 100644 index 000000000..386f94669 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/ExternalMessages.properties @@ -0,0 +1,32 @@ +############################################################################### +# Copyright (c) 2001, 2005 IBM Corporation. +# 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 +############################################################################### +# NLS_MESSAGEFORMAT_ALL + +OSGi_User_Admin_service_IBM_Implementation_3=OSGi User Admin service - IBM Implementation +Service_Vendor=IBM +adding_Credential_to__15=adding Credential to {0} +adding_member__18=adding member {0} to group {1} +adding_required_member__21=adding required member {0} to group {1} +removing_member__24=removing member {0} from group {1} +Unable_to_load_role__27=Unable to load role {0} +Backing_Store_Read_Exception=UserAdmin is unable to read its data from backing store {0} +Backing_Store_Write_Exception=UserAdmin is unable to write its data from backing store {0} +Event_Delivery_Exception=A UserAdminListener threw an Exception +CREATE_NULL_ROLE_EXCEPTION =Role can not be null +CREATE_INVALID_TYPE_ROLE_EXCEPTION=Invalid type for createRole +INVALID_KEY_EXCEPTION=Key can only be of type String +INVALID_VALUE_EXCEPTION=Value can only be of type String or byte[] +USERADMIN_UNREGISTERED_EXCEPTION=UserAdmin service has been unregistered + +Unknown_Log_level=Unknown Log Level +Info=Log Info +Warning=Log Warning +Error=Log Error
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Group.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Group.java new file mode 100644 index 000000000..224659f59 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Group.java @@ -0,0 +1,296 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.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 UserAdmin useradmin; + + 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.useradmin.Role) role).addImpliedRole(this); + if (store) { + try { + useradmin.userAdminStore.addMember(this, (org.eclipse.equinox.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.useradmin.Role) role).addImpliedRole(this); + if (store) { + try { + useradmin.userAdminStore.addRequiredMember(this, (org.eclipse.equinox.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.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.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 (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); + } + +} diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/LogTracker.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/LogTracker.java new file mode 100644 index 000000000..74b43be51 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/LogTracker.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 1998, 2005 IBM Corporation. + * 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.useradmin; + +import java.io.PrintStream; +import java.text.DateFormat; +import java.util.Calendar; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogService; +import org.osgi.util.tracker.ServiceTracker; + +/** + * LogTracker class. This class encapsulates the LogService + * and handles all issues such as the service coming and going. + */ + +public class LogTracker extends ServiceTracker implements LogService { + /** LogService interface class name */ + protected final static String clazz = "org.osgi.service.log.LogService"; //$NON-NLS-1$ + + /** PrintStream to use if LogService is unavailable */ + protected PrintStream out; + + /** Calendar and DateFormat to user if LogService is unavailable */ + private static Calendar calendar; + private static DateFormat dateFormat; + private String timestamp; + + /** + * Create new LogTracker. + * + * @param context BundleContext of parent bundle. + * @param out Default PrintStream to use if LogService is unavailable. + */ + public LogTracker(BundleContext context, PrintStream out) { + super(context, clazz, null); + this.out = out; + calendar = Calendar.getInstance(); + dateFormat = DateFormat.getDateTimeInstance(); + open(); + } + + /* + * ---------------------------------------------------------------------- + * LogService Interface implementation + * ---------------------------------------------------------------------- + */ + + public void log(int level, String message) { + log(null, level, message, null); + } + + public void log(int level, String message, Throwable exception) { + log(null, level, message, exception); + } + + public void log(ServiceReference reference, int level, String message) { + log(reference, level, message, null); + } + + public synchronized void log(ServiceReference reference, int level, String message, Throwable exception) { + ServiceReference[] references = getServiceReferences(); + + if (references != null) { + int size = references.length; + + for (int i = 0; i < size; i++) { + LogService service = (LogService) getService(references[i]); + if (service != null) { + try { + service.log(reference, level, message, exception); + } catch (Exception e) { + } + } + } + + return; + } + + noLogService(level, message, exception, reference); + } + + /** + * The LogService is not available so we write the message to a PrintStream. + * + * @param level Logging level + * @param message Log message. + * @param throwable Log exception or null if none. + * @param reference ServiceReference associated with message or null if none. + */ + protected void noLogService(int level, String message, Throwable throwable, ServiceReference reference) { + if (out != null) { + synchronized (out) { + // Bug #113286. If no log service present and messages are being + // printed to stdout, prepend message with a timestamp. + timestamp = dateFormat.format(calendar.getTime()); + out.print(timestamp + " "); //$NON-NLS-1$ + + switch (level) { + case LOG_DEBUG : { + out.print("Debug: "); //$NON-NLS-1$ + + break; + } + case LOG_INFO : { + out.print(LogTrackerMsg.Info); + + break; + } + case LOG_WARNING : { + out.print(LogTrackerMsg.Warning); + + break; + } + case LOG_ERROR : { + out.print(LogTrackerMsg.Error); + + break; + } + default : { + out.print("["); //$NON-NLS-1$ + out.print(LogTrackerMsg.Unknown_Log_level); + out.print("]: "); //$NON-NLS-1$ + + break; + } + } + + out.println(message); + + if (reference != null) { + out.println(reference); + } + + if (throwable != null) { + throwable.printStackTrace(out); + } + } + } + } +} diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/LogTrackerMsg.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/LogTrackerMsg.java new file mode 100644 index 000000000..127910bad --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/LogTrackerMsg.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2005 IBM Corporation. + * 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.useradmin; + +import org.eclipse.osgi.util.NLS; + +public class LogTrackerMsg extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.equinox.useradmin.ExternalMessages"; //$NON-NLS-1$ + + public static String Unknown_Log_level; + public static String Info; + public static String Warning; + public static String Error; + + static { + // initialize resource bundles + NLS.initializeMessages(BUNDLE_NAME, LogTrackerMsg.class); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Role.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Role.java new file mode 100644 index 000000000..195407bc6 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/Role.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import java.util.*; + +/** + * The base interface for Role objects managed by the {@link UserAdmin} + * service. + * <p> + * This interface exposes the characteristics shared by all Roles: a name, + * a type, and a set of properties. + * <p> + * Properties represent public information about the Role that can be read by + * anyone. Specific {@link UserAdminPermission}s are required to + * change a Role's properties. + * <p> + * Role properties are Dictionary objects. Changes to + * these objects are propagated to the {@link UserAdmin} service and + * made persistent. + * <p> + * Every UserAdmin contains a set of predefined roles that are always present + * and cannot be removed. All predefined roles are of type <tt>ROLE</tt>. + * This version of the <tt>org.osgi.service.useradmin</tt> package defines a + * single predefined role named "user.anyone", which is inherited + * by any other role. Other predefined roles may be added in the future. + */ + +public class Role implements org.osgi.service.useradmin.Role { + + protected String name; + protected UserAdminHashtable properties; + protected Vector impliedRoles; + protected UserAdmin useradmin; + protected static final String anyoneString = "user.anyone"; //$NON-NLS-1$ + protected boolean exists = true; + + protected Role(String name, UserAdmin useradmin) { + this.name = name; + this.properties = new UserAdminHashtable(this, useradmin, UserAdminHashtable.PROPERTIES); + this.useradmin = useradmin; + + // This is used only to track which Groups this role is directly a member of. + // This info is needed so when we delete a Role, we know which groups to remove + // it from. + impliedRoles = new Vector(); + } + + /** + * Returns the name of this role. + * + * @return The role's name. + */ + + public String getName() { + useradmin.checkAlive(); + return (name); + } + + /** + * Returns the type of this role. + * + * @return The role's type. + */ + public int getType() { + useradmin.checkAlive(); + return (Role.ROLE); + } + + /** + * Returns a Dictionary of the (public) properties of this Role. Any changes + * to the returned Dictionary will change the properties of this Role. This + * will cause a UserAdminEvent of type {@link UserAdminEvent#ROLE_CHANGED} + * to be broadcast to any UserAdminListeners. + * <p> + * Only objects of type <tt>String</tt> may be used as property keys, and + * only objects of type <tt>String</tt> or <tt>byte[]</tt> + * may be used as property values. + * Any other types will cause an exception of type + * <tt>IllegalArgumentException</tt> to be raised. + * <p> + * In order to add, change, or remove a property in the returned Dictionary, + * a {@link UserAdminPermission} named after the property name (or + * a prefix of it) with action <code>changeProperty</code> is required. + * + * @return Dictionary containing the properties of this Role. + */ + public Dictionary getProperties() { + useradmin.checkAlive(); + return (properties); + } + + protected void addImpliedRole(Group group) { + impliedRoles.addElement(group); + } + + protected void removeImpliedRole(Group group) { + if (exists) //this prevents a loop when destroy is called + { + impliedRoles.removeElement(group); + } + } + + //we are being deleted so delete ourselves from all of the groups + protected synchronized void destroy() { + exists = false; + Enumeration e = impliedRoles.elements(); + while (e.hasMoreElements()) { + Group group = (Group) e.nextElement(); + if (group.exists) //so we don't try to remove any groups twice from storage + { + group.removeMember(this); + } + } + properties = null; + impliedRoles = null; + } + + protected boolean isImpliedBy(Role role, Vector checkLoop) { //Roles do not imply themselves + //The user.anyone role is always implied + if (checkLoop.contains(name)) { + //we have a circular dependency + return (false); + } + checkLoop.addElement(name); + return (name.equals(Role.anyoneString)); + } + +} diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/User.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/User.java new file mode 100644 index 000000000..025773a91 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/User.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import java.util.Dictionary; +import java.util.Vector; + +/** + * A User managed by a {@link UserAdmin} service. + * <p> + * In this context, the term "user" is not limited to just + * human beings. + * Instead, it refers to any entity that may have any number of + * credentials associated with it that it may use to authenticate itself. + * <p> + * In general, User objects are associated with a specific {@link UserAdmin} + * service (namely the one that created them), and cannot be used with other + * UserAdmin services. + * <p> + * A User may have credentials (and properties, inherited from {@link Role}) + * associated with it. Specific {@link UserAdminPermission}s are required to + * read or change a User's credentials. + * <p> + * Credentials are Dictionary objects and have semantics that are similar + * to the properties in Role. + */ + +public class User extends Role implements org.osgi.service.useradmin.User { + + protected UserAdminHashtable credentials; + protected UserAdmin useradmin; + + protected User(String name, UserAdmin useradmin) { + super(name, useradmin); + this.useradmin = useradmin; + credentials = new UserAdminHashtable(this, useradmin, UserAdminHashtable.CREDENTIALS); + } + + /** + * Returns a Dictionary of the credentials of this User. Any changes + * to the returned Dictionary will change the credentials of this User. + * This will cause a UserAdminEvent of type + * {@link UserAdminEvent#ROLE_CHANGED} to be broadcast to any + * UserAdminListeners. + * <p> + * Only objects of type String may be used as credential keys, and only + * objects of type <code>String</code> or of type <code>byte[]</code> + * may be used as credential values. Any other types will cause an exception + * of type <code>IllegalArgumentException</code> to be raised. + * <p> + * In order to retrieve a credential from the returned Dictionary, + * a {@link UserAdminPermission} named after the credential name (or + * a prefix of it) with action <code>getCredential</code> is required. + * <p> + * In order to add or remove a credential from the returned Dictionary, + * a {@link UserAdminPermission} named after the credential name (or + * a prefix of it) with action <code>changeCredential</code> is required. + * + * @return Dictionary containing the credentials of this User. + */ + + public Dictionary getCredentials() { + useradmin.checkAlive(); + return (credentials); + } + + /** + * Checks to see if this User has a credential with the specified key + * set to the specified value. + * <p> + * If the specified credential value is not of type <tt>String</tt> or + * <tt>byte[]</tt>, it is ignored, that is, <tt>false</tt> is returned + * (as opposed to an <tt>IllegalArgumentException</tt> being raised). + * + * @param key The credential key. + * @param value The credential value. + * + * @return <code>true</code> if this user has the specified credential; + * <code>false</code> otherwise. + * + * @throws SecurityException If a security manager exists and the caller + * does not have the <tt>UserAdminPermission</tt> named after the credential + * key (or a prefix of it) with action <code>getCredential</code>. + */ + public boolean hasCredential(String key, Object value) { + useradmin.checkAlive(); + Object checkValue = credentials.get(key); + if (checkValue != null) { + if (value instanceof String) { + if (checkValue.equals(value)) { + return (true); + } + } else if (value instanceof byte[])//FIXME Is there a good way to do this compare??? + { + if (!(checkValue instanceof byte[])) { + return (false); + } + //???do they need to be in order? + byte[] valueArray = (byte[]) value; + byte[] checkValueArray = (byte[]) checkValue; + int length = valueArray.length; + if (length != checkValueArray.length) { + return (false); + } + for (int i = 0; i < length; i++) { + if (valueArray[i] != checkValueArray[i]) { + return (false); + } + } + return (true); + } + } + return (false); //if checkValue is null + } + + /** + * Returns the type of this role. + * + * @return The role's type. + */ + public int getType() { + useradmin.checkAlive(); + return Role.USER; + } + + //A user always implies itself + protected boolean isImpliedBy(Role role, Vector checkLoop) { + if (checkLoop.contains(name)) { + //we have a circular dependency + return (false); + } + checkLoop.addElement(name); + return ((role.getName()).equals(name)); + } + +} diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdmin.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdmin.java new file mode 100644 index 000000000..e0e079324 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdmin.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import java.util.*; +import org.osgi.framework.*; +import org.osgi.service.prefs.BackingStoreException; +import org.osgi.service.prefs.PreferencesService; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminPermission; + +/** + * This interface is used to manage a database of named roles, which can + * be used for authentication and authorization purposes. + * <p> + * This version of UserAdmin defines two types of roles: "User" and + * "Group". Each type of role is represented by an "int" 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> + * Every role has a name and a type. + * <p> + * A {@link User} role can be configured with credentials (e.g., a password) + * and properties (e.g., a street address, phone number, etc.). + * <p> + * A {@link Group} role represents an aggregation of {@link User} and + * {@link Group} roles. In + * other words, the members of a Group role are roles themselves. + * <p> + * Every UserAdmin manages and maintains its own + * namespace of roles, in which each role has a unique name. + */ + +public class UserAdmin implements org.osgi.service.useradmin.UserAdmin { + + protected Vector users; + protected Vector roles; + protected BundleContext context; + protected UserAdminEventProducer eventProducer; + protected boolean alive; + protected UserAdminStore userAdminStore; + protected UserAdminPermission adminPermission; + protected ServiceReference reference; + protected LogTracker log; + + protected UserAdmin(PreferencesService preferencesService, BundleContext context) throws Exception { + roles = new Vector(); + users = new Vector(); + this.context = context; + + log = new LogTracker(context, System.out); + alive = true; + //This handles user admin persistence + try { + userAdminStore = new UserAdminStore(preferencesService, this, log); + userAdminStore.init(); + } catch (Exception e) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Read_Exception, e); + throw e; + } + } + + protected void setServiceReference(ServiceReference reference) { + if (this.reference == null) { + this.reference = reference; + + eventProducer = new UserAdminEventProducer(reference, context, log); + } + } + + /** + * Creates a role with the given name and of the given type. + * + * <p> If a role was created, a UserAdminEvent of type + * {@link UserAdminEvent#ROLE_CREATED} is broadcast to any + * UserAdminListener. + * + * @param name The name of the role to create. + * @param type The type of the role to create. Must be either + * {@link Role#USER} or {@link Role#GROUP}. + * + * @return The newly created role, or <code>null</code> if a role with + * the given name already exists. + * + * @throws IllegalArgumentException if <tt>type</tt> is invalid. + * + * @throws SecurityException If a security manager exists and the caller + * does not have the <tt>UserAdminPermission</tt> with name <tt>admin</tt>. + */ + public org.osgi.service.useradmin.Role createRole(String name, int type) { + checkAlive(); + checkAdminPermission(); + if (name == null) { + throw (new IllegalArgumentException(UserAdminMsg.CREATE_NULL_ROLE_EXCEPTION)); + } + if ((type != Role.GROUP) && (type != Role.USER)) { + throw (new IllegalArgumentException(UserAdminMsg.CREATE_INVALID_TYPE_ROLE_EXCEPTION)); + } + //if the role already exists, return null + if (getRole(name) != null) { + return (null); + } + + synchronized (this) { + return createRole(name, type, true); + } + } + + protected org.osgi.service.useradmin.Role createRole(String name, int type, boolean store) { + Role newRole = null; + if (type == Role.ROLE) { + newRole = new Role(name, this); + } else if (type == Role.USER) { + newRole = new User(name, this); + } else if (type == Role.GROUP) { + newRole = new Group(name, this); + } else //unknown type + { + return (null); + } + if (store) { + try { + userAdminStore.addRole(newRole); + } catch (BackingStoreException ex) { + return (null); + } + if (eventProducer != null) { + eventProducer.generateEvent(UserAdminEvent.ROLE_CREATED, newRole); + } + } + if (type == Role.GROUP || type == Role.USER) { + users.addElement(newRole); + } + roles.addElement(newRole); + return (newRole); + } + + /** + * Removes the role with the given name from this UserAdmin. + * + * <p> If the role was removed, a UserAdminEvent of type + * {@link UserAdminEvent#ROLE_REMOVED} is broadcast to any + * UserAdminListener. + * + * @param name The name of the role to remove. + * + * @return <code>true</code> If a role with the given name is present in this + * UserAdmin and 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 removeRole(String name) { + checkAlive(); + checkAdminPermission(); + if (name.equals(Role.anyoneString)) { + //silently ignore + return (true); + } + synchronized (this) { + Role role = (org.eclipse.equinox.useradmin.Role) getRole(name); + if (role != null) { + try { + userAdminStore.removeRole(role); + } catch (BackingStoreException ex) { + return (false); + } + roles.removeElement(role); + users.removeElement(role); + role.destroy(); + eventProducer.generateEvent(UserAdminEvent.ROLE_REMOVED, role); + role = null; + return (true); + } + return (false); + } + } + + /** + * Gets the role with the given name from this UserAdmin. + * + * @param name The name of the role to get. + * + * @return The requested role, or <code>null</code> if this UserAdmin does + * not have a role with the given name. + */ + public org.osgi.service.useradmin.Role getRole(String name) { + checkAlive(); + if (name == null) { + return (null); + } + synchronized (this) { + Enumeration e = roles.elements(); + while (e.hasMoreElements()) { + Role role = (Role) e.nextElement(); + if (role.getName().equals(name)) { + return (role); + } + } + return (null); + } + } + + /** + * Gets the roles managed by this UserAdmin that have properties matching + * the specified LDAP filter criteria. See + * <code>org.osgi.framework.Filter</code> or IETF RFC 2254 for a + * description of the filter syntax. If a <code>null</code> filter is + * specified, all roles managed by this UserAdmin are returned. + * + * @param filter The filter criteria to match. + * + * @return The roles managed by this UserAdmin whose properties + * match the specified filter criteria, or all roles if a + * <code>null</code> filter is specified. + * + */ + public org.osgi.service.useradmin.Role[] getRoles(String filterString) throws InvalidSyntaxException { + checkAlive(); + Vector returnedRoles; + synchronized (this) { + if (filterString == null) { + returnedRoles = roles; + } else { + Filter filter = context.createFilter(filterString); //We do this first so an + //InvalidSyntaxException will be + //thrown even if there are no roles + //present. + returnedRoles = new Vector(); + for (int i = 0; i < roles.size(); i++) { + Role role = (Role) roles.elementAt(i); + if (filter.match(role.getProperties())) { + returnedRoles.addElement(role); + } + } + } + int size = returnedRoles.size(); + if (size == 0) { + return (null); + } + Role[] roleArray = new Role[size]; + returnedRoles.copyInto(roleArray); + return (roleArray); + } + } + + /** + * Gets the user with the given property key-value pair from the UserAdmin + * database. This is a convenience method for retrieving a user based on + * a property for which every user is supposed to have a unique value + * (within the scope of this UserAdmin), such as a user's + * X.500 distinguished name. + * + * @param key The property key to look for. + * @param value The property value to compare with. + * + * @return A matching user, if <em>exactly</em> one is found. If zero or + * more than one matching users are found, <code>null</code> is returned. + */ + public org.osgi.service.useradmin.User getUser(String key, String value) { + checkAlive(); + if (key == null) { + return (null); + } + User user; + User foundUser = null; + Dictionary props; + String keyValue; + synchronized (this) { + Enumeration e = users.elements(); + while (e.hasMoreElements()) { + user = (User) e.nextElement(); + props = user.getProperties(); + keyValue = (String) props.get(key); + if (keyValue != null && keyValue.equals(value)) { + if (foundUser != null) { + return (null); //we found more than one match + } + foundUser = user; + } + } + return (foundUser); + } + } + + /** + * Creates an Authorization object that encapsulates the specified user + * and the roles it possesses. The <code>null</code> user is interpreted + * as the anonymous user. + * + * @param user The user to create an Authorization object for, or + * <code>null</code> for the anonymous user. + * + * @return the Authorization object for the specified user. + */ + public org.osgi.service.useradmin.Authorization getAuthorization(org.osgi.service.useradmin.User user) { + checkAlive(); + return (new Authorization((User) user, this)); + } + + protected synchronized void destroy() { + alive = false; + eventProducer.close(); + userAdminStore.destroy(); + + log.close(); + } + + public void checkAdminPermission() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (adminPermission == null) { + adminPermission = new UserAdminPermission(UserAdminPermission.ADMIN, null); + } + sm.checkPermission(adminPermission); + } + } + + public void checkGetCredentialPermission(String credential) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new org.osgi.service.useradmin.UserAdminPermission(credential, org.osgi.service.useradmin.UserAdminPermission.GET_CREDENTIAL)); + } + } + + public void checkChangeCredentialPermission(String credential) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new org.osgi.service.useradmin.UserAdminPermission(credential, org.osgi.service.useradmin.UserAdminPermission.CHANGE_CREDENTIAL)); + } + } + + public void checkChangePropertyPermission(String property) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new org.osgi.service.useradmin.UserAdminPermission(property, org.osgi.service.useradmin.UserAdminPermission.CHANGE_PROPERTY)); + } + } + + public void checkAlive() { + if (!alive) { + throw (new IllegalStateException(UserAdminMsg.USERADMIN_UNREGISTERED_EXCEPTION)); + } + } + +} diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminEventProducer.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminEventProducer.java new file mode 100644 index 000000000..aa3af0a13 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminEventProducer.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import org.eclipse.osgi.framework.eventmgr.*; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogService; +import org.osgi.service.useradmin.UserAdminEvent; +import org.osgi.service.useradmin.UserAdminListener; +import org.osgi.util.tracker.ServiceTracker; + +/* + * UserAdminEventProducer is responsible for sending out UserAdminEvents + * to all UserAdminListeners. + */ + +public class UserAdminEventProducer extends ServiceTracker implements EventDispatcher { + + protected ServiceReference userAdmin; + static protected final String userAdminListenerClass = "org.osgi.service.useradmin.UserAdminListener"; //$NON-NLS-1$ + protected LogService log; + /** List of UserAdminListeners */ + protected EventListeners listeners; + /** EventManager for event delivery. */ + protected EventManager eventManager; + + protected UserAdminEventProducer(ServiceReference userAdmin, BundleContext context, LogService log) { + super(context, userAdminListenerClass, null); + this.userAdmin = userAdmin; + this.log = log; + eventManager = new EventManager(); + listeners = new EventListeners(); + + open(); + } + + public void close() { + super.close(); + listeners.removeAllListeners(); + eventManager.close(); + userAdmin = null; + } + + protected void generateEvent(int type, Role role) { + if (userAdmin != null) { + UserAdminEvent event = new UserAdminEvent(userAdmin, type, role); + + /* queue to hold set of listeners */ + ListenerQueue queue = new ListenerQueue(eventManager); + + /* add set of UserAdminListeners to queue */ + queue.queueListeners(listeners, this); + + /* dispatch event to set of listeners */ + queue.dispatchEventAsynchronous(0, event); + } + } + + /** + * A service is being added to the <tt>ServiceTracker</tt> object. + * + * <p>This method is called before a service which matched + * the search parameters of the <tt>ServiceTracker</tt> object is + * added to it. This method should return the + * service object to be tracked for this <tt>ServiceReference</tt> object. + * The returned service object is stored in the <tt>ServiceTracker</tt> object + * and is available from the <tt>getService</tt> and <tt>getServices</tt> + * methods. + * + * @param reference Reference to service being added to the <tt>ServiceTracker</tt> object. + * @return The service object to be tracked for the + * <tt>ServiceReference</tt> object or <tt>null</tt> if the <tt>ServiceReference</tt> object should not + * be tracked. + */ + public Object addingService(ServiceReference reference) { + Object service = super.addingService(reference); + + listeners.addListener(service, service); + + return service; + } + + /** + * A service tracked by the <tt>ServiceTracker</tt> object has been removed. + * + * <p>This method is called after a service is no longer being tracked + * by the <tt>ServiceTracker</tt> object. + * + * @param reference Reference to service that has been removed. + * @param service The service object for the removed service. + */ + public void removedService(ServiceReference reference, Object service) { + listeners.removeListener(service); + + super.removedService(reference, service); + } + + /** + * This method is the call back that is called once for each listener. + * This method must cast the EventListener object to the appropriate listener + * class for the event type and call the appropriate listener method. + * + * @param listener This listener must be cast to the appropriate listener + * class for the events created by this source and the appropriate listener method + * must then be called. + * @param listenerObject This is the optional object that was passed to + * ListenerList.addListener when the listener was added to the ListenerList. + * @param eventAction This value was passed to the EventQueue object via one of its + * dispatchEvent* method calls. It can provide information (such + * as which listener method to call) so that this method + * can complete the delivery of the event to the listener. + * @param eventObject This object was passed to the EventQueue object via one of its + * dispatchEvent* method calls. This object was created by the event source and + * is passed to this method. It should contain all the necessary information (such + * as what event object to pass) so that this method + * can complete the delivery of the event to the listener. + */ + public void dispatchEvent(Object listener, Object listenerObject, int eventAction, Object eventObject) { + ServiceReference userAdmin = this.userAdmin; + + if (userAdmin == null) { + return; + } + + UserAdminListener ual = (UserAdminListener) listener; + try { + ual.roleChanged((UserAdminEvent) eventObject); + } catch (Throwable t) { + log.log(userAdmin, log.LOG_WARNING, UserAdminMsg.Event_Delivery_Exception, t); + } + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminHashtable.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminHashtable.java new file mode 100644 index 000000000..c1b124f08 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminHashtable.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import java.util.Enumeration; +import java.util.Hashtable; +import org.osgi.service.prefs.BackingStoreException; +import org.osgi.service.useradmin.UserAdminEvent; + +/* The UserAdminHashtable is a Hashtable that generates UserAdminEvents when there + * is a change to the Hashtable. This is used specifically to store Role properties + * and User credentials. + */ + +public class UserAdminHashtable extends Hashtable { + + protected Role role; + protected int propertyType; + protected UserAdmin userAdmin; + protected UserAdminStore userAdminStore; + + //TODO - split this into two classes so we don't have to do this + protected static final int CREDENTIALS = 0; + protected static final int PROPERTIES = 1; + + protected UserAdminHashtable(Role role, UserAdmin userAdmin, int propertyType) { + this.role = role; + this.propertyType = propertyType; + this.userAdmin = userAdmin; + this.userAdminStore = userAdmin.userAdminStore; + } + + /* + * We want to generate an event every time we put something into the hashtable, except + * upon initialization where role data is being read from persistent store. + */ + protected synchronized Object put(String key, Object value, boolean generateEvent) { + + if (generateEvent) { + if (propertyType == UserAdminHashtable.PROPERTIES) { + try { + userAdminStore.addProperty(role, key, value); + } catch (BackingStoreException ex) { + return (null); + } + userAdmin.eventProducer.generateEvent(UserAdminEvent.ROLE_CHANGED, role); + } else if (propertyType == UserAdminHashtable.CREDENTIALS) { + try { + userAdminStore.addCredential(role, key, value); + } catch (BackingStoreException ex) { + return (null); + } + userAdmin.eventProducer.generateEvent(UserAdminEvent.ROLE_CHANGED, role); + } + } + Object retVal = super.put(key, value); + return retVal; + } + + public Object put(Object key, Object value) { + if (!(key instanceof String)) { + throw new IllegalArgumentException(UserAdminMsg.INVALID_KEY_EXCEPTION); + } + + if (!((value instanceof String) || (value instanceof byte[]))) { + throw new IllegalArgumentException(UserAdminMsg.INVALID_VALUE_EXCEPTION); + } + + String name = (String) key; + + switch (propertyType) { + case PROPERTIES : + userAdmin.checkChangePropertyPermission(name); + break; + case CREDENTIALS : + userAdmin.checkChangeCredentialPermission(name); + break; + } + + return put(name, value, true); + } + + public synchronized Object remove(Object key) { + if (!(key instanceof String)) { + throw new IllegalArgumentException(UserAdminMsg.INVALID_KEY_EXCEPTION); + } + + String name = (String) key; + + switch (propertyType) { + case PROPERTIES : + userAdmin.checkChangePropertyPermission(name); + try { + userAdminStore.removeProperty(role, name); + } catch (BackingStoreException ex) { + return (null); + } + userAdmin.eventProducer.generateEvent(UserAdminEvent.ROLE_CHANGED, role); + break; + case CREDENTIALS : + userAdmin.checkChangeCredentialPermission(name); + try { + userAdminStore.removeCredential(role, name); + } catch (BackingStoreException ex) { + return (null); + } + userAdmin.eventProducer.generateEvent(UserAdminEvent.ROLE_CHANGED, role); + break; + } + + return super.remove(name); + } + + public synchronized void clear() { + Enumeration enum = keys(); + + while (enum.hasMoreElements()) { + String name = (String) enum.nextElement(); + + switch (propertyType) { + case PROPERTIES : + userAdmin.checkChangePropertyPermission(name); + break; + case CREDENTIALS : + userAdmin.checkChangeCredentialPermission(name); + break; + } + } + + switch (propertyType) { + case PROPERTIES : + try { + userAdminStore.clearProperties(role); + } catch (BackingStoreException ex) { + return; + } + userAdmin.eventProducer.generateEvent(UserAdminEvent.ROLE_CHANGED, role); + break; + case CREDENTIALS : + try { + userAdminStore.clearCredentials(role); + } catch (BackingStoreException ex) { + return; + } + userAdmin.eventProducer.generateEvent(UserAdminEvent.ROLE_CHANGED, role); + break; + } + + super.clear(); + } + + public Object get(Object key) { + if (!(key instanceof String)) { + throw new IllegalArgumentException(UserAdminMsg.INVALID_KEY_EXCEPTION); + } + + String name = (String) key; + + if (propertyType == CREDENTIALS) { + userAdmin.checkGetCredentialPermission(name); + } + + return super.get(name); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminMsg.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminMsg.java new file mode 100644 index 000000000..be4bf7618 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminMsg.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import org.eclipse.osgi.util.NLS; + +public class UserAdminMsg extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.equinox.useradmin.ExternalMessages"; //$NON-NLS-1$ + + public static String adding_Credential_to__15; + public static String adding_member__18; + public static String adding_required_member__21; + public static String removing_member__24; + public static String Unable_to_load_role__27; + public static String Backing_Store_Read_Exception; + public static String Backing_Store_Write_Exception; + public static String Event_Delivery_Exception; + public static String CREATE_NULL_ROLE_EXCEPTION; + public static String CREATE_INVALID_TYPE_ROLE_EXCEPTION; + public static String INVALID_KEY_EXCEPTION; + public static String INVALID_VALUE_EXCEPTION; + public static String USERADMIN_UNREGISTERED_EXCEPTION; + public static String Service_Vendor; + public static String OSGi_User_Admin_service_IBM_Implementation_3; + + static { + // initialize resource bundles + NLS.initializeMessages(BUNDLE_NAME, UserAdminMsg.class); + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminStore.java b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminStore.java new file mode 100644 index 000000000..557aeca45 --- /dev/null +++ b/bundles/org.eclipse.equinox.useradmin/src/org/eclipse/equinox/useradmin/UserAdminStore.java @@ -0,0 +1,348 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 IBM Corporation. + * 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.useradmin; + +import java.security.*; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.log.LogService; +import org.osgi.service.prefs.*; + +/* + * UserAdminStore is responsible for managing the persistence data of the useradmin + * service. It uses the PersistenceNode service as its underlying storage. + */ + +public class UserAdminStore { + + static protected final String propertiesNode = "properties"; //$NON-NLS-1$ + static protected final String credentialsNode = "credentials"; //$NON-NLS-1$ + static protected final String membersNode = "members"; //$NON-NLS-1$ + static protected final String basicString = "basic"; //$NON-NLS-1$ + static protected final String requiredString = "required"; //$NON-NLS-1$ + static protected final String typeString = "type"; //$NON-NLS-1$ + static protected final String persistenceUserName = "UserAdmin"; //$NON-NLS-1$ + + protected ServiceReference prefsRef; + protected ServiceRegistration userAdminListenerReg; + protected UserAdmin useradmin; + protected LogService log; + protected Preferences rootNode; + protected PreferencesService preferencesService; + + protected UserAdminStore(PreferencesService preferencesService, UserAdmin useradmin, LogService log) { + this.preferencesService = preferencesService; + this.useradmin = useradmin; + this.log = log; + } + + protected void init() throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + + public Object run() throws BackingStoreException { + rootNode = preferencesService.getUserPreferences(persistenceUserName); + loadRoles(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + + throw ((BackingStoreException) ex.getException()); + } + } + + protected void addRole(final org.osgi.service.useradmin.Role role) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences node = rootNode.node(role.getName()); + node.putInt(typeString, role.getType()); + node.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void removeRole(final org.osgi.service.useradmin.Role role) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences node = rootNode.node(role.getName()); + node.removeNode(); + rootNode.node("").flush(); //$NON-NLS-1$ + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void clearProperties(final org.osgi.service.useradmin.Role role) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences propertyNode = rootNode.node(role.getName() + "/" + propertiesNode); //$NON-NLS-1$ + propertyNode.clear(); + propertyNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void addProperty(final org.osgi.service.useradmin.Role role, final String key, final Object value) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences propertyNode = rootNode.node(role.getName() + "/" + propertiesNode); //$NON-NLS-1$ + if (value instanceof String) { + propertyNode.put(key, (String) value); + } else //must be a byte array, then + { + propertyNode.putByteArray(key, (byte[]) value); + } + propertyNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void removeProperty(final org.osgi.service.useradmin.Role role, final String key) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences propertyNode = rootNode.node(role.getName() + "/" + propertiesNode); //$NON-NLS-1$ + propertyNode.remove(key); + propertyNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void clearCredentials(final org.osgi.service.useradmin.Role role) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences credentialNode = rootNode.node(role.getName() + "/" + credentialsNode); //$NON-NLS-1$ + credentialNode.clear(); + credentialNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void addCredential(final org.osgi.service.useradmin.Role role, final String key, final Object value) throws BackingStoreException { + + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences credentialNode = rootNode.node(role.getName() + "/" + credentialsNode); //$NON-NLS-1$ + if (value instanceof String) { + credentialNode.put(key, (String) value); + } else //assume it is a byte array + { + credentialNode.putByteArray(key, (byte[]) value); + } + credentialNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, NLS.bind(UserAdminMsg.Backing_Store_Write_Exception, new Object[] {NLS.bind(UserAdminMsg.adding_Credential_to__15, role.getName())}), ex); + throw ((BackingStoreException) ex.getException()); + } + + } + + protected void removeCredential(final org.osgi.service.useradmin.Role role, final String key) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences credentialNode = rootNode.node(role.getName() + "/" + credentialsNode); //$NON-NLS-1$ + credentialNode.remove(key); + credentialNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void addMember(final Group group, final Role role) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences memberNode = rootNode.node(group.getName() + "/" + membersNode); //$NON-NLS-1$ + memberNode.put(role.getName(), basicString); + memberNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, NLS.bind(UserAdminMsg.Backing_Store_Write_Exception, new Object[] {NLS.bind(UserAdminMsg.adding_member__18, role.getName(), group.getName())}), ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void addRequiredMember(final Group group, final Role role) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences memberNode = rootNode.node(group.getName() + "/" + membersNode); //$NON-NLS-1$ + memberNode.put(role.getName(), requiredString); + memberNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, NLS.bind(UserAdminMsg.Backing_Store_Write_Exception, new Object[] {NLS.bind(UserAdminMsg.adding_required_member__21, role.getName(), group.getName())}), ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void removeMember(final Group group, final Role role) throws BackingStoreException { + try { + AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws BackingStoreException { + Preferences memberNode = rootNode.node(group.getName() + "/" + membersNode); //$NON-NLS-1$ + memberNode.remove(role.getName()); + memberNode.flush(); + return (null); + } + }); + } catch (PrivilegedActionException ex) { + log.log(log.LOG_ERROR, NLS.bind(UserAdminMsg.Backing_Store_Write_Exception, new Object[] {NLS.bind(UserAdminMsg.removing_member__24, role.getName(), group.getName())}), ex); + throw ((BackingStoreException) ex.getException()); + } + } + + protected void loadRoles() throws BackingStoreException { + synchronized (this) { + createAnonRole(); + + String[] children = rootNode.node("").childrenNames(); //$NON-NLS-1$ + + for (int i = 0; i < children.length; i++) { + if (useradmin.getRole(children[i]) == null) //check to see if it is already loaded + { //(we may have had to load some roles out of + loadRole(rootNode.node(children[i]), null); // order due to dependencies) + //modified to solve defect 95982 + } + } + } + } + + /* modified to solve defect 95982 */ + protected void loadRole(Preferences node, Role role) throws BackingStoreException { + int type = node.getInt(typeString, Integer.MIN_VALUE); + + if (type == Integer.MIN_VALUE) { + String errorString = NLS.bind(UserAdminMsg.Backing_Store_Read_Exception, new Object[] {NLS.bind(UserAdminMsg.Unable_to_load_role__27, node.name())}); + BackingStoreException ex = new BackingStoreException(errorString); + log.log(log.LOG_ERROR, errorString, ex); + throw (ex); + } + if (role == null) { + role = (Role) useradmin.createRole(node.name(), type, false); + } + Preferences propsNode = node.node(propertiesNode); + String[] keys = propsNode.keys(); + UserAdminHashtable properties = (UserAdminHashtable) role.getProperties(); + String value; + + //load properties + for (int i = 0; i < keys.length; i++) { + value = propsNode.get(keys[i], null); + properties.put(keys[i], value, false); + } + + //load credentials + if (type == Role.USER || type == Role.GROUP) { + Object credValue; + Preferences credNode = node.node(credentialsNode); + keys = credNode.keys(); + UserAdminHashtable credentials = (UserAdminHashtable) ((User) role).getCredentials(); + for (int i = 0; i < keys.length; i++) { + credValue = credNode.get(keys[i], null); + credentials.put(keys[i], credValue, false); + } + } + + //load group members + if (type == Role.GROUP) { + Preferences memberNode = node.node(membersNode); + keys = memberNode.keys(); + for (int i = 0; i < keys.length; i++) { + value = memberNode.get(keys[i], null); + Role member = (Role) useradmin.getRole(keys[i]); + if (member == null) //then we have not loaded this one yet, so load it + { + loadRole(rootNode.node(keys[i]), null); // modified to solve defect 95982 + member = (Role) useradmin.getRole(keys[i]); + } + if (value.equals(requiredString)) { + ((Group) role).addRequiredMember(member, false); + } else { + ((Group) role).addMember(member, false); + } + } + } + } + + protected void destroy() { + try { + rootNode.flush(); + rootNode = null; + preferencesService = null; + } catch (BackingStoreException ex) { + log.log(log.LOG_ERROR, UserAdminMsg.Backing_Store_Write_Exception, ex); + } + } + + private void createAnonRole() throws BackingStoreException { + Role role = null; + if (!rootNode.nodeExists(Role.anyoneString)) { + //If the user.anyone role is not present, create it + role = (Role) useradmin.createRole(Role.anyoneString, Role.ROLE, true); + } + /* modified to solve defect 95982 */ + if (role != null) + loadRole(rootNode.node(Role.anyoneString), role); + else + loadRole(rootNode.node(Role.anyoneString), null); + } + +} |