opends/resource/config/config.ldif
@@ -1028,8 +1028,24 @@ dn: cn=Root DNs,cn=config objectClass: top objectClass: ds-cfg-branch objectClass: ds-cfg-root-dn-base cn: Root DNs ds-cfg-default-root-privilege-name: bypass-acl ds-cfg-default-root-privilege-name: modify-acl ds-cfg-default-root-privilege-name: config-read ds-cfg-default-root-privilege-name: config-write ds-cfg-default-root-privilege-name: ldif-import ds-cfg-default-root-privilege-name: ldif-export ds-cfg-default-root-privilege-name: backend-backup ds-cfg-default-root-privilege-name: backend-restore ds-cfg-default-root-privilege-name: server-shutdown ds-cfg-default-root-privilege-name: server-restart ds-cfg-default-root-privilege-name: disconnect-client ds-cfg-default-root-privilege-name: cancel-request ds-cfg-default-root-privilege-name: search-unindexed ds-cfg-default-root-privilege-name: password-reset ds-cfg-default-root-privilege-name: update-schema ds-cfg-default-root-privilege-name: privilege-change dn: cn=Directory Manager,cn=Root DNs,cn=config objectClass: top opends/resource/schema/02-config.ldif
@@ -1042,6 +1042,12 @@ NAME 'ds-cfg-heartbeat-interval' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.307 NAME 'ds-privilege-name' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE directoryOperation X-ORIGIN 'OpenDS Directory Server' ) attributeTypes: ( 1.3.6.1.4.1.26027.1.1.308 NAME 'ds-cfg-default-root-privilege-name' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.1 NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled ) @@ -1429,4 +1435,7 @@ objectClasses: ( 1.3.6.1.4.1.26027.1.2.81 NAME 'ds-cfg-group-implementation' SUP top STRUCTURAL MUST ( cn $ ds-cfg-group-implementation-class $ ds-cfg-group-implementation-enabled ) X-ORIGIN 'OpenDS Directory Server' ) objectClasses: ( 1.3.6.1.4.1.26027.1.2.82 NAME 'ds-cfg-root-dn-base' SUP top STRUCTURAL MUST cn MAY ds-cfg-default-root-privilege-name X-ORIGIN 'OpenDS Directory Server' ) opends/src/server/org/opends/server/api/ClientConnection.java
@@ -32,6 +32,7 @@ import java.nio.ByteBuffer; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; @@ -41,6 +42,9 @@ import org.opends.server.core.PersistentSearch; import org.opends.server.core.PluginConfigManager; import org.opends.server.core.SearchOperation; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.AuthenticationInfo; import org.opends.server.types.CancelRequest; import org.opends.server.types.CancelResult; @@ -48,13 +52,20 @@ import org.opends.server.types.DisconnectReason; import org.opends.server.types.DN; import org.opends.server.types.Entry; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.IntermediateResponse; import org.opends.server.types.Privilege; import org.opends.server.types.SearchResultEntry; import org.opends.server.types.SearchResultReference; import org.opends.server.util.TimeThread; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.loggers.Debug.*; import static org.opends.server.loggers.Error.*; import static org.opends.server.messages.CoreMessages.*; import static org.opends.server.messages.MessageHandler.*; import static org.opends.server.util.StaticUtils.*; @@ -84,6 +95,9 @@ // for this client connection. private boolean finalized; // The set of privileges assigned to this client connection. private HashSet<Privilege> privileges; // The size limit for use with this client connection. private int sizeLimit; @@ -127,6 +141,7 @@ timeLimit = DirectoryServer.getTimeLimit(); lookthroughLimit = DirectoryServer.getLookthroughLimit(); finalized = false; privileges = new HashSet<Privilege>(); } @@ -846,6 +861,7 @@ if (authenticationInfo == null) { this.authenticationInfo = new AuthenticationInfo(); privileges = new HashSet<Privilege>(); } else { @@ -869,11 +885,18 @@ DirectoryServer.getAuthenticatedUsers().put( authZEntry.getDN(), this); } updatePrivileges(authNEntry, authenticationInfo.isRoot()); } else if (authZEntry != null) else { DirectoryServer.getAuthenticatedUsers().put( authZEntry.getDN(), this); if (authZEntry != null) { DirectoryServer.getAuthenticatedUsers().put( authZEntry.getDN(), this); } privileges = new HashSet<Privilege>(); } } } @@ -908,11 +931,13 @@ { authenticationInfo = authenticationInfo.duplicate(newEntry, authZEntry); updatePrivileges(newEntry, authenticationInfo.isRoot()); } else { authenticationInfo = authenticationInfo.duplicate(newEntry, newEntry); updatePrivileges(newEntry, authenticationInfo.isRoot()); } } else if ((authZEntry != null) && @@ -943,6 +968,213 @@ /** * Indicates whether the authenticated client has the specified * privilege. * * @param privilege The privilege for which to make the * determination. * @param operation The operation being processed which needs to * make the privilege determination, or * {@code null} if there is no associated * operation. * * @return {@code true} if the authenticated client has the * specified privilege, or {@code false} if not. */ public boolean hasPrivilege(Privilege privilege, Operation operation) { assert debugEnter(CLASS_NAME, "hasPrivilege", String.valueOf(privilege), String.valueOf(operation)); boolean result = privileges.contains(privilege); if (operation == null) { DN authDN = authenticationInfo.getAuthenticationDN(); int msgID = MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGE; String message = getMessage(msgID, getConnectionID(), -1L, String.valueOf(authDN), privilege.getName(), result); logError(ErrorLogCategory.ACCESS_CONTROL, ErrorLogSeverity.INFORMATIONAL, message, msgID); } else { DN authDN = authenticationInfo.getAuthenticationDN(); int msgID = MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGE; String message = getMessage(msgID, getConnectionID(), operation.getOperationID(), String.valueOf(authDN), privilege.getName(), result); logError(ErrorLogCategory.ACCESS_CONTROL, ErrorLogSeverity.INFORMATIONAL, message, msgID); } return result; } /** * Indicates whether the authenticate client has all of the * specified privileges. * * @param privileges The array of privileges for which to make the * determination. * @param operation The operation being processed which needs to * make the privilege determination, or * {@code null} if there is no associated * operation. * * @return {@code true} if the authenticated client has all of the * specified privileges, or {@code false} if not. */ public boolean hasAllPrivileges(Privilege[] privileges, Operation operation) { assert debugEnter(CLASS_NAME, "hasAllPrivileges", String.valueOf(privileges), String.valueOf(operation)); HashSet<Privilege> privSet = this.privileges; boolean result = true; StringBuilder buffer = new StringBuilder(); buffer.append("{"); for (int i=0; i < privileges.length; i++) { if (i > 0) { buffer.append(","); } buffer.append(privileges[i].getName()); if (! privSet.contains(privileges[i])) { result = false; } } buffer.append(" }"); if (operation == null) { DN authDN = authenticationInfo.getAuthenticationDN(); int msgID = MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGES; String message = getMessage(msgID, getConnectionID(), -1L, String.valueOf(authDN), buffer.toString(), result); logError(ErrorLogCategory.ACCESS_CONTROL, ErrorLogSeverity.INFORMATIONAL, message, msgID); } else { DN authDN = authenticationInfo.getAuthenticationDN(); int msgID = MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGES; String message = getMessage(msgID, getConnectionID(), operation.getOperationID(), String.valueOf(authDN), buffer.toString(), result); logError(ErrorLogCategory.ACCESS_CONTROL, ErrorLogSeverity.INFORMATIONAL, message, msgID); } return result; } /** * Updates the privileges associated with this client connection * object based on the provided entry for the authentication * identity. * * @param entry The entry for the authentication identity * associated with this client connection. * @param isRoot Indicates whether the associated user is a root * user and should automatically inherit the root * privilege set. */ private void updatePrivileges(Entry entry, boolean isRoot) { assert debugEnter(CLASS_NAME, "updatePrivileges", String.valueOf(entry)); HashSet<Privilege> newPrivileges = new HashSet<Privilege>(); HashSet<Privilege> removePrivileges = new HashSet<Privilege>(); if (isRoot) { newPrivileges.addAll(DirectoryServer.getRootPrivileges()); } AttributeType privType = DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME); List<Attribute> attrList = entry.getAttribute(privType); if (attrList != null) { for (Attribute a : attrList) { for (AttributeValue v : a.getValues()) { String privName = toLowerCase(v.getStringValue()); // If the name of the privilege is prefixed with a minus // sign, then we will take away that privilege from the // user. We'll handle that at the end so that we can make // sure it's not added back later. if (privName.startsWith("-")) { privName = privName.substring(1); Privilege p = Privilege.privilegeForName(privName); if (p == null) { // FIXME -- Generate an administrative alert. // We don't know what privilege to remove, so we'll // remove all of them. newPrivileges.clear(); privileges = newPrivileges; return; } else { removePrivileges.add(p); } } else { Privilege p = Privilege.privilegeForName(privName); if (p == null) { // FIXME -- Generate an administrative alert. } else { newPrivileges.add(p); } } } } } for (Privilege p : removePrivileges) { newPrivileges.remove(p); } privileges = newPrivileges; } /** * Retrieves an opaque set of information that may be used for * processing multi-stage SASL binds. * opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -57,6 +57,7 @@ import org.opends.server.api.AlertGenerator; import org.opends.server.api.Backend; import org.opends.server.api.ClientConnection; import org.opends.server.api.ConfigurableComponent; import org.opends.server.api.MatchingRule; import org.opends.server.config.BooleanConfigAttribute; @@ -103,6 +104,7 @@ import org.opends.server.types.NameForm; import org.opends.server.types.ObjectClass; import org.opends.server.types.ObjectClassType; import org.opends.server.types.Privilege; import org.opends.server.types.RDN; import org.opends.server.types.RestoreConfig; import org.opends.server.types.ResultCode; @@ -950,6 +952,20 @@ String.valueOf(modifyOperation)); // Make sure that the authenticated user has the necessary UPDATE_SCHEMA // privilege. ClientConnection clientConnection = modifyOperation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.UPDATE_SCHEMA, modifyOperation)) { int msgID = MSGID_SCHEMA_MODIFY_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } // At present, we only allow the addition of new attribute types, // object classes, name forms, DIT content rules, DIT structure rules, and // matching rule uses. We will not support removing or replacing existing opends/src/server/org/opends/server/backends/task/Task.java
@@ -40,6 +40,7 @@ import java.util.concurrent.locks.Lock; import org.opends.server.core.DirectoryServer; import org.opends.server.core.Operation; import org.opends.server.protocols.asn1.ASN1OctetString; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; @@ -114,6 +115,9 @@ // The time that this task was scheduled to start processing. private long scheduledStartTime; // The operation used to create this task in the server. private Operation operation; // The ID of the recurring task with which this task is associated. private String recurringTaskID; @@ -473,6 +477,40 @@ /** * Retrieves the operation used to create this task in the server. Note that * this will only be available when the task is first added to the scheduler, * and it should only be accessed from within the {@code initializeTask} * method (and even that method should not depend on it always being * available, since it will not be available if the server is restarted and * the task needs to be reinitialized). * * @return The operation used to create this task in the server, or * {@code null} if it is not available. */ public final Operation getOperation() { assert debugEnter(CLASS_NAME, "getOperation"); return operation; } /** * Specifies the operation used to create this task in the server. * * @param operation The operation used to create this task in the server. */ public final void setOperation(Operation operation) { assert debugEnter(CLASS_NAME, "setOperation", String.valueOf(operation)); this.operation = operation; } /** * Retrieves the unique identifier assigned to this task. * * @return The unique identifier assigned to this task. opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -608,7 +608,7 @@ // treat the provided entry like a scheduled task. if (parentDN.equals(scheduledTaskParentDN)) { Task task = taskScheduler.entryToScheduledTask(entry); Task task = taskScheduler.entryToScheduledTask(entry, addOperation); taskScheduler.scheduleTask(task, true); return; } opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -43,6 +43,7 @@ import org.opends.server.api.AlertGenerator; import org.opends.server.api.DirectoryThread; import org.opends.server.core.DirectoryServer; import org.opends.server.core.Operation; import org.opends.server.core.SearchOperation; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; @@ -1044,7 +1045,7 @@ { try { Task task = entryToScheduledTask(entry); Task task = entryToScheduledTask(entry, null); if (TaskState.isDone(task.getTaskState())) { completedTasks.add(task); @@ -1792,14 +1793,16 @@ * Decodes the contents of the provided entry as a scheduled task. The * resulting task will not actually be scheduled for processing. * * @param entry The entry to decode as a scheduled task. * @param entry The entry to decode as a scheduled task. * @param operation The operation used to create this task in the server, or * {@code null} if the operation is not available. * * @return The scheduled task decoded from the provided entry. * * @throws DirectoryException If the provided entry cannot be decoded as a * scheduled task. */ public Task entryToScheduledTask(Entry entry) public Task entryToScheduledTask(Entry entry, Operation operation) throws DirectoryException { assert debugEnter(CLASS_NAME, "entryToScheduledTask", @@ -1916,7 +1919,9 @@ } task.setOperation(operation); task.initializeTask(); task.setOperation(null); return task; } opends/src/server/org/opends/server/config/ConfigConstants.java
@@ -544,6 +544,15 @@ /** * The name of the configuration attribute that specifies the set of * privileges that root users should automatically be granted in the server. */ public static final String ATTR_DEFAULT_ROOT_PRIVILEGE_NAME = NAME_PREFIX_CFG + "default-root-privilege-name"; /** * The name of the configuration attribute that indicates which clients * should not be allowed to establish connections. */ @@ -3149,6 +3158,14 @@ /** * The name of the operational attribute that will appear in a user's entry to * specify the set of privileges assigned to that user. */ public static final String OP_ATTR_PRIVILEGE_NAME = "ds-privilege-name"; /** * The name of the operational attribute that will appear in a user's entry * to indicate the time that the password was last changed. */ opends/src/server/org/opends/server/core/AddOperation.java
@@ -76,6 +76,7 @@ import org.opends.server.types.LockManager; import org.opends.server.types.ObjectClass; import org.opends.server.types.OperationType; import org.opends.server.types.Privilege; import org.opends.server.types.RDN; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchFilter; @@ -1397,6 +1398,20 @@ operationalAttributes); // Check to see if the entry includes a privilege specification. If so, // then the requester must have the PRIVILEGE_CHANGE privilege. AttributeType privType = DirectoryServer.getAttributeType(OP_ATTR_PRIVILEGE_NAME, true); if (entry.hasAttribute(privType) && (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, this))) { int msgID = MSGID_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES; appendErrorMessage(getMessage(msgID)); setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); break addProcessing; } // Check to see if the entry contains one or more passwords and if they // are valid in accordance with the password policies associated with // the user. Also perform any encoding that might be required by opends/src/server/org/opends/server/core/CompareOperation.java
@@ -57,6 +57,7 @@ import org.opends.server.types.Entry; import org.opends.server.types.LockManager; import org.opends.server.types.OperationType; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchFilter; import org.opends.server.types.operation.PostOperationCompareOperation; @@ -651,6 +652,20 @@ } // If the target entry is in the server configuration, then make sure the // requester has the CONFIG_READ privilege. if (DirectoryServer.getConfigHandler().handlesEntry(entryDN) && (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, this))) { int msgID = MSGID_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES; appendErrorMessage(getMessage(msgID)); setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); skipPostOperation = true; break compareProcessing; } // Check for and handle a request to cancel this operation. if (cancelRequest != null) { opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -146,6 +146,7 @@ import org.opends.server.types.ObjectClassType; import org.opends.server.types.OperatingSystem; import org.opends.server.types.OperationType; import org.opends.server.types.Privilege; import org.opends.server.types.RDN; import org.opends.server.types.ResultCode; import org.opends.server.types.Schema; @@ -473,6 +474,9 @@ // The special backend used for the Directory Server root DSE. private RootDSEBackend rootDSEBackend; // The root DN config manager for the server. private RootDNConfigManager rootDNConfigManager; // The SASL mechanism config manager for the Directory Server. private SASLConfigManager saslConfigManager; @@ -982,7 +986,8 @@ // Initialize the root DNs. new RootDNConfigManager().initializeRootDNs(); rootDNConfigManager = new RootDNConfigManager(); rootDNConfigManager.initializeRootDNs(); // Initialize the group manager. @@ -5007,6 +5012,22 @@ /** * Retrieves the set of privileges that should automatically be granted to * root users when they authenticate. * * @return The set of privileges that should automatically be granted to root * users when they authenticate. */ public static Set<Privilege> getRootPrivileges() { assert debugEnter(CLASS_NAME, "getRootPrivileges"); return directoryServer.rootDNConfigManager.getRootPrivileges(); } /** * Retrieves the DNs for the root users configured in the Directory Server. * Note that this set should only contain the actual DNs for the root users * and not any alternate DNs. Also, the contents of the returned set must not opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -81,6 +81,7 @@ import org.opends.server.types.Modification; import org.opends.server.types.ModificationType; import org.opends.server.types.OperationType; import org.opends.server.types.Privilege; import org.opends.server.types.RDN; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchFilter; @@ -1283,6 +1284,18 @@ pwPolicyState.getPasswordAttribute())) { passwordChanged = true; if (! selfChange) { if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET, this)) { int msgID = MSGID_MODIFY_PWRESET_INSUFFICIENT_PRIVILEGES; appendErrorMessage(getMessage(msgID)); setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); break modifyProcessing; } } break; } } @@ -1371,6 +1384,22 @@ } // See if the attribute is one which controls the privileges available // for a user. If it is, then the client must have the // PRIVILEGE_CHANGE privilege. if (t.hasName(OP_ATTR_PRIVILEGE_NAME)) { if (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, this)) { int msgID = MSGID_MODIFY_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES; appendErrorMessage(getMessage(msgID)); setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); break modifyProcessing; } } // If the modification is updating the password attribute, then // perform any necessary password policy processing. This processing // should be skipped for synchronization operations. opends/src/server/org/opends/server/core/RootDNConfigManager.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.core; @@ -30,22 +30,29 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.opends.server.api.ConfigAddListener; import org.opends.server.api.ConfigChangeListener; import org.opends.server.api.ConfigDeleteListener; import org.opends.server.api.ConfigHandler; import org.opends.server.api.ConfigurableComponent; import org.opends.server.config.ConfigAttribute; import org.opends.server.config.ConfigEntry; import org.opends.server.config.ConfigException; import org.opends.server.config.DNConfigAttribute; import org.opends.server.config.MultiChoiceConfigAttribute; import org.opends.server.types.ConfigChangeResult; import org.opends.server.types.DirectoryException; import org.opends.server.types.DN; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.InitializationException; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import static org.opends.server.config.ConfigConstants.*; @@ -66,7 +73,8 @@ * DN for use when binding to the server. */ public class RootDNConfigManager implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener implements ConfigChangeListener, ConfigAddListener, ConfigDeleteListener, ConfigurableComponent { /** * The fully-qualified name of this class for debugging purposes. @@ -80,6 +88,13 @@ // bind DNs for that user. private ConcurrentHashMap<DN,List<DN>> bindMappings; // The DN of the entry that serves as the base for the root DN // configuration entries. private DN rootDNConfigBaseDN; // The set of privileges that will be automatically inherited by root users. private LinkedHashSet<Privilege> rootPrivileges; /** @@ -116,8 +131,8 @@ ConfigEntry baseEntry; try { DN configBase = DN.decode(DN_ROOT_DN_CONFIG_BASE); baseEntry = configHandler.getConfigEntry(configBase); rootDNConfigBaseDN = DN.decode(DN_ROOT_DN_CONFIG_BASE); baseEntry = configHandler.getConfigEntry(rootDNConfigBaseDN); } catch (Exception e) { @@ -136,11 +151,62 @@ } // Get the set of privileges that root users should have by default. rootPrivileges = new LinkedHashSet<Privilege>( Privilege.getDefaultRootPrivileges()); int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; MultiChoiceConfigAttribute rootPrivStub = new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, getMessage(msgID), false, true, false, Privilege.getPrivilegeNames()); try { MultiChoiceConfigAttribute rootPrivAttr = (MultiChoiceConfigAttribute) baseEntry.getConfigAttribute(rootPrivStub); if (rootPrivAttr != null) { ArrayList<Privilege> privList = new ArrayList<Privilege>(); for (String value : rootPrivAttr.activeValues()) { String privName = toLowerCase(value); Privilege p = Privilege.privilegeForName(privName); if (p == null) { msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE; String message = getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, String.valueOf(rootDNConfigBaseDN), String.valueOf(value)); logError(ErrorLogCategory.CONFIGURATION, ErrorLogSeverity.SEVERE_WARNING, message, msgID); } else { privList.add(p); } } rootPrivileges = new LinkedHashSet<Privilege>(privList); } } catch (Exception e) { assert debugException(CLASS_NAME, "initializeRootDNs", e); msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES; String message = getMessage(msgID, stackTraceToSingleLineString(e)); throw new InitializationException(msgID, message, e); } // Register with the configuration base entry as an add and delete listener. // So that we will be notified of attempts to create or remove root DN // entries. // entries. Also, register with the server as a configurable component so // that we can detect and apply any changes to the root baseEntry.registerAddListener(this); baseEntry.registerDeleteListener(this); DirectoryServer.registerConfigurableComponent(this); // See if the base entry has any children. If not, then we don't need to @@ -206,6 +272,22 @@ /** * Retrieves the set of privileges that should automatically be granted to * root users when they authenticate. * * @return The set of privileges that should automatically be granted to root * users when they authenticate. */ public Set<Privilege> getRootPrivileges() { assert debugEnter(CLASS_NAME, "getRootPrivileges"); return rootPrivileges; } /** * Indicates whether the configuration entry that will result from a proposed * modification is acceptable to this change listener. * @@ -638,5 +720,225 @@ return new ConfigChangeResult(resultCode, adminActionRequired); } /** * Retrieves the DN of the configuration entry with which this * component is associated. * * @return The DN of the configuration entry with which this * component is associated. */ public DN getConfigurableComponentEntryDN() { assert debugEnter(CLASS_NAME, "getConfigurableComponentEntryDN"); return rootDNConfigBaseDN; } /** * Retrieves the set of configuration attributes that are associated * with this configurable component. * * @return The set of configuration attributes that are associated * with this configurable component. */ public List<ConfigAttribute> getConfigurationAttributes() { assert debugEnter(CLASS_NAME, "getConfigurationAttributes"); LinkedList<ConfigAttribute> attrList = new LinkedList<ConfigAttribute>(); LinkedList<String> currentValues = new LinkedList<String>(); for (Privilege p : rootPrivileges) { currentValues.add(p.getName()); } int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; attrList.add(new MultiChoiceConfigAttribute( ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, getMessage(msgID), false, true, false, Privilege.getPrivilegeNames(), currentValues)); return attrList; } /** * Indicates whether the provided configuration entry has an * acceptable configuration for this component. If it does not, * then detailed information about the problem(s) should be added to * the provided list. * * @param configEntry The configuration entry for which to * make the determination. * @param unacceptableReasons A list that can be used to hold * messages about why the provided * entry does not have an acceptable * configuration. * * @return <CODE>true</CODE> if the provided entry has an * acceptable configuration for this component, or * <CODE>false</CODE> if not. */ public boolean hasAcceptableConfiguration(ConfigEntry configEntry, List<String> unacceptableReasons) { assert debugEnter(CLASS_NAME, "hasAcceptableConfiguration", String.valueOf(configEntry), "List<String>"); int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; MultiChoiceConfigAttribute rootPrivStub = new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, getMessage(msgID), false, true, false, Privilege.getPrivilegeNames()); try { MultiChoiceConfigAttribute rootPrivAttr = (MultiChoiceConfigAttribute) configEntry.getConfigAttribute(rootPrivStub); if (rootPrivAttr != null) { for (String value : rootPrivAttr.activeValues()) { String privName = toLowerCase(value); Privilege p = Privilege.privilegeForName(privName); if (p == null) { msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE; String message = getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, String.valueOf(rootDNConfigBaseDN), String.valueOf(value)); unacceptableReasons.add(message); return false; } } } } catch (Exception e) { assert debugException(CLASS_NAME, "initializeRootDNs", e); msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES; String message = getMessage(msgID, stackTraceToSingleLineString(e)); unacceptableReasons.add(message); return false; } // If we've gotten here, then everything looks OK. return true; } /** * Makes a best-effort attempt to apply the configuration contained * in the provided entry. Information about the result of this * processing should be added to the provided message list. * Information should always be added to this list if a * configuration change could not be applied. If detailed results * are requested, then information about the changes applied * successfully (and optionally about parameters that were not * changed) should also be included. * * @param configEntry The entry containing the new * configuration to apply for this * component. * @param detailedResults Indicates whether detailed information * about the processing should be added to * the list. * * @return Information about the result of the configuration * update. */ public ConfigChangeResult applyNewConfiguration(ConfigEntry configEntry, boolean detailedResults) { assert debugEnter(CLASS_NAME, "applyNewConfiguration", String.valueOf(configEntry), String.valueOf(detailedResults)); ResultCode resultCode = ResultCode.SUCCESS; ArrayList<String> messages = new ArrayList<String>(); boolean adminActionRequired = false; LinkedHashSet<Privilege> newRootPrivileges = new LinkedHashSet<Privilege>(Privilege.getDefaultRootPrivileges()); int msgID = MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE; MultiChoiceConfigAttribute rootPrivStub = new MultiChoiceConfigAttribute(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, getMessage(msgID), false, true, false, Privilege.getPrivilegeNames()); try { MultiChoiceConfigAttribute rootPrivAttr = (MultiChoiceConfigAttribute) configEntry.getConfigAttribute(rootPrivStub); if (rootPrivAttr != null) { ArrayList<Privilege> privList = new ArrayList<Privilege>(); for (String value : rootPrivAttr.activeValues()) { String privName = toLowerCase(value); Privilege p = Privilege.privilegeForName(privName); if (p == null) { if (resultCode == ResultCode.SUCCESS) { resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; } msgID = MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE; messages.add(getMessage(msgID, ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, String.valueOf(rootDNConfigBaseDN), String.valueOf(value))); } else { privList.add(p); } } newRootPrivileges = new LinkedHashSet<Privilege>(privList); } } catch (Exception e) { assert debugException(CLASS_NAME, "initializeRootDNs", e); if (resultCode == ResultCode.SUCCESS) { resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; } msgID = MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES; messages.add(getMessage(msgID, stackTraceToSingleLineString(e))); } if (resultCode == ResultCode.SUCCESS) { rootPrivileges = newRootPrivileges; if (detailedResults) { msgID = MSGID_CONFIG_ROOTDN_UPDATED_PRIVILEGES; messages.add(getMessage(msgID)); } } return new ConfigChangeResult(resultCode, adminActionRequired, messages); } } opends/src/server/org/opends/server/extensions/ConfigFileHandler.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.extensions; @@ -57,6 +57,7 @@ import javax.crypto.Mac; import org.opends.server.api.AlertGenerator; import org.opends.server.api.ClientConnection; import org.opends.server.api.ConfigAddListener; import org.opends.server.api.ConfigChangeListener; import org.opends.server.api.ConfigDeleteListener; @@ -72,6 +73,7 @@ import org.opends.server.core.ModifyDNOperation; import org.opends.server.core.SearchOperation; import org.opends.server.tools.LDIFModify; import org.opends.server.types.AttributeType; import org.opends.server.types.BackupConfig; import org.opends.server.types.BackupDirectory; import org.opends.server.types.BackupInfo; @@ -86,6 +88,8 @@ import org.opends.server.types.InitializationException; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.LDIFExportConfig; import org.opends.server.types.Modification; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import org.opends.server.types.RestoreConfig; import org.opends.server.types.SearchFilter; @@ -122,6 +126,18 @@ /** * The privilege array containing both the CONFIG_READ and CONFIG_WRITE * privileges. */ private static final Privilege[] CONFIG_READ_AND_WRITE = { Privilege.CONFIG_READ, Privilege.CONFIG_WRITE }; // The mapping that holds all of the configuration entries that have been read // from the LDIF file. private ConcurrentHashMap<DN,ConfigEntry> configEntries; @@ -959,6 +975,22 @@ String.valueOf(addOperation)); // If there is an add operation, then make sure that the associated user has // both the CONFIG_READ and CONFIG_WRITE privileges. if (addOperation != null) { ClientConnection clientConnection = addOperation.getClientConnection(); if (! (clientConnection.hasAllPrivileges(CONFIG_READ_AND_WRITE, addOperation))) { int msgID = MSGID_CONFIG_FILE_ADD_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } // Grab the config lock to ensure that only one config update may be in // progress at any given time. configLock.lock(); @@ -1095,6 +1127,22 @@ String.valueOf(deleteOperation)); // If there is a delete operation, then make sure that the associated user // has both the CONFIG_READ and CONFIG_WRITE privileges. if (deleteOperation != null) { ClientConnection clientConnection = deleteOperation.getClientConnection(); if (! (clientConnection.hasAllPrivileges(CONFIG_READ_AND_WRITE, deleteOperation))) { int msgID = MSGID_CONFIG_FILE_DELETE_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } // Grab the config lock to ensure that only one config update may be in // progress at any given time. configLock.lock(); @@ -1227,6 +1275,44 @@ String.valueOf(modifyOperation)); // If there is a modify operation, then make sure that the associated user // has both the CONFIG_READ and CONFIG_WRITE privileges. Also, if the // operation targets the set of root privileges then make sure the user has // the PRIVILEGE_CHANGE privilege. if (modifyOperation != null) { ClientConnection clientConnection = modifyOperation.getClientConnection(); if (! (clientConnection.hasAllPrivileges(CONFIG_READ_AND_WRITE, modifyOperation))) { int msgID = MSGID_CONFIG_FILE_MODIFY_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } AttributeType privType = DirectoryServer.getAttributeType(ATTR_DEFAULT_ROOT_PRIVILEGE_NAME, true); for (Modification m : modifyOperation.getModifications()) { if (m.getAttribute().getAttributeType().equals(privType)) { if (! clientConnection.hasPrivilege(Privilege.PRIVILEGE_CHANGE, modifyOperation)) { int msgID = MSGID_CONFIG_FILE_MODIFY_PRIVS_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } break; } } } // Grab the config lock to ensure that only one config update may be in // progress at any given time. configLock.lock(); @@ -1381,6 +1467,23 @@ String.valueOf(entry), String.valueOf(modifyDNOperation)); // If there is a modify DN operation, then make sure that the associated // user has both the CONFIG_READ and CONFIG_WRITE privileges. if (modifyDNOperation != null) { ClientConnection clientConnection = modifyDNOperation.getClientConnection(); if (! (clientConnection.hasAllPrivileges(CONFIG_READ_AND_WRITE, modifyDNOperation))) { int msgID = MSGID_CONFIG_FILE_MODDN_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } // Modify DN operations will not be allowed in the configuration, so this // will always throw an exception. int msgID = MSGID_CONFIG_FILE_MODDN_NOT_ALLOWED; @@ -1407,6 +1510,17 @@ assert debugEnter(CLASS_NAME, "search", String.valueOf(searchOperation)); // Make sure that the associated user has the CONFIG_READ privilege. ClientConnection clientConnection = searchOperation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.CONFIG_READ, searchOperation)) { int msgID = MSGID_CONFIG_FILE_SEARCH_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } // First, get the base DN for the search and make sure that it exists. DN baseDN = searchOperation.getBaseDN(); ConfigEntry baseEntry = configEntries.get(baseDN); opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -74,6 +74,7 @@ import org.opends.server.types.LockManager; import org.opends.server.types.Modification; import org.opends.server.types.ModificationType; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import static org.opends.server.config.ConfigConstants.*; @@ -494,10 +495,34 @@ // Determine whether the user is changing his own password or if it's an // administrative reset. boolean selfChange = ((userIdentity == null) || (requestorEntry == null) || userDN.equals(requestorEntry.getDN())); // administrative reset. If it's an administrative reset, then the // requester must have the PASSWORD_RESET privilege. boolean selfChange; if (userIdentity == null) { selfChange = true; } else if (requestorEntry == null) { selfChange = (oldPassword != null); } else { selfChange = userDN.equals(requestorEntry.getDN()); } if (! selfChange) { ClientConnection clientConnection = operation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.PASSWORD_RESET, operation)) { int msgID = MSGID_EXTOP_PASSMOD_INSUFFICIENT_PRIVILEGES; operation.appendErrorMessage(getMessage(msgID)); operation.setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS); return; } } // See if the account is locked. If so, then reject the request. opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -3171,6 +3171,16 @@ /** * The message ID for the message that will be used if a user attempts to * modify the server schema without the appropriate privilege. This does not * take any arguments. */ public static final int MSGID_SCHEMA_MODIFY_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 293; /** * Associates a set of generic messages with the message IDs defined in this * class. */ @@ -3397,6 +3407,9 @@ registerMessage(MSGID_SCHEMA_DELETE_NOT_SUPPORTED, "Unwilling to remove entry \"%s\" because delete " + "operations are not supported in the schema backend."); registerMessage(MSGID_SCHEMA_MODIFY_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to modify the " + "Directory Server schema."); registerMessage(MSGID_SCHEMA_MODIFY_NOT_SUPPORTED, "Unwilling to update entry \"%s\" because modify " + "operations are not yet supported in the schema " + opends/src/server/org/opends/server/messages/ConfigMessages.java
@@ -6389,6 +6389,110 @@ CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 593; /** * The message ID for the message that will be used as the description for the * default root privilege names configuration attribute. This does not take * any arguments. */ public static final int MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE = CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 594; /** * The message ID for the message that will be used if the set of root * privileges contains an unrecognized privilege. This takes three arguments, * which are the name of the attribute holding the privilege names, the DN of * the configuration entry, and the name of the unrecognized privilege. */ public static final int MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE = CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_WARNING | 595; /** * The message ID for the message that will be used if an error occurs while * attempting to determine the set of root privileges. This takes a single * argument, which is a stack trace of the exception that was caught. */ public static final int MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_SEVERE_ERROR | 596; /** * The message ID for the message that will be used to indicate that the set * of root privileges has been updated. This does not take any arguments. */ public static final int MSGID_CONFIG_ROOTDN_UPDATED_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_INFORMATIONAL | 597; /** * The message ID for the message that will be used if an attempt is made to * perform an add operation in the server configuration but the user doesn't * have the necessary privileges to do so. This does not take any arguments. */ public static final int MSGID_CONFIG_FILE_ADD_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 598; /** * The message ID for the message that will be used if an attempt is made to * perform a delete operation in the server configuration but the user doesn't * have the necessary privileges to do so. This does not take any arguments. */ public static final int MSGID_CONFIG_FILE_DELETE_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 599; /** * The message ID for the message that will be used if an attempt is made to * perform a modify operation in the server configuration but the user doesn't * have the necessary privileges to do so. This does not take any arguments. */ public static final int MSGID_CONFIG_FILE_MODIFY_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 600; /** * The message ID for the message that will be used if an attempt is made to * perform a modify DN operation in the server configuration but the user * doesn't have the necessary privileges to do so. This does not take any * arguments. */ public static final int MSGID_CONFIG_FILE_MODDN_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 601; /** * The message ID for the message that will be used if an attempt is made to * perform a search operation in the server configuration but the user doesn't * have the necessary privileges to do so. This does not take any arguments. */ public static final int MSGID_CONFIG_FILE_SEARCH_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 602; /** * The message ID for the message that will be used if an attempt is made to * modify the set of default root privileges but the user doesn't have the * necessary privileges to do so. This does not take any arguments. */ public static final int MSGID_CONFIG_FILE_MODIFY_PRIVS_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CONFIG | SEVERITY_MASK_MILD_ERROR | 603; /** * Associates a set of generic messages with the message IDs defined in this * class. @@ -6721,6 +6825,24 @@ "the server is online. The server configuration should " + "only be managed using the administration utilities " + "provided with the Directory Server."); registerMessage(MSGID_CONFIG_FILE_ADD_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to perform add " + "operations in the Directory Server configuration."); registerMessage(MSGID_CONFIG_FILE_DELETE_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to perform delete " + "operations in the Directory Server configuration."); registerMessage(MSGID_CONFIG_FILE_MODIFY_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to perform modify " + "operations in the Directory Server configuration."); registerMessage(MSGID_CONFIG_FILE_MODIFY_PRIVS_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to change the set " + "of default root privileges."); registerMessage(MSGID_CONFIG_FILE_MODDN_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to perform modify " + "DN operations in the Directory Server configuration."); registerMessage(MSGID_CONFIG_FILE_SEARCH_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to perform search " + "operations in the Directory Server configuration."); registerMessage(MSGID_CONFIG_LOGGER_CANNOT_GET_BASE, @@ -9032,6 +9154,24 @@ DN_ROOT_DN_CONFIG_BASE + " does not exist in the " + "Directory Server configuration. This entry must be " + "present for the server to function properly."); registerMessage(MSGID_CONFIG_ROOTDN_DESCRIPTION_ROOT_PRIVILEGE, "Specifies the set of privileges that should " + "automatically be assigned to root users when they " + "authenticate to the server."); registerMessage(MSGID_CONFIG_ROOTDN_UNRECOGNIZED_PRIVILEGE, "The set of default root privileges contained in " + "configuration attribute %s of entry %s contains an " + "unrecognized privilege %s."); registerMessage(MSGID_CONFIG_ROOTDN_ERROR_DETERMINING_ROOT_PRIVILEGES, "An error occurred while attempting to determine the " + "set of privileges that root users should be granted by " + "default: %s."); registerMessage(MSGID_CONFIG_ROOTDN_UPDATED_PRIVILEGES, "The set of privileges that will automatically be " + "assigned to root users has been updated. This new " + "privilege set will not apply to any existing " + "connection already authenticated as a root user, but " + "will used for any subsequent root user authentications."); registerMessage(MSGID_CONFIG_ROOTDN_ENTRY_UNACCEPTABLE, "Configuration entry %s does not contain a valid root DN " + "configuration: %s. It will be ignored."); opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -6161,6 +6161,74 @@ /** * The message ID for the message that will be used if a modify request * includes an attempt to reset another user's password by an individual that * does not have the appropriate privileges. This does not take any * arguments. */ public static final int MSGID_MODIFY_PWRESET_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 589; /** * The message ID for the message that will be used if a compare request * targets the server configuration but the requester doesn't have the * appropriate privileges. This does not take any arguments. */ public static final int MSGID_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 590; /** * The message ID for the message that will be used if an attempt is made to * add an entry with one or more privileges but the user doesn't have * sufficient privilege to update privileges. This does not take any * arguments. */ public static final int MSGID_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_ERROR | 591; /** * The message ID for the message that will be used if an attempt is made to * modify the set of privileges contained in an entry but the user doesn't * have sufficient privileges to make that change. This does not take any * arguments. */ public static final int MSGID_MODIFY_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 592; /** * The message ID for the audit message that will be generated when a client * attempts to perform a privileged operation that requires a single * privilege. This takes five arguments, which are the connection ID, the * operation ID, the authentication DN, the name of the requested privilege, * and the result of the determination. */ public static final int MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGE = CATEGORY_MASK_CORE | SEVERITY_MASK_INFORMATIONAL | 593; /** * The message ID for the audit message that will be generated when a client * attempts to perform a privileged operation that requires a multiple * privileges. This takes five arguments, which are the connection ID, the * operation ID, the authentication DN, a formatted list of the names of the * requested privileges, and the result of the determination. */ public static final int MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGES = CATEGORY_MASK_CORE | SEVERITY_MASK_INFORMATIONAL | 594; /** * Associates a set of generic messages with the message IDs defined * in this class. */ @@ -6932,6 +7000,9 @@ "attribute %s that is contained in the entry's RDN. " + "All attributes used in the RDN must also be provided in " + "the attribute list for the entry."); registerMessage(MSGID_ADD_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to add entries " + "that include privileges."); registerMessage(MSGID_ADD_NOOP, "The add operation was not actually performed in the " + "Directory Server backend because the LDAP no-op control " + @@ -7047,6 +7118,9 @@ "plugin working on referral %s."); registerMessage(MSGID_COMPARE_CONFIG_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to access the " + "server configuration."); registerMessage(MSGID_COMPARE_CANNOT_LOCK_ENTRY, "The Directory Server was unable to obtain a read " + "lock on entry %s after multiple attempts. Processing " + @@ -7295,6 +7369,9 @@ "contained a critical control with OID %s that is not " + "supported by the Directory Server for this type of " + "operation."); registerMessage(MSGID_MODIFY_PWRESET_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to reset user " + "passwords."); registerMessage(MSGID_MODIFY_MUST_CHANGE_PASSWORD, "You must change your password before you will be " + "allowed to perform any other operations."); @@ -7306,6 +7383,9 @@ "Entry %s cannot be modified because the modification " + "attempted to set one or more new values for attribute " + "%s which is marked OBSOLETE in the server schema."); registerMessage(MSGID_MODIFY_CHANGE_PRIVILEGE_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to modify the " + "set of privileges contained in an entry."); registerMessage(MSGID_MODIFY_PASSWORDS_CANNOT_HAVE_OPTIONS, "Attributes used to hold user passwords are not allowed " + "to have any attribute options."); @@ -8337,6 +8417,12 @@ "Terminating the client connection because its " + "associated authentication or authorization entry %s has " + "been deleted."); registerMessage(MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGE, "hasPrivilege determination for connID=%d opID=%d " + "requesterDN=\"%s\" privilege=\"%s\" result=%b"); registerMessage(MSGID_CLIENTCONNECTION_AUDIT_HASPRIVILEGES, "hasPrivilege determination for connID=%d opID=%d " + "requesterDN=\"%s\" privilegeSet=\"%s\" result=%b"); } } opends/src/server/org/opends/server/messages/ExtensionsMessages.java
@@ -4140,6 +4140,16 @@ /** * The message ID for the message that will be used if a password modify * extended operation is requested to reset another user's password by a * client without sufficient privileges. This does not take any arguments. */ public static final int MSGID_EXTOP_PASSMOD_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_EXTENSIONS | SEVERITY_MASK_MILD_ERROR | 392; /** * Associates a set of generic messages with the message IDs defined in this * class. */ @@ -4396,10 +4406,13 @@ registerMessage(MSGID_EXTOP_PASSMOD_INVALID_OLD_PASSWORD, "The password modify extended operation cannot be " + "processed because the current password provided for the " + "use is invalid."); "user is invalid."); registerMessage(MSGID_EXTOP_PASSMOD_CANNOT_GET_PW_POLICY, "An error occurred while attempting to get the " + "password policy for user %s: %s."); registerMessage(MSGID_EXTOP_PASSMOD_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to perform " + "password reset operations."); registerMessage(MSGID_EXTOP_PASSMOD_ACCOUNT_DISABLED, "The user account has been administratively disabled."); registerMessage(MSGID_EXTOP_PASSMOD_ACCOUNT_LOCKED, opends/src/server/org/opends/server/messages/TaskMessages.java
@@ -122,9 +122,9 @@ /** * The message ID for the shutdown message that will be used if the server is * unable to obtain a write lock on the server schema. This takes a single * argument, which is the DN of the schema entry. * The message ID for the message that will be used if the server is unable to * obtain a write lock on the server schema. This takes a single argument, * which is the DN of the schema entry. */ public static final int MSGID_TASK_ADDSCHEMAFILE_CANNOT_LOCK_SCHEMA = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 9; @@ -132,6 +132,76 @@ /** * The message ID for the message that will be used an attempt is made to * invoke the add schema file task by a user that does not have the required * privileges. This does not take any arguments. */ public static final int MSGID_TASK_ADDSCHEMAFILE_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 10; /** * The message ID for the message that will be used an attempt is made to * invoke the backend backup task by a user that does not have the required * privileges. This does not take any arguments. */ public static final int MSGID_TASK_BACKUP_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 11; /** * The message ID for the message that will be used an attempt is made to * invoke the backend restore task by a user that does not have the required * privileges. This does not take any arguments. */ public static final int MSGID_TASK_RESTORE_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 12; /** * The message ID for the message that will be used an attempt is made to * invoke the LDIF import task by a user that does not have the required * privileges. This does not take any arguments. */ public static final int MSGID_TASK_LDIFIMPORT_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 13; /** * The message ID for the message that will be used an attempt is made to * invoke the LDIF export task by a user that does not have the required * privileges. This does not take any arguments. */ public static final int MSGID_TASK_LDIFEXPORT_INSUFFICIENT_PRIVILEGES = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 14; /** * The message ID for the message that will be used an attempt is made to * invoke the server shutdown task to restart the server by a user that does * not have the required privileges. This does not take any arguments. */ public static final int MSGID_TASK_SHUTDOWN_INSUFFICIENT_RESTART_PRIVILEGES = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 15; /** * The message ID for the message that will be used an attempt is made to * invoke the server shutdown task to shut down the server by a user that does * not have the required privileges. This does not take any arguments. */ public static final int MSGID_TASK_SHUTDOWN_INSUFFICIENT_SHUTDOWN_PRIVILEGES = CATEGORY_MASK_TASK | SEVERITY_MASK_SEVERE_ERROR | 16; /** * Associates a set of generic messages with the message IDs defined in this * class. */ @@ -149,8 +219,17 @@ registerMessage(MSGID_TASK_SHUTDOWN_CUSTOM_MESSAGE, "The Directory Server shutdown process has been " + "initiated by task %s: %s"); registerMessage(MSGID_TASK_SHUTDOWN_INSUFFICIENT_RESTART_PRIVILEGES, "You do not have sufficient privileges to initiate a " + "Directory Server restart."); registerMessage(MSGID_TASK_SHUTDOWN_INSUFFICIENT_SHUTDOWN_PRIVILEGES, "You do not have sufficient privileges to initiate a " + "Directory Server shutdown."); registerMessage(MSGID_TASK_ADDSCHEMAFILE_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to modify the " + "server schema."); registerMessage(MSGID_TASK_ADDSCHEMAFILE_NO_FILENAME, "Unable to add one or more files to the server schema " + "because no schema file names were provided in " + @@ -170,6 +249,20 @@ "Unable to add one or more files to the server schema " + "because the server was unable to obtain a write lock on " + "the schema entry %s after multiple attempts."); registerMessage(MSGID_TASK_BACKUP_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to initiate a " + "Directory Server backup."); registerMessage(MSGID_TASK_RESTORE_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to initiate a " + "Directory Server restore."); registerMessage(MSGID_TASK_LDIFIMPORT_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to initiate an " + "LDIF import."); registerMessage(MSGID_TASK_LDIFEXPORT_INSUFFICIENT_PRIVILEGES, "You do not have sufficient privileges to initiate an " + "LDIF export."); } } opends/src/server/org/opends/server/protocols/internal/InternalClientConnection.java
@@ -201,7 +201,6 @@ LinkedHashMap<AttributeType,List<Attribute>> operationalAttrs = new LinkedHashMap<AttributeType,List<Attribute>>(); // FIXME -- Add privileges here. DN internalUserDN = DN.decode(fullDNString); @@ -211,6 +210,7 @@ this.authenticationInfo = new AuthenticationInfo(internalUserEntry, true); super.setAuthenticationInfo(authenticationInfo); } catch (DirectoryException de) { opends/src/server/org/opends/server/tasks/AddSchemaFileTask.java
@@ -33,10 +33,12 @@ import java.util.TreeSet; import java.util.concurrent.locks.Lock; import org.opends.server.api.ClientConnection; import org.opends.server.backends.task.Task; import org.opends.server.backends.task.TaskState; import org.opends.server.config.ConfigException; import org.opends.server.core.DirectoryServer; import org.opends.server.core.Operation; import org.opends.server.core.SchemaConfigManager; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; @@ -48,6 +50,7 @@ import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.InitializationException; import org.opends.server.types.LockManager; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import org.opends.server.types.Schema; @@ -88,6 +91,23 @@ { assert debugEnter(CLASS_NAME, "initializeTask"); // If the client connection is available, then make sure the associated // client has the UPDATE_SCHEMA privilege. Operation operation = getOperation(); if (operation != null) { ClientConnection clientConnection = operation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.UPDATE_SCHEMA, operation)) { int msgID = MSGID_TASK_ADDSCHEMAFILE_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } // Get the attribute that specifies which schema file(s) to add. Entry taskEntry = getTaskEntry(); AttributeType attrType = DirectoryServer.getAttributeType( opends/src/server/org/opends/server/tasks/BackupTask.java
@@ -22,13 +22,14 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.tasks; import static org.opends.server.loggers.Debug.debugEnter; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.core.DirectoryServer.getAttributeType; import static org.opends.server.messages.TaskMessages.*; import static org.opends.server.messages.ToolMessages.*; import static org.opends.server.messages.MessageHandler.getMessage; import static org.opends.server.util.ServerConstants.DATE_FORMAT_UTC_TIME; @@ -41,7 +42,9 @@ import org.opends.server.backends.task.TaskState; import org.opends.server.core.DirectoryServer; import org.opends.server.core.LockFileManager; import org.opends.server.core.Operation; import org.opends.server.api.Backend; import org.opends.server.api.ClientConnection; import org.opends.server.config.ConfigEntry; import org.opends.server.config.ConfigException; import org.opends.server.types.Attribute; @@ -52,6 +55,8 @@ import org.opends.server.types.Entry; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import java.util.ArrayList; import java.util.Date; @@ -104,7 +109,20 @@ assert debugEnter(CLASS_NAME, "initializeTask"); // FIXME -- Do we need any special authorization here? // If the client connection is available, then make sure the associated // client has the BACKEND_BACKUP privilege. Operation operation = getOperation(); if (operation != null) { ClientConnection clientConnection = operation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.BACKEND_BACKUP, operation)) { int msgID = MSGID_TASK_BACKUP_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } Entry taskEntry = getTaskEntry(); opends/src/server/org/opends/server/tasks/ExportTask.java
@@ -22,13 +22,14 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.tasks; import static org.opends.server.loggers.Debug.debugEnter; import static org.opends.server.core.DirectoryServer.getAttributeType; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.messages.TaskMessages.*; import static org.opends.server.messages.ToolMessages.*; import static org.opends.server.messages.MessageHandler.getMessage; import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; @@ -37,7 +38,9 @@ import org.opends.server.backends.task.TaskState; import org.opends.server.core.DirectoryServer; import org.opends.server.core.LockFileManager; import org.opends.server.core.Operation; import org.opends.server.api.Backend; import org.opends.server.api.ClientConnection; import org.opends.server.config.ConfigEntry; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; @@ -48,6 +51,8 @@ import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.ExistingFileBehavior; import org.opends.server.types.LDIFExportConfig; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchFilter; import java.util.ArrayList; @@ -90,7 +95,20 @@ assert debugEnter(CLASS_NAME, "initializeTask"); // FIXME -- Do we need any special authorization here? // If the client connection is available, then make sure the associated // client has the LDIF_EXPORT privilege. Operation operation = getOperation(); if (operation != null) { ClientConnection clientConnection = operation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.LDIF_EXPORT, operation)) { int msgID = MSGID_TASK_LDIFEXPORT_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } Entry taskEntry = getTaskEntry(); opends/src/server/org/opends/server/tasks/ImportTask.java
@@ -22,10 +22,11 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.tasks; import static org.opends.server.messages.TaskMessages.*; import static org.opends.server.messages.ToolMessages.*; import static org.opends.server.messages.MessageHandler.getMessage; import static org.opends.server.loggers.Debug.*; @@ -38,7 +39,9 @@ import org.opends.server.backends.task.TaskState; import org.opends.server.core.DirectoryServer; import org.opends.server.core.LockFileManager; import org.opends.server.core.Operation; import org.opends.server.api.Backend; import org.opends.server.api.ClientConnection; import org.opends.server.config.ConfigEntry; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; @@ -49,6 +52,8 @@ import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.ExistingFileBehavior; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import org.opends.server.types.SearchFilter; import java.util.HashSet; @@ -95,7 +100,20 @@ assert debugEnter(CLASS_NAME, "initializeTask"); // FIXME -- Do we need any special authorization here? // If the client connection is available, then make sure the associated // client has the LDIF_IMPORT privilege. Operation operation = getOperation(); if (operation != null) { ClientConnection clientConnection = operation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.LDIF_IMPORT, operation)) { int msgID = MSGID_TASK_LDIFIMPORT_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } Entry taskEntry = getTaskEntry(); opends/src/server/org/opends/server/tasks/RestoreTask.java
@@ -22,13 +22,14 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.tasks; import static org.opends.server.loggers.Debug.debugEnter; import static org.opends.server.core.DirectoryServer.getAttributeType; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.messages.TaskMessages.*; import static org.opends.server.messages.ToolMessages.*; import static org.opends.server.messages.MessageHandler.getMessage; import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; @@ -39,7 +40,9 @@ import org.opends.server.backends.task.TaskState; import org.opends.server.core.DirectoryServer; import org.opends.server.core.LockFileManager; import org.opends.server.core.Operation; import org.opends.server.api.Backend; import org.opends.server.api.ClientConnection; import org.opends.server.config.ConfigEntry; import org.opends.server.config.ConfigException; import org.opends.server.types.Attribute; @@ -51,7 +54,9 @@ import org.opends.server.types.Entry; import org.opends.server.types.ErrorLogCategory; import org.opends.server.types.ErrorLogSeverity; import org.opends.server.types.Privilege; import org.opends.server.types.RestoreConfig; import org.opends.server.types.ResultCode; import java.util.List; import java.io.File; @@ -84,7 +89,20 @@ assert debugEnter(CLASS_NAME, "initializeTask"); // FIXME -- Do we need any special authorization here? // If the client connection is available, then make sure the associated // client has the BACKEND_RESTORE privilege. Operation operation = getOperation(); if (operation != null) { ClientConnection clientConnection = operation.getClientConnection(); if (! clientConnection.hasPrivilege(Privilege.BACKEND_RESTORE, operation)) { int msgID = MSGID_TASK_RESTORE_INSUFFICIENT_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } Entry taskEntry = getTaskEntry(); opends/src/server/org/opends/server/tasks/ShutdownTask.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.tasks; @@ -31,14 +31,18 @@ import java.util.LinkedHashSet; import java.util.List; import org.opends.server.api.ClientConnection; import org.opends.server.backends.task.Task; import org.opends.server.backends.task.TaskState; import org.opends.server.core.DirectoryServer; import org.opends.server.core.Operation; import org.opends.server.types.Attribute; import org.opends.server.types.AttributeType; import org.opends.server.types.AttributeValue; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.Privilege; import org.opends.server.types.ResultCode; import static org.opends.server.config.ConfigConstants.*; import static org.opends.server.loggers.Debug.*; @@ -88,9 +92,6 @@ assert debugEnter(CLASS_NAME, "initializeTask"); // FIXME -- Do we need any special authorization here? // See if the entry contains a shutdown message. If so, then use it. // Otherwise, use a default message. Entry taskEntry = getTaskEntry(); @@ -132,6 +133,38 @@ valueString.equals("on") || valueString.equals("1")); } } // If the client connection is available, then make sure the associated // client has either the SERVER_SHUTDOWN or SERVER_RESTART privilege, based // on the appropriate action. Operation operation = getOperation(); if (operation != null) { ClientConnection clientConnection = operation.getClientConnection(); if (restart) { if (! clientConnection.hasPrivilege(Privilege.SERVER_RESTART, operation)) { int msgID = MSGID_TASK_SHUTDOWN_INSUFFICIENT_RESTART_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } else { if (! clientConnection.hasPrivilege(Privilege.SERVER_SHUTDOWN, operation)) { int msgID = MSGID_TASK_SHUTDOWN_INSUFFICIENT_SHUTDOWN_PRIVILEGES; String message = getMessage(msgID); throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, message, msgID); } } } } opends/src/server/org/opends/server/tools/LDAPPasswordModify.java
@@ -22,7 +22,7 @@ * CDDL HEADER END * * * Portions Copyright 2006 Sun Microsystems, Inc. * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.tools; @@ -617,6 +617,11 @@ requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_OLD_PASSWORD, currentPW.getValue())); } else if (currentPWFile.isPresent()) { requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_OLD_PASSWORD, currentPWFile.getValue())); } else if (provideDNForAuthzID.isPresent()) { requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_OLD_PASSWORD, @@ -628,6 +633,11 @@ requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_NEW_PASSWORD, newPW.getValue())); } else if (newPWFile.isPresent()) { requestElements.add(new ASN1OctetString(TYPE_PASSWORD_MODIFY_NEW_PASSWORD, newPWFile.getValue())); } ASN1OctetString requestValue = new ASN1OctetString(new ASN1Sequence(requestElements).encode()); opends/src/server/org/opends/server/types/Privilege.java
New file @@ -0,0 +1,389 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.server.types; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import static org.opends.server.util.StaticUtils.*; /** * This class implements an enumeration that defines the set of * privileges available in the Directory Server. */ public enum Privilege { /** * The privilege that provides the ability to bypass access control * evaluation. */ BYPASS_ACL("bypass-acl"), /** * The privilege that provides the ability to modify access control * rules. */ MODIFY_ACL("modify-acl"), /** * The privilege that provides the ability to read the server * configuration. */ CONFIG_READ("config-read"), /** * The privilege that provides the ability to update the server * configuration. */ CONFIG_WRITE("config-write"), /** * The privilege that provides the ability to perform read * operations via JMX. */ JMX_READ("jmx-read"), /** * The privilege that provides the ability to perform write * operations via JMX. */ JMX_WRITE("jmx-write"), /** * The privilege that provides the ability to subscribe to JMX * notifications. */ JMX_NOTIFY("jmx-notify"), /** * The privilege that provides the ability to perform LDIF import * operations. */ LDIF_IMPORT("ldif-import"), /** * The privilege that provides the ability to perform LDIF export * operations. */ LDIF_EXPORT("ldif-export"), /** * The privilege that provides the ability to perform backend backup * operations. */ BACKEND_BACKUP("backend-backup"), /** * The privilege that provides the ability to perform backend * restore operations. */ BACKEND_RESTORE("backend-restore"), /** * The privilege that provides the ability to request a server * shutdown. */ SERVER_SHUTDOWN("server-shutdown"), /** * The privilege that provides the ability to request a server * restart. */ SERVER_RESTART("server-restart"), /** * The privilege that provides the ability to perform proxied * authorization or request an alternate authorization identity. */ PROXIED_AUTH("proxied-auth"), /** * The privilege that provides the ability to terminate arbitrary * client connections. */ DISCONNECT_CLIENT("disconnect-client"), /** * The privilege that provides the ability to cancel arbitrary * client requests. */ CANCEL_REQUEST("cancel-request"), /** * The privilege that provides the ability to request unindexed * searches. */ SEARCH_UNINDEXED("search-unindexed"), /** * The privilege that provides the ability to reset user passwords. */ PASSWORD_RESET("password-reset"), /** * The privilege that provides the ability to participate in a * data synchronization environment. */ DATA_SYNC("data-sync"), /** * The privilege that provides the ability to update the server * schema. */ UPDATE_SCHEMA("update-schema"), /** * The privilege that provides the ability to change the set of * privileges for a user, or to change the set of privileges * automatically assigned to a root user. */ PRIVILEGE_CHANGE("privilege-change"); /** * A map that will be used to hold a mapping between privilege names * and enum values. */ private static final HashMap<String,Privilege> PRIV_MAP = new HashMap<String,Privilege>(); /** * The set of privileges that will be automatically assigned to root * users if the root privilege set is not specified in the * configuration. */ private static final HashSet<Privilege> DEFAULT_ROOT_PRIV_SET = new HashSet<Privilege>(); /** * The names of the available privileges defined in this class. */ private static final HashSet<String> PRIV_NAMES = new HashSet<String>(); // The human-readable name for this privilege. private final String privilegeName; static { PRIV_MAP.put("bypass-acl", BYPASS_ACL); PRIV_MAP.put("modify-acl", MODIFY_ACL); PRIV_MAP.put("config-read", CONFIG_READ); PRIV_MAP.put("config-write", CONFIG_WRITE); PRIV_MAP.put("jmx-read", JMX_READ); PRIV_MAP.put("jmx-write", JMX_WRITE); PRIV_MAP.put("jmx-notify", JMX_NOTIFY); PRIV_MAP.put("ldif-import", LDIF_IMPORT); PRIV_MAP.put("ldif-export", LDIF_EXPORT); PRIV_MAP.put("backend-backup", BACKEND_BACKUP); PRIV_MAP.put("backend-restore", BACKEND_RESTORE); PRIV_MAP.put("server-shutdown", SERVER_SHUTDOWN); PRIV_MAP.put("server-restart", SERVER_RESTART); PRIV_MAP.put("proxied-auth", PROXIED_AUTH); PRIV_MAP.put("disconnect-client", DISCONNECT_CLIENT); PRIV_MAP.put("cancel-request", CANCEL_REQUEST); PRIV_MAP.put("search-unindexed", SEARCH_UNINDEXED); PRIV_MAP.put("password-reset", PASSWORD_RESET); PRIV_MAP.put("data-sync", DATA_SYNC); PRIV_MAP.put("update-schema", UPDATE_SCHEMA); PRIV_MAP.put("privilege-change", PRIVILEGE_CHANGE); PRIV_NAMES.add("bypass-acl"); PRIV_NAMES.add("modify-acl"); PRIV_NAMES.add("config-read"); PRIV_NAMES.add("config-write"); PRIV_NAMES.add("jmx-read"); PRIV_NAMES.add("jmx-write"); PRIV_NAMES.add("jmx-notify"); PRIV_NAMES.add("ldif-import"); PRIV_NAMES.add("ldif-export"); PRIV_NAMES.add("backend-backup"); PRIV_NAMES.add("backend-restore"); PRIV_NAMES.add("server-shutdown"); PRIV_NAMES.add("server-restart"); PRIV_NAMES.add("proxied-auth"); PRIV_NAMES.add("disconnect-client"); PRIV_NAMES.add("cancel-request"); PRIV_NAMES.add("search-unindexed"); PRIV_NAMES.add("password-reset"); PRIV_NAMES.add("data-sync"); PRIV_NAMES.add("update-schema"); PRIV_NAMES.add("privilege-change"); DEFAULT_ROOT_PRIV_SET.add(BYPASS_ACL); DEFAULT_ROOT_PRIV_SET.add(MODIFY_ACL); DEFAULT_ROOT_PRIV_SET.add(CONFIG_READ); DEFAULT_ROOT_PRIV_SET.add(CONFIG_WRITE); DEFAULT_ROOT_PRIV_SET.add(LDIF_IMPORT); DEFAULT_ROOT_PRIV_SET.add(LDIF_EXPORT); DEFAULT_ROOT_PRIV_SET.add(BACKEND_BACKUP); DEFAULT_ROOT_PRIV_SET.add(BACKEND_RESTORE); DEFAULT_ROOT_PRIV_SET.add(SERVER_SHUTDOWN); DEFAULT_ROOT_PRIV_SET.add(SERVER_RESTART); DEFAULT_ROOT_PRIV_SET.add(DISCONNECT_CLIENT); DEFAULT_ROOT_PRIV_SET.add(CANCEL_REQUEST); DEFAULT_ROOT_PRIV_SET.add(SEARCH_UNINDEXED); DEFAULT_ROOT_PRIV_SET.add(PASSWORD_RESET); DEFAULT_ROOT_PRIV_SET.add(UPDATE_SCHEMA); DEFAULT_ROOT_PRIV_SET.add(PRIVILEGE_CHANGE); } /** * Creates a new privilege with the provided name. * * @param privilegeName The human-readable name for this policy. */ private Privilege(String privilegeName) { this.privilegeName = privilegeName; } /** * Retrieves the name for this privilege. * * @return The name for this privilege. */ public String getName() { return privilegeName; } /** * Retrieves the privilege with the specified name. * * @param lowerPrivName The name of the privilege to retrieve, * formatted in all lowercase characters. * * @return The requested privilege, or {@code null} if the provided * value is not the name of a valid privilege. */ public static Privilege privilegeForName(String lowerPrivName) { return PRIV_MAP.get(lowerPrivName); } /** * Retrieves the human-readable name for this privilege. * * @return The human-readable name for this privilege. */ public String toString() { return privilegeName; } /** * Retrieves the set of available privilege names. * * @return The set of available privilege names. */ public static Set<String> getPrivilegeNames() { return PRIV_NAMES; } /** * Retrieves the set of privileges that should be automatically * granted to root users if the root privilege set is not specified * in the configuration. * * @return The set of privileges that should be automatically * granted to root users if the root privilege set is not * specified in the configuration. */ public static Set<Privilege> getDefaultRootPrivileges() { return DEFAULT_ROOT_PRIV_SET; } } opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
@@ -1778,7 +1778,7 @@ TestCaseUtils.initializeTestBackend(true); InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo()); InternalClientConnection.getRootConnection(); String attr = "ds-cfg-bind-with-dn-requires-password"; ArrayList<Modification> mods = new ArrayList<Modification>(); opends/tests/unit-tests-testng/src/server/org/opends/server/types/PrivilegeTestCase.java
New file @@ -0,0 +1,1166 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.server.types; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.util.ArrayList; import java.util.UUID; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import org.opends.server.TestCaseUtils; import org.opends.server.backends.task.Task; import org.opends.server.backends.task.TaskBackend; import org.opends.server.backends.task.TaskState; import org.opends.server.core.AddOperation; import org.opends.server.core.CompareOperation; import org.opends.server.core.DeleteOperation; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ModifyOperation; import org.opends.server.core.ModifyDNOperation; import org.opends.server.core.SchemaConfigManager; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.internal.InternalSearchOperation; import org.opends.server.tools.LDAPModify; import org.opends.server.tools.LDAPPasswordModify; import org.opends.server.types.Attribute; import org.opends.server.types.AuthenticationInfo; import org.opends.server.types.DN; import org.opends.server.types.Modification; import org.opends.server.types.ModificationType; import org.opends.server.types.RDN; import org.opends.server.types.SearchFilter; import org.opends.server.types.SearchScope; import static org.testng.Assert.*; /** * This class provides a set of test cases for the Directory Server privilege * subsystem. * * FIXME -- It will likely be necessary to also have access control rules in * place to allow operations as necessary once that functionality has * integrated into the server. */ public class PrivilegeTestCase extends TypesTestCase { /** * The DN of the user that is associated with the internal root connection. */ private static final String INTERNAL_ROOT_DN = "cn=Internal Client,cn=Root DNs,cn=config"; // An array of boolean values that indicates whether config read operations // should be successful for users in the corresponding slots of the // connections array. private boolean[] successful; // The set of client connections that should be used when performing // operations. private InternalClientConnection[] connections; /** * Make sure that the server is running and that an appropriate set of * structures are in place. * * @throws Exception If an unexpected problem occurs. */ @BeforeClass() public void setUp() throws Exception { TestCaseUtils.startServer(); TestCaseUtils.initializeTestBackend(true); TestCaseUtils.addEntries( "dn: cn=Unprivileged Root,cn=Root DNs,cn=config", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "objectClass: ds-cfg-root-dn", "cn: Unprivileged Root", "givenName: Unprivileged", "sn: Root", "userPassword: password", "ds-privilege-name: -config-read", "ds-privilege-name: -config-write", "ds-privilege-name: -password-reset", "ds-privilege-name: -update-schema", "ds-privilege-name: -ldif-import", "ds-privilege-name: -ldif-export", "ds-privilege-name: -backend-backup", "ds-privilege-name: -backend-restore", "", "dn: cn=Privileged User,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "cn: Privileged User", "givenName: Privileged", "sn: User", "userPassword: password", "ds-privilege-name: config-read", "ds-privilege-name: config-write", "ds-privilege-name: password-reset", "ds-privilege-name: update-schema", "ds-privilege-name: ldif-import", "ds-privilege-name: ldif-export", "ds-privilege-name: backend-backup", "ds-privilege-name: backend-restore", "", "dn: cn=Unprivileged User,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "cn: Unprivileged User", "givenName: Unprivileged", "sn: User", "userPassword: password", "", "dn: cn=PWReset Target,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "cn: PWReset Target", "givenName: PWReset", "sn: Target", "userPassword: password"); // FIXME -- It will likely be necessary to also have access control rules in // place to allow operations as necessary once that functionality has // integrated into the server. // Build the array of connections we will use to perform the tests. ArrayList<InternalClientConnection> connList = new ArrayList<InternalClientConnection>(); ArrayList<Boolean> successList = new ArrayList<Boolean>(); connList.add(new InternalClientConnection(new AuthenticationInfo())); successList.add(false); connList.add(InternalClientConnection.getRootConnection()); successList.add(true); String userDN = "cn=Directory Manager,cn=Root DNs,cn=config"; Entry userEntry = DirectoryServer.getEntry(DN.decode(userDN)); AuthenticationInfo authInfo = new AuthenticationInfo(userEntry, true); connList.add(new InternalClientConnection(authInfo)); successList.add(true); userDN = "cn=Unprivileged Root,cn=Root DNs,cn=config"; userEntry = DirectoryServer.getEntry(DN.decode(userDN)); authInfo = new AuthenticationInfo(userEntry, true); connList.add(new InternalClientConnection(authInfo)); successList.add(false); userDN = "cn=Unprivileged User,o=test"; userEntry = DirectoryServer.getEntry(DN.decode(userDN)); authInfo = new AuthenticationInfo(userEntry, false); connList.add(new InternalClientConnection(authInfo)); successList.add(false); userDN = "cn=Privileged User,o=test"; userEntry = DirectoryServer.getEntry(DN.decode(userDN)); authInfo = new AuthenticationInfo(userEntry, false); connList.add(new InternalClientConnection(authInfo)); successList.add(true); connections = new InternalClientConnection[connList.size()]; successful = new boolean[connections.length]; for (int i=0; i < connections.length; i++) { connections[i] = connList.get(i); successful[i] = successList.get(i); } } /** * Cleans up anything that might be left around after running the tests in * this class. * * @throws Exception If an unexpected problem occurs. */ @AfterClass() public void cleanUp() throws Exception { InternalClientConnection conn = InternalClientConnection.getRootConnection(); DeleteOperation deleteOperation = conn.processDelete( DN.decode("cn=Unprivileged Root,cn=Root DNs,cn=config")); assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); } /** * Retrieves a set of data that can be used for performing the tests. The * arguments generated for each method will be: * <OL> * <LI>A client connection to use to perform the operation</LI> * <LI>A flag indicating whether or not the operation should succeed</LI> * </OL> * * @return A set of data that can be used for performing the tests. */ @DataProvider(name = "testdata") public Object[][] getTestData() { Object[][] returnArray = new Object[connections.length][2]; for (int i=0; i < connections.length; i++) { returnArray[i][0] = connections[i]; returnArray[i][1] = successful[i]; } return returnArray; } /** * Tests to ensure that search operations in the server configuration properly * respect the CONFIG_READ privilege. * * @param conn The client connection to use to perform the search * operation. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the CONFIG_READ privilege and therefore the * search should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testConfigReadSearch(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.CONFIG_READ, null), hasPrivilege); InternalSearchOperation searchOperation = conn.processSearch(DN.decode("cn=config"), SearchScope.BASE_OBJECT, SearchFilter.createFilterFromString("(objectClass=*)")); if (hasPrivilege) { assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS); } else { assertEquals(searchOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that compare operations in the server configuration * properly respect the CONFIG_READ privilege. * * @param conn The client connection to use to perform the compare * operation. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the CONFIG_READ privilege and therefore the * compare should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testConfigReadCompare(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.CONFIG_READ, null), hasPrivilege); CompareOperation compareOperation = conn.processCompare(DN.decode("cn=config"), DirectoryServer.getAttributeType("cn"), ByteStringFactory.create("config")); if (hasPrivilege) { assertEquals(compareOperation.getResultCode(), ResultCode.COMPARE_TRUE); } else { assertEquals(compareOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that add and delete operations in the server configuration * properly respect the CONFIG_WRITE privilege. * * @param conn The client connection to use to perform the * operations. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the CONFIG_WRITE privilege and therefore the * operations should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testConfigWriteAddAndDelete(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.CONFIG_WRITE, null), hasPrivilege); Entry entry = TestCaseUtils.makeEntry( "dn: cn=Test Root,cn=Root DNs,cn=config", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "objectClass: ds-cfg-root-dn", "cn: Test Root", "givenName: Test", "sn: Root", "userPassword: password"); AddOperation addOperation = conn.processAdd(entry.getDN(), entry.getObjectClasses(), entry.getUserAttributes(), entry.getOperationalAttributes()); if (hasPrivilege) { assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); DeleteOperation deleteOperation = conn.processDelete(entry.getDN()); assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); } else { assertEquals(addOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); DeleteOperation deleteOperation = conn.processDelete( DN.decode("cn=Telex Number,cn=Syntaxes,cn=config")); assertEquals(deleteOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that modify operations in the server configuration * properly respect the CONFIG_WRITE privilege. * * @param conn The client connection to use to perform the modify * operation. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the CONFIG_WRITE privilege and therefore the * modify should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testConfigWriteModify(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.CONFIG_WRITE, null), hasPrivilege); ArrayList<Modification> mods = new ArrayList<Modification>(); mods.add(new Modification(ModificationType.REPLACE, new Attribute("ds-cfg-size-limit", "2000"))); ModifyOperation modifyOperation = conn.processModify(DN.decode("cn=config"), mods); if (hasPrivilege) { assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); mods.clear(); mods.add(new Modification(ModificationType.REPLACE, new Attribute("ds-cfg-size-limit", "1000"))); modifyOperation = conn.processModify(DN.decode("cn=config"), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } else { assertEquals(modifyOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that modify DN operations in the server configuration * properly respect the CONFIG_WRITE privilege. * * @param conn The client connection to use to perform the modify DN * operation. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the CONFIG_WRITE privilege and therefore the * modify DN should succeed (or at least get past the * privilege check, only to fail because we don't * support modify DN in the server configuration). * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testConfigWriteModifyDN(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.CONFIG_WRITE, null), hasPrivilege); ModifyDNOperation modifyDNOperation = conn.processModifyDN(DN.decode("cn=Work Queue,cn=config"), RDN.decode("cn=New RDN for Work Queue"), true, null); if (hasPrivilege) { // We don't support modify DN operations in the server configuration, but // at least we need to make sure we're getting past the privilege check. assertEquals(modifyDNOperation.getResultCode(), ResultCode.UNWILLING_TO_PERFORM); } else { assertEquals(modifyDNOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that modify operations which attempt to reset a user's * password properly respect the PASSWORD_RESET privilege. * * @param conn The client connection to use to perform the password * reset. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the PASSWORD_RESET privilege and therefore * the password reset should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testPasswordResetModify(InternalClientConnection conn, boolean hasPrivilege) throws Exception { // We've got to do this as an external operation rather than internal, so // get the bind DN and password to use from the client connection. String userDN; String userPassword; Entry authNEntry = conn.getAuthenticationInfo().getAuthenticationEntry(); if (authNEntry == null) { userDN = ""; userPassword = ""; } else if (authNEntry.getDN().toString().equalsIgnoreCase(INTERNAL_ROOT_DN)) { return; } else { userDN = authNEntry.getDN().toString(); userPassword = "password"; } assertEquals(conn.hasPrivilege(Privilege.PASSWORD_RESET, null), hasPrivilege); String path = TestCaseUtils.createTempFile( "dn: cn=PWReset Target,o=test", "changetype: modify", "replace: userPassword", "userPassword: newpassword", "", "dn: cn=PWReset Target,o=test", "changetype: modify", "replace: userPassword", "userPassword: password"); String[] args = { "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-D", userDN, "-w", userPassword, "-f", path }; int resultCode = LDAPModify.mainModify(args, false, null, null); if (hasPrivilege) { assertEquals(resultCode, 0); } else { assertEquals(resultCode, 50); } } /** * Tests to ensure that password modify extended operations which attempt to * reset a user's password properly respect the PASSWORD_RESET privilege. * * @param conn The client connection to use to perform the password * reset. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the PASSWORD_RESET privilege and therefore * the password reset should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testPasswordResetExtOp(InternalClientConnection conn, boolean hasPrivilege) throws Exception { // We've got to do this as an external operation rather than internal, so // get the bind DN and password to use from the client connection. String userDN; String userPassword; Entry authNEntry = conn.getAuthenticationInfo().getAuthenticationEntry(); if (authNEntry == null) { userDN = ""; userPassword = ""; } else if (authNEntry.getDN().toString().equalsIgnoreCase(INTERNAL_ROOT_DN)) { return; } else { userDN = authNEntry.getDN().toString(); userPassword = "password"; } assertEquals(conn.hasPrivilege(Privilege.PASSWORD_RESET, null), hasPrivilege); String[] args = { "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-D", userDN, "-w", userPassword, "-a", "dn:cn=PWReset Target,o=test", "-n", "newpassword" }; int resultCode = LDAPPasswordModify.mainPasswordModify(args, false, null, null); if (hasPrivilege) { assertEquals(resultCode, 0); args = new String[] { "-h", "127.0.0.1", "-p", String.valueOf(TestCaseUtils.getServerLdapPort()), "-D", userDN, "-w", userPassword, "-a", "dn:cn=PWReset Target,o=test", "-n", "password" }; assertEquals(LDAPPasswordModify.mainPasswordModify(args, false, null, null), 0); } else { assertEquals(resultCode, 50); } } /** * Tests to ensure that attempts to update the schema with a modify operation * will properly respect the UPDATE_SCHEMA privilege. * * @param conn The client connection to use to perform the schema * update. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the UPDATE_SCHEMA privilege and therefore * the schema update should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testUpdateSchemaModify(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.UPDATE_SCHEMA, null), hasPrivilege); String attrDefinition = "( testupdateschemaat-oid NAME 'testUpdateSchemaAT' " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " + "X-ORIGIN 'PrivilegeTestCase' )"; ArrayList<Modification> mods = new ArrayList<Modification>(); mods.add(new Modification(ModificationType.ADD, new Attribute("attributetypes", attrDefinition))); ModifyOperation modifyOperation = conn.processModify(DN.decode("cn=schema"), mods); if (hasPrivilege) { assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); mods.clear(); mods.add(new Modification(ModificationType.DELETE, new Attribute("attributetypes", attrDefinition))); modifyOperation = conn.processModify(DN.decode("cn=schema"), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); } else { assertEquals(modifyOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that attempts to update the schema with an add schema file * task will properly respect the UPDATE_SCHEMA privilege. * * @param conn The client connection to use to perform the schema * update. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the UPDATE_SCHEMA privilege and therefore * the schema update should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata") public void testUpdateSchemaAddSchemaFile(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.UPDATE_SCHEMA, null), hasPrivilege); String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath(); String identifier; Entry authNEntry = conn.getAuthenticationInfo().getAuthenticationEntry(); if (authNEntry == null) { identifier = "null"; } else { identifier = authNEntry.getDN().toString(); identifier = identifier.replace(',', '-'); identifier = identifier.replace(' ', '-'); identifier = identifier.replace('=', '-'); } String[] fileLines = { "dn: cn=schema", "objectClass: top", "objectClass: ldapSubentry", "objectClass: subschema", "attributeTypes: ( " + identifier.toLowerCase() + "-oid " + "NAME '" + identifier + "' )" }; File validFile = new File(schemaDirectory, "05-" + identifier + ".ldif"); BufferedWriter writer = new BufferedWriter(new FileWriter(validFile)); for (String line : fileLines) { writer.write(line); writer.newLine(); } writer.close(); Entry taskEntry = TestCaseUtils.makeEntry( "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks", "objectClass: top", "objectClass: ds-task", "objectClass: ds-task-add-schema-file", "ds-task-class-name: org.opends.server.tasks.AddSchemaFileTask", "ds-task-schema-file-name: 05-" + identifier + ".ldif"); AddOperation addOperation = conn.processAdd(taskEntry.getDN(), taskEntry.getObjectClasses(), taskEntry.getUserAttributes(), taskEntry.getOperationalAttributes()); if (hasPrivilege) { assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); Task task = getCompletedTask(taskEntry.getDN()); assertNotNull(task); assertTrue(TaskState.isSuccessful(task.getTaskState())); } else { assertEquals(addOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that attempts to backup the Directory Server backends * will properly respect the BACKEND_BACKUP privilege. * * @param conn The client connection to use to perform the backup. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the BACKEND_BACKUP privilege and therefore * the backup should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata", groups = { "slow" }) public void testBackupBackend(InternalClientConnection conn, boolean hasPrivilege) throws Exception { // We have to sleep here because the backup ID that gets generated will be // based on a timestamp and we don't want two in the same second. Thread.sleep(1100); assertEquals(conn.hasPrivilege(Privilege.BACKEND_BACKUP, null), hasPrivilege); Entry taskEntry = TestCaseUtils.makeEntry( "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks", "objectclass: top", "objectclass: ds-task", "objectclass: ds-task-backup", "ds-task-class-name: org.opends.server.tasks.BackupTask", "ds-backup-directory-path: bak", "ds-task-backup-all: TRUE"); AddOperation addOperation = conn.processAdd(taskEntry.getDN(), taskEntry.getObjectClasses(), taskEntry.getUserAttributes(), taskEntry.getOperationalAttributes()); if (hasPrivilege) { assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); Task task = getCompletedTask(taskEntry.getDN()); assertNotNull(task); assertTrue(TaskState.isSuccessful(task.getTaskState())); } else { assertEquals(addOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that attempts to restore the Directory Server backends * will properly respect the BACKEND_RESTORE privilege. * * @param conn The client connection to use to perform the restore. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the BACKEND_RESTORE privilege and therefore * the restore should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(enabled = false, dataProvider = "testdata", groups = { "slow" }, dependsOnMethods = { "testBackupBackend" }) public void testRestoreBackend(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.BACKEND_RESTORE, null), hasPrivilege); Entry taskEntry = TestCaseUtils.makeEntry( "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks", "objectclass: top", "objectclass: ds-task", "objectclass: ds-task-restore", "ds-task-class-name: org.opends.server.tasks.RestoreTask", "ds-backup-directory-path: bak" + File.separator + "userRoot"); AddOperation addOperation = conn.processAdd(taskEntry.getDN(), taskEntry.getObjectClasses(), taskEntry.getUserAttributes(), taskEntry.getOperationalAttributes()); if (hasPrivilege) { assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); Task task = getCompletedTask(taskEntry.getDN()); assertNotNull(task); assertTrue(TaskState.isSuccessful(task.getTaskState())); } else { assertEquals(addOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that attempts to export the contents of a Directory Server * backend will properly respect the LDIF_EXPORT privilege. * * @param conn The client connection to use to perform the export. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the LDIF_EXPORT privilege and therefore * the export should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata", groups = { "slow" }) public void testLDIFExport(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.LDIF_EXPORT, null), hasPrivilege); File tempFile = File.createTempFile("export-", ".ldif"); String tempFilePath = tempFile.getAbsolutePath(); tempFile.delete(); Entry taskEntry = TestCaseUtils.makeEntry( "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks", "objectclass: top", "objectclass: ds-task", "objectclass: ds-task-export", "ds-task-class-name: org.opends.server.tasks.ExportTask", "ds-task-export-backend-id: userRoot", "ds-task-export-ldif-file: " + tempFilePath); AddOperation addOperation = conn.processAdd(taskEntry.getDN(), taskEntry.getObjectClasses(), taskEntry.getUserAttributes(), taskEntry.getOperationalAttributes()); if (hasPrivilege) { assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); Task task = getCompletedTask(taskEntry.getDN()); assertNotNull(task); assertTrue(TaskState.isSuccessful(task.getTaskState())); tempFile.delete(); } else { assertEquals(addOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests to ensure that attempts to import into a Directory Server backend * will properly respect the LDIF_IMPORT privilege. * * @param conn The client connection to use to perform the import. * @param hasPrivilege Indicates whether the authenticated user is expected * to have the LDIF_IMPORT privilege and therefore * the import should succeed. * * @throws Exception If an unexpected problem occurs. */ @Test(dataProvider = "testdata", groups = { "slow" }) public void testLDIFImport(InternalClientConnection conn, boolean hasPrivilege) throws Exception { assertEquals(conn.hasPrivilege(Privilege.LDIF_IMPORT, null), hasPrivilege); String path = TestCaseUtils.createTempFile( "dn: dc=example,dc=com", "objectClass: top", "objectClass: domain", "dc: example"); Entry taskEntry = TestCaseUtils.makeEntry( "dn: ds-task-id=" + UUID.randomUUID() + ",cn=Scheduled Tasks,cn=Tasks", "objectclass: top", "objectclass: ds-task", "objectclass: ds-task-import", "ds-task-class-name: org.opends.server.tasks.ImportTask", "ds-task-import-backend-id: userRoot", "ds-task-import-ldif-file: " + path); AddOperation addOperation = conn.processAdd(taskEntry.getDN(), taskEntry.getObjectClasses(), taskEntry.getUserAttributes(), taskEntry.getOperationalAttributes()); if (hasPrivilege) { assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS); Task task = getCompletedTask(taskEntry.getDN()); assertNotNull(task); assertTrue(TaskState.isSuccessful(task.getTaskState())); } else { assertEquals(addOperation.getResultCode(), ResultCode.INSUFFICIENT_ACCESS_RIGHTS); } } /** * Tests the ability to update the set of privileges for a user on the fly * and have them take effect immediately. * * @throws Exception If an unexpected problem occurs. */ @Test() public void testUpdateUserPrivileges() throws Exception { InternalClientConnection rootConnection = InternalClientConnection.getRootConnection(); TestCaseUtils.addEntry( "dn: cn=Test User,o=test", "objectClass: top", "objectClass: person", "objectClass: organizationalPerson", "objectClass: inetOrgPerson", "cn: Test User", "givenName: Test", "sn: User", "userPassword: password"); Entry testEntry = DirectoryServer.getEntry(DN.decode("cn=Test User,o=test")); AuthenticationInfo authInfo = new AuthenticationInfo(testEntry, false); InternalClientConnection testConnection = new InternalClientConnection(authInfo); // Make sure the user starts out without any privileges. for (Privilege p : Privilege.values()) { assertFalse(testConnection.hasPrivilege(p, null)); } // Modify the user entry to add the CONFIG_READ privilege and verify that // the client connection reflects that. ArrayList<Modification> mods = new ArrayList<Modification>(); mods.add(new Modification(ModificationType.ADD, new Attribute("ds-privilege-name", "config-read"))); ModifyOperation modifyOperation = rootConnection.processModify(DN.decode("cn=Test User,o=test"), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); assertTrue(testConnection.hasPrivilege(Privilege.CONFIG_READ, null)); // Take the privilege away from the user and verify that it is recognized // immediately. mods.clear(); mods.add(new Modification(ModificationType.DELETE, new Attribute("ds-privilege-name", "config-read"))); modifyOperation = rootConnection.processModify(DN.decode("cn=Test User,o=test"), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); assertFalse(testConnection.hasPrivilege(Privilege.CONFIG_READ, null)); DeleteOperation deleteOperation = rootConnection.processDelete(DN.decode("cn=Test User,o=test")); assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS); } /** * Tests the ability to update the set of root privileges and have them take * effect immediately for new root connections. * * @throws Exception If an unexpected problem occurs. */ @Test() public void testUpdateRootPrivileges() throws Exception { // Make sure that a root connection doesn't have the proxied auth // privilege. DN unprivRootDN = DN.decode("cn=Unprivileged Root,cn=Root DNs,cn=config"); Entry unprivRootEntry = DirectoryServer.getEntry(unprivRootDN); AuthenticationInfo authInfo = new AuthenticationInfo(unprivRootEntry, true); InternalClientConnection unprivRootConn = new InternalClientConnection(authInfo); assertFalse(unprivRootConn.hasPrivilege(Privilege.PROXIED_AUTH, null)); // Update the set of root privileges to include proxied auth. InternalClientConnection internalRootConn = InternalClientConnection.getRootConnection(); ArrayList<Modification> mods = new ArrayList<Modification>(); mods.add(new Modification(ModificationType.ADD, new Attribute("ds-cfg-default-root-privilege-name", "proxied-auth"))); ModifyOperation modifyOperation = internalRootConn.processModify(DN.decode("cn=Root DNs,cn=config"), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); // Get a new root connection and verify that it now has proxied auth. unprivRootEntry = DirectoryServer.getEntry(unprivRootDN); authInfo = new AuthenticationInfo(unprivRootEntry, true); unprivRootConn = new InternalClientConnection(authInfo); assertTrue(unprivRootConn.hasPrivilege(Privilege.PROXIED_AUTH, null)); // Update the set of root privileges to revoke proxied auth. mods.clear(); mods.add(new Modification(ModificationType.DELETE, new Attribute("ds-cfg-default-root-privilege-name", "proxied-auth"))); modifyOperation = internalRootConn.processModify(DN.decode("cn=Root DNs,cn=config"), mods); assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS); // Get a new root connection and verify that it no longer has proxied auth. unprivRootEntry = DirectoryServer.getEntry(unprivRootDN); authInfo = new AuthenticationInfo(unprivRootEntry, true); unprivRootConn = new InternalClientConnection(authInfo); assertFalse(unprivRootConn.hasPrivilege(Privilege.PROXIED_AUTH, null)); } /** * Retrieves the specified task from the server, waiting for it to finish all * the running its going to do before returning. * * @param taskEntryDN The DN of the entry for the task to retrieve. * * @return The requested task entry. * * @throws Exception If an unexpected problem occurs. */ private Task getCompletedTask(DN taskEntryDN) throws Exception { TaskBackend taskBackend = (TaskBackend) DirectoryServer.getBackend(DN.decode("cn=tasks")); Task task = taskBackend.getScheduledTask(taskEntryDN); if (task == null) { long stopWaitingTime = System.currentTimeMillis() + 10000L; while ((task == null) && (System.currentTimeMillis() < stopWaitingTime)) { Thread.sleep(10); task = taskBackend.getScheduledTask(taskEntryDN); } } if (task == null) { throw new AssertionError("There is no such task " + taskEntryDN.toString()); } if (! TaskState.isDone(task.getTaskState())) { long stopWaitingTime = System.currentTimeMillis() + 20000L; while ((! TaskState.isDone(task.getTaskState())) && (System.currentTimeMillis() < stopWaitingTime)) { Thread.sleep(10); } } if (! TaskState.isDone(task.getTaskState())) { throw new AssertionError("Task " + taskEntryDN.toString() + " did not complete in a timely manner."); } return task; } }