Author: akarasulu Date: Mon Nov 1 00:03:59 2004 New Revision: 56217 Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/BackingStore.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/SystemPartition.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/auth/LdapPrincipal.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/BaseInterceptor.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContext.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContextFactory.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveDirContext.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveLdapContext.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/AuthorizationService.java incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/OperationalAttributeService.java incubator/directory/eve/trunk/jndi-provider/src/test/org/apache/eve/jndi/ibs/OperationalAttributeServiceTest.java Log: Changes ...
o rolled back http://nagoya.apache.org/jira/browse/DIREVE-67 o added guards against all operations except for list, search and lookup o added some utility methods to SystemPartition o replaced some code to use new utility methods in SystemPartition Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/BackingStore.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/BackingStore.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/BackingStore.java Mon Nov 1 00:03:59 2004 @@ -98,9 +98,7 @@ * retrieval. * * @param base the base distinguished/absolute name for the search/listing - * @return a NamingEnumeration containing objects of type - * <a href="http://java.sun.com/j2se/1.4.2/docs/api/ - * javax/naming/NameClassPair.html">NameClassPair</a>. + * @return a NamingEnumeration containing objects of type [EMAIL PROTECTED] Index} * @throws NamingException if there are any problems */ NamingEnumeration list( Name base ) throws NamingException; Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/SystemPartition.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/SystemPartition.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/SystemPartition.java Mon Nov 1 00:03:59 2004 @@ -43,6 +43,11 @@ { /** the default user principal or DN */ public final static String ADMIN_PRINCIPAL = "uid=admin,ou=system"; + /** the base dn under which all users reside */ + public final static String USERS_BASE_DN = "ou=users,ou=system"; + /** the base dn under which all groups reside */ + public final static String GROUPS_BASE_DN = "ou=groups,ou=system"; + /** the admin super user uid */ public final static String ADMIN_UID = "admin"; /** @@ -56,7 +61,86 @@ /** The suffix as a name. */ private final Name suffix ; - + + // ------------------------------------------------------------------------ + // S T A T I C M E T H O D S + // ------------------------------------------------------------------------ + + + /** + * Gets the DN for the base entry under which all non-admin users reside. + * A new Name instance is created and returned every time. + * + * @see #USERS_BASE_DN + * @return the users base DN + */ + public static final Name getUsersBaseDn() + { + Name usersBaseDn = null; + + try + { + usersBaseDn = new LdapName( USERS_BASE_DN ); + } + catch ( NamingException e ) + { + e.printStackTrace(); + // should never really happen since names are correct + } + + return usersBaseDn; + } + + + /** + * Gets the DN for the base entry under which all groups reside. + * A new Name instance is created and returned every time. + * + * @see #GROUPS_BASE_DN + * @return the groups base DN + */ + public static final Name getGroupsBaseDn() + { + Name groupsBaseDn = null; + + try + { + groupsBaseDn = new LdapName( GROUPS_BASE_DN ); + } + catch ( NamingException e ) + { + e.printStackTrace(); + // should never really happen since names are correct + } + + return groupsBaseDn; + } + + + /** + * Gets the DN for the admin user. + * + * @see #ADMIN_PRINCIPAL + * @return the admin user DN + */ + public static final Name getAdminDn() + { + Name adminDn = null; + + try + { + adminDn = new LdapName( ADMIN_PRINCIPAL ); + } + catch ( NamingException e ) + { + e.printStackTrace(); + // should never really happen since names are correct + } + + return adminDn; + } + + // ------------------------------------------------------------------------ // C O N S T R U C T O R S // ------------------------------------------------------------------------ Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/auth/LdapPrincipal.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/auth/LdapPrincipal.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/auth/LdapPrincipal.java Mon Nov 1 00:03:59 2004 @@ -35,7 +35,7 @@ /** the normalized distinguished name of the principal */ private final Name name; /** the no name anonymous user whose DN is the empty String */ - public static final Principal ANONYMOUS = new LdapPrincipal(); + public static final LdapPrincipal ANONYMOUS = new LdapPrincipal(); /** Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/BaseInterceptor.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/BaseInterceptor.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/BaseInterceptor.java Mon Nov 1 00:03:59 2004 @@ -25,6 +25,7 @@ import javax.naming.directory.SearchControls; import org.apache.ldap.common.filter.ExprNode; +import org.apache.eve.auth.LdapPrincipal; /** @@ -74,6 +75,19 @@ static void setInvocation( Invocation invocation ) { invocations.set( invocation ); + } + + + /** + * Gets the invocation's current context's Principal. + * + * @param invocation the current invocation context's principal + * @return the principal making the call + */ + public static LdapPrincipal getPrincipal( Invocation invocation ) + { + EveContext ctx = ( EveContext ) invocation.getContextStack().peek(); + return ctx.getPrincipal(); } Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContext.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContext.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContext.java Mon Nov 1 00:03:59 2004 @@ -18,7 +18,6 @@ import java.util.Hashtable; -import java.security.Principal; import javax.naming.*; import javax.naming.ldap.Control; @@ -31,6 +30,7 @@ import org.apache.ldap.common.message.LockableAttributesImpl; import org.apache.eve.PartitionNexus; +import org.apache.eve.auth.LdapPrincipal; /** @@ -51,7 +51,7 @@ /** The distinguished name of this Context */ private final LdapName dn; /** The Principal associated with this context */ - private Principal principal; + private LdapPrincipal principal; // ------------------------------------------------------------------------ @@ -116,7 +116,7 @@ * @param env the environment properties used by this context * @param dn the distinguished name of this context */ - protected EveContext( Principal principal, PartitionNexus nexusProxy, + protected EveContext( LdapPrincipal principal, PartitionNexus nexusProxy, Hashtable env, Name dn ) { this.dn = ( LdapName ) dn.clone(); @@ -136,7 +136,7 @@ * Gets the principal of the authenticated user which also happens to own * @return */ - public Principal getPrincipal() + public LdapPrincipal getPrincipal() { return principal; } @@ -148,7 +148,7 @@ * * @param principal the directory user principal */ - void setPrincipal( Principal principal ) + void setPrincipal( LdapPrincipal principal ) { this.principal = principal; } Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContextFactory.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContextFactory.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveContextFactory.java Mon Nov 1 00:03:59 2004 @@ -68,7 +68,8 @@ // for convenience private static final String TYPE = Context.SECURITY_AUTHENTICATION; private static final String PRINCIPAL = Context.SECURITY_PRINCIPAL; - //private static final String ADMIN = SystemPartition.ADMIN_PRINCIPAL; + private static final String ADMIN = SystemPartition.ADMIN_PRINCIPAL; + private static final Name ADMIN_NAME = SystemPartition.getAdminDn(); /** property used to shutdown the system */ public static final String SHUTDOWN_OP_ENV = "eve.operation.shutdown"; @@ -197,8 +198,7 @@ + "- this is not allowed ONLY the admin can bootstrap" ); } else if ( initialEnv.containsKey( PRINCIPAL ) && - ! initialEnv.get( PRINCIPAL ).equals( - SystemPartition.ADMIN_PRINCIPAL ) ) + ! initialEnv.get( PRINCIPAL ).equals( ADMIN ) ) { throw new EveConfigurationException( "user " + initialEnv.get( PRINCIPAL ) @@ -230,14 +230,12 @@ */ private boolean createAdminAccount() throws NamingException { - Name admin = new LdapName( SystemPartition.ADMIN_PRINCIPAL ); - /* * If the admin entry is there, then the database was already created * before so we just need to lookup the userPassword field to see if * the password matches. */ - if ( nexus.hasEntry( admin ) ) + if ( nexus.hasEntry( ADMIN_NAME ) ) { return false; } @@ -249,7 +247,7 @@ attributes.put( "objectClass", "inetOrgPerson" ); attributes.put( "uid", SystemPartition.ADMIN_UID ); attributes.put( "displayName", "Directory Superuser" ); - attributes.put( "creatorsName", SystemPartition.ADMIN_PRINCIPAL ); + attributes.put( "creatorsName", ADMIN ); attributes.put( "createTimestamp", DateUtils.getGeneralizedTime() ); attributes.put( "displayName", "Directory Superuser" ); @@ -263,7 +261,7 @@ attributes.put( "userPassword", ArrayUtils.EMPTY_BYTE_ARRAY ); } - nexus.add( SystemPartition.ADMIN_PRINCIPAL, admin, attributes ); + nexus.add( ADMIN, ADMIN_NAME, attributes ); return true; } Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveDirContext.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveDirContext.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveDirContext.java Mon Nov 1 00:03:59 2004 @@ -20,7 +20,6 @@ import java.io.IOException; import java.util.Hashtable; import java.text.ParseException; -import java.security.Principal; import javax.naming.Name; import javax.naming.ldap.Control; @@ -43,6 +42,7 @@ import org.apache.ldap.common.filter.FilterParserImpl; import org.apache.eve.PartitionNexus; +import org.apache.eve.auth.LdapPrincipal; /** @@ -82,7 +82,7 @@ * @param env the environment properties used by this context * @param dn the distinguished name of this context */ - protected EveDirContext( Principal principal, PartitionNexus nexusProxy, + protected EveDirContext( LdapPrincipal principal, PartitionNexus nexusProxy, Hashtable env, Name dn ) { super( principal, nexusProxy, env, dn ); Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveLdapContext.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveLdapContext.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/EveLdapContext.java Mon Nov 1 00:03:59 2004 @@ -18,7 +18,6 @@ import java.util.Hashtable; -import java.security.Principal; import javax.naming.NamingException; import javax.naming.Name; @@ -30,6 +29,7 @@ import org.apache.ldap.common.NotImplementedException; import org.apache.eve.PartitionNexus; +import org.apache.eve.auth.LdapPrincipal; /** @@ -68,7 +68,7 @@ * @param env the environment properties used by this context * @param dn the distinguished name of this context */ - EveLdapContext( Principal principal, PartitionNexus nexusProxy, Hashtable env, Name dn ) + EveLdapContext( LdapPrincipal principal, PartitionNexus nexusProxy, Hashtable env, Name dn ) { super( principal, nexusProxy, env, dn ); } Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/AuthorizationService.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/AuthorizationService.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/AuthorizationService.java Mon Nov 1 00:03:59 2004 @@ -17,8 +17,17 @@ package org.apache.eve.jndi.ibs; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.ModificationItem; + import org.apache.eve.RootNexus; +import org.apache.eve.SystemPartition; +import org.apache.eve.exception.EveNoPermissionException; import org.apache.eve.jndi.BaseInterceptor; +import org.apache.eve.jndi.Invocation; +import org.apache.eve.jndi.InvocationStateEnum; /** @@ -29,6 +38,11 @@ */ public class AuthorizationService extends BaseInterceptor { + /** the administrator's distinguished [EMAIL PROTECTED] Name} */ + private static final Name ADMIN_DN = SystemPartition.getAdminDn(); + /** the base distinguished [EMAIL PROTECTED] Name} for all users */ + private static final Name USER_BASE_DN = SystemPartition.getUsersBaseDn(); + /** the root nexus to all database partitions */ private final RootNexus nexus; @@ -41,5 +55,162 @@ public AuthorizationService( RootNexus nexus ) { this.nexus = nexus; + } + + + // Note: + // Lookup, search and list operations need to be handled using a filter + // and so we need access to the filter service. + + + protected void delete( Name name ) throws NamingException + { + Invocation invocation = getInvocation(); + + if ( invocation.getState() == InvocationStateEnum.PREINVOCATION ) + { + Name principalDn = getPrincipal( invocation ).getDn(); + + if ( name == ADMIN_DN || name.equals( ADMIN_DN ) ) + { + String msg = "User " + principalDn; + msg += " does not have permission to delete the admin account."; + msg += " No one not even the admin can delete this account!"; + throw new EveNoPermissionException( msg ); + } + + if ( name.startsWith( USER_BASE_DN ) && ! principalDn.equals( ADMIN_DN ) ) + { + String msg = "User " + principalDn; + msg += " does not have permission to delete the user account: "; + msg += name + ". Only the admin can delete user accounts."; + throw new EveNoPermissionException( msg ); + } + } + } + + + /** + * Note that we do nothing here. First because this is not an externally + * exposed function via the JNDI interfaces. It is used internally be the + * provider for optimization purposes so there is no reason for us to start + * to constrain it. + * + * @see BaseInterceptor#hasEntry(Name) + */ + protected void hasEntry( Name dn ) throws NamingException + { + } + + + // ------------------------------------------------------------------------ + // Entry Modification Operations + // ------------------------------------------------------------------------ + + + /** + * This policy needs to be really tight too because some attributes may + * take part in giving the user permissions to protected resources. We + * do not want users to self access these resources. As far as we're + * concerned no one but the admin needs access. + * + * @see BaseInterceptor#modify(Name, int, Attributes) + */ + protected void modify( Name dn, int modOp, Attributes mods ) throws NamingException + { + protectModifyAlterations( dn ); + } + + + /** + * This policy needs to be really tight too because some attributes may + * take part in giving the user permissions to protected resources. We + * do not want users to self access these resources. As far as we're + * concerned no one but the admin needs access. + * + * @see BaseInterceptor#modify(Name, ModificationItem[]) + */ + protected void modify( Name dn, ModificationItem[] mods ) throws NamingException + { + protectModifyAlterations( dn ); + } + + + private void protectModifyAlterations( Name dn ) throws EveNoPermissionException + { + Invocation invocation = getInvocation(); + + if ( invocation.getState() == InvocationStateEnum.PREINVOCATION ) + { + Name principalDn = getPrincipal( invocation ).getDn(); + + if ( dn.startsWith( USER_BASE_DN ) && ! principalDn.equals( ADMIN_DN ) ) + { + String msg = "User " + principalDn; + msg += " does not have permission to modify the account of the"; + msg += " user " + dn + ".\nEven the owner of an account cannot"; + msg += " modify it.\nUser accounts can only be modified by the"; + msg += " administrator."; + throw new EveNoPermissionException( msg ); + } + } + } + + + // ------------------------------------------------------------------------ + // DN altering operations are a no no for any user entry. Basically here + // are the rules of conduct to follow: + // + // o No user should have the ability to move or rename their entry + // o Only the administrator can move or rename non-admin user entries + // o The administrator entry cannot be moved or renamed by anyone + // ------------------------------------------------------------------------ + + + protected void modifyRdn( Name dn, String newRdn, boolean deleteOldRdn ) throws NamingException + { + protectDnAlterations( dn ); + } + + + protected void move( Name oriChildName, Name newParentName ) throws NamingException + { + protectDnAlterations( oriChildName ); + } + + + protected void move( Name oriChildName, Name newParentName, String newRdn, + boolean deleteOldRdn ) throws NamingException + { + protectDnAlterations( oriChildName ); + } + + + private void protectDnAlterations( Name dn ) throws EveNoPermissionException + { + Invocation invocation = getInvocation(); + + if ( invocation.getState() == InvocationStateEnum.PREINVOCATION ) + { + Name principalDn = getPrincipal( invocation ).getDn(); + + if ( dn == ADMIN_DN || dn.equals( ADMIN_DN ) ) + { + String msg = "User " + principalDn; + msg += " does not have permission to move or rename the admin"; + msg += " account. No one not even the admin can move or"; + msg += " rename " + dn + "!"; + throw new EveNoPermissionException( msg ); + } + + if ( dn.startsWith( USER_BASE_DN ) && ! principalDn.equals( ADMIN_DN ) ) + { + String msg = "User " + principalDn; + msg += " does not have permission to move or rename the user"; + msg += " account: " + dn + ". Only the admin can move or"; + msg += " rename user accounts."; + throw new EveNoPermissionException( msg ); + } + } } } Modified: incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/OperationalAttributeService.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/OperationalAttributeService.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/java/org/apache/eve/jndi/ibs/OperationalAttributeService.java Mon Nov 1 00:03:59 2004 @@ -18,14 +18,12 @@ import javax.naming.Name; -import javax.naming.Context; import javax.naming.NamingException; import javax.naming.NamingEnumeration; import javax.naming.ldap.LdapContext; import javax.naming.directory.*; import org.apache.eve.RootNexus; -import org.apache.eve.SystemPartition; import org.apache.eve.db.DbSearchResult; import org.apache.eve.db.SearchResultFilter; import org.apache.eve.jndi.Invocation; @@ -37,7 +35,6 @@ import org.apache.ldap.common.util.DateUtils; import org.apache.ldap.common.schema.AttributeType; import org.apache.ldap.common.schema.UsageEnum; -import org.apache.ldap.common.name.LdapName; /** @@ -85,7 +82,6 @@ /** a service used to filter search and lookup operations */ private final FilterService filteringService; private final AttributeTypeRegistry registry; - private static Name usersBaseDn; /** @@ -118,15 +114,6 @@ this.filteringService.addLookupFilter( LOOKUP_FILTER ); this.filteringService.addSearchResultFilter( SEARCH_FILTER ); - - try - { - usersBaseDn = new LdapName( "ou=users,ou=system" ); - } - catch ( NamingException e ) - { - // never gets thrown since the DN used is static and correct - } } @@ -141,15 +128,7 @@ if ( invocation.getState() == InvocationStateEnum.PREINVOCATION ) { - String principal; - if ( normName.startsWith( usersBaseDn ) && normName.size() > 2 ) - { - principal = upName; - } - else - { - principal = getPrincipal( invocation ); - } + String principal = getPrincipal( invocation ).toString(); BasicAttribute attribute = new BasicAttribute( "creatorsName" ); attribute.add( principal ); @@ -298,18 +277,4 @@ } - /** - * Gets the DN of the principal associated with this operation. - * - * @param invocation the invocation to get the principal for - * @return the principal as a String - * @throws NamingException if there are problems - */ - private String getPrincipal( Invocation invocation ) throws NamingException - { - String principal; - Context ctx = ( ( Context ) invocation.getContextStack().peek() ); - principal = ( String ) ctx.getEnvironment().get( Context.SECURITY_PRINCIPAL ); - return principal == null ? SystemPartition.ADMIN_PRINCIPAL : principal; - } } Modified: incubator/directory/eve/trunk/jndi-provider/src/test/org/apache/eve/jndi/ibs/OperationalAttributeServiceTest.java ============================================================================== --- incubator/directory/eve/trunk/jndi-provider/src/test/org/apache/eve/jndi/ibs/OperationalAttributeServiceTest.java (original) +++ incubator/directory/eve/trunk/jndi-provider/src/test/org/apache/eve/jndi/ibs/OperationalAttributeServiceTest.java Mon Nov 1 00:03:59 2004 @@ -125,13 +125,16 @@ * user even though the admin is creating the user. This is the basis * for some authorization rules to protect passwords. * + * NOTE THIS CHANGE WAS REVERTED SO WE ADAPTED THE TEST TO MAKE SURE THE + * CHANGE DOES NOT PERSIST! + * * @see <a href="http://nagoya.apache.org/jira/browse/DIREVE-67">JIRA Issue DIREVE-67</a> */ public void testConfirmNonAdminUserDnIsCreatorsName() throws NamingException { Attributes attributes = sysRoot.getAttributes( "uid=akarasulu,ou=users", new String[] { "creatorsName" } ); - assertEquals( "uid=akarasulu,ou=users,ou=system", - attributes.get( "creatorsName" ).get() ); + assertFalse( "uid=akarasulu,ou=users,ou=system" + .equals( attributes.get( "creatorsName" ).get() ) ); } }
