You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by tb...@apache.org on 2006/12/12 16:24:14 UTC
svn commit: r486187 [23/49] - in /directory/trunks/triplesec: ./ admin-api/
admin-api/src/ admin-api/src/main/ admin-api/src/main/java/
admin-api/src/main/java/org/ admin-api/src/main/java/org/safehaus/
admin-api/src/main/java/org/safehaus/triplesec/ a...
Added: directory/trunks/triplesec/store/pom.xml
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/pom.xml?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/pom.xml (added)
+++ directory/trunks/triplesec/store/pom.xml Tue Dec 12 07:23:31 2006
@@ -0,0 +1,173 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.safehaus.triplesec</groupId>
+ <artifactId>build</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>triplesec-store</artifactId>
+ <name>Triplesec Store</name>
+ <packaging>jar</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>triplesec-testdata</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>triplesec-profile</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>triplesec-jaas</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>nlog4j</artifactId>
+ <version>1.2.25</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.directory.server</groupId>
+ <artifactId>apacheds-kerberos-shared</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.directory.server</groupId>
+ <artifactId>apacheds-core</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.directory.shared</groupId>
+ <artifactId>shared-ldap</artifactId>
+ <version>0.9.5.3-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.directory.server</groupId>
+ <artifactId>apacheds-core-unit</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.directory.server</groupId>
+ <artifactId>apacheds-core-plugin</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <configuration>
+ <schemaSourcesDir>src/main/schema</schemaSourcesDir>
+ <schemas>
+ <schema>
+ <name>safehaus</name>
+ <pkg>org.safehaus.triplesec.store.schema</pkg>
+ <dependencies>
+ <dependency>system</dependency>
+ <dependency>core</dependency>
+ <dependency>cosine</dependency>
+ </dependencies>
+ </schema>
+ </schemas>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>no-integration-tests</id>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/*ITest.java</exclude>
+ <exclude>**/*IntegrationTest.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>validate</phase>
+ <configuration>
+ <tasks>
+ <echo>
+=================================================================
+ W A R N I N G
+ -------------
+
+Integration tests have been disabled. To enable integration
+tests run maven with the -Dintegration switch.
+=================================================================
+ </echo>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>integration</id>
+ <activation>
+ <property><name>integration</name></property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemProperties>
+ <property>
+ <name>workingDirectory</name>
+ <value>${basedir}/target/server-work</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/DefaultServerProfileStore.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/DefaultServerProfileStore.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/DefaultServerProfileStore.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/DefaultServerProfileStore.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.safehaus.triplesec.store;
+
+
+import java.util.ArrayList;
+
+import javax.naming.directory.*;
+import javax.naming.NamingException;
+import javax.naming.NamingEnumeration;
+import javax.naming.OperationNotSupportedException;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.apache.directory.shared.ldap.NotImplementedException;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.message.LockableAttributesImpl;
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
+
+import org.safehaus.profile.ServerProfile;
+
+
+/**
+ * The default Safehaus store implementation which is backed by the ApacheDS.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class DefaultServerProfileStore implements ServerProfileStore
+{
+ /** Empty array of modification items so we do not create on every time */
+ private static final ModificationItem[] EMPTY = new ModificationItem[0];
+
+ /** temporary property to lookup alternate monitor */
+ public static final String MONITOR_PROP = "org.safehaus.store.monitor";
+
+ // ------------------------------------------------------------------------
+ // Members
+ // ------------------------------------------------------------------------
+
+ /** the context under which users are created */
+ private DirContext userContext;
+
+ /** the StoreMonitor used to report notable events to */
+ private StoreMonitor monitor = new StoreMonitorAdapter();
+
+ // ------------------------------------------------------------------------
+ // C O N S T R U C T O R S
+ // ------------------------------------------------------------------------
+
+ /**
+ * Creates the embedded ApacheDS principal store.
+ *
+ * @param userContext the context under which users are created.
+ */
+ public DefaultServerProfileStore( DirContext userContext ) throws NamingException
+ {
+ this.userContext = userContext;
+ }
+
+
+ // ------------------------------------------------------------------------
+ // ServerProfileStore methods
+ // ------------------------------------------------------------------------
+
+
+ public void init() throws NamingException
+ {
+ }
+
+
+ private ProfileObjectFactory objectFactory = new ProfileObjectFactory();
+
+ public ServerProfile getProfile( KerberosPrincipal principal ) throws NamingException
+ {
+ Attributes attributes = new LockableAttributesImpl();
+ attributes.put( PRINCIPAL_ATTR, principal.getName() );
+ SearchResult result = null;
+ Attributes attrs = null;
+
+ NamingEnumeration list = userContext.search( "", attributes );
+ if ( list.hasMore() )
+ {
+ result = ( SearchResult ) list.next();
+ attrs = result.getAttributes();
+ }
+ list.close();
+
+ if ( attrs == null || result == null )
+ {
+ monitor.storeFailure( this, principal, "principal not in store" );
+ return null;
+ }
+
+ Object obj = objectFactory.getObjectInstance( null, null, null, null, attrs );
+ if ( obj instanceof ServerProfile )
+ {
+ monitor.profileAccessed( this, ( ServerProfile ) obj );
+ return ( ServerProfile ) obj;
+ }
+
+ monitor.storeFailure( this, principal, "failed to recognize principal type" );
+ return null;
+ }
+
+
+ private SearchResult getProfileEntry( KerberosPrincipal principal ) throws NamingException
+ {
+ Attributes attributes = new LockableAttributesImpl();
+ attributes.put( PRINCIPAL_ATTR, principal.getName() );
+ SearchResult result = null;
+ NamingEnumeration list = userContext.search( "", attributes );
+ if ( list.hasMore() )
+ {
+ result = ( SearchResult ) list.next();
+ }
+ list.close();
+ return result;
+ }
+
+
+ public boolean hasProfile( KerberosPrincipal principal ) throws NamingException
+ {
+ Attributes attributes = new LockableAttributesImpl();
+ attributes.put( PRINCIPAL_ATTR, principal.getName() );
+ SearchResult result = null;
+ Attributes attrs = null;
+ NamingEnumeration list = userContext.search( "", attributes );
+
+ if ( list.hasMore() )
+ {
+ result = ( SearchResult ) list.next();
+ attrs = result.getAttributes();
+ }
+ list.close();
+
+ if ( attrs == null || result == null )
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+
+ public void add( ServerProfile profile ) throws NamingException
+ {
+ userContext.bind( "uid=" + profile.getUserId(), profile );
+ monitor.profileAdded( this, profile );
+ }
+
+
+ public void delete( KerberosPrincipal principal ) throws NamingException
+ {
+ String msg = "delete in org.safehaus.triplesec.store.DefaultServerProfileStore not implemented!";
+ throw new NotImplementedException( msg );
+ }
+
+
+ public void update( KerberosPrincipal oldPrincipal, ServerProfile updated ) throws NamingException
+ {
+ String oldId = oldPrincipal.getName();
+ oldId = oldId.split( "@" )[0];
+ String oldRealm = oldPrincipal.getName();
+ oldRealm = oldRealm.split( "@" )[1];
+
+ if ( ! oldId.equals( updated.getUserId() ) || ! oldRealm.equals( updated.getRealm() ) )
+ {
+ String msg = "Attempt to move or rename existing profile not yet supported!";
+ OperationNotSupportedException onse = new OperationNotSupportedException( msg );
+ monitor.storeFailure( this, onse );
+ throw onse;
+ }
+
+ ArrayList list = new ArrayList(); // list of modification items
+ SearchResult result = getProfileEntry( oldPrincipal );
+ Attributes original = result.getAttributes();
+
+ if ( updated.getFactor() != getLong( "safehausFactor", original ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausFactor" );
+ attr.add( Long.toString( updated.getFactor() ) );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( updated.getResynchCount() != getInt( "safehausResynchCount", original ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausResynchCount" );
+ attr.add( Integer.toString( updated.getResynchCount() ) );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( updated.getFailuresInEpoch() != getInt( "safehausFailuresInEpoch", original ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausFailuresInEpoch" );
+ attr.add( Integer.toString( updated.getFailuresInEpoch() ) );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( ! updated.getUserId().equals( getString( "safehausUid", original ) ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausUid" );
+ attr.add( updated.getUserId() );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( ! updated.getRealm().equals( getString( "safehausRealm", original ) ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausRealm" );
+ attr.add( updated.getRealm() );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( ! updated.getLabel().equals( getString( "safehausLabel", original ) ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausLabel" );
+ attr.add( updated.getLabel() );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( ! updated.getSecret().equals( getString( "safehausSecret", original ) ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausSecret" );
+ attr.add( updated.getSecret() );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( updated.getInfo() != null && ! updated.getInfo().equals( getString( "safehausInfo", original ) ) )
+ {
+ Attribute attr = new LockableAttributeImpl( "safehausInfo" );
+ attr.add( updated.getInfo() );
+ list.add( new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ) );
+ }
+
+ if ( result.getObject() instanceof DirContext )
+ {
+ DirContext entryCtx = ( DirContext ) result.getObject();
+ entryCtx.modifyAttributes( "", ( ModificationItem[] ) list.toArray( EMPTY ) );
+ return;
+ }
+
+ LdapDN base = new LdapDN( userContext.getNameInNamespace() );
+ LdapDN dn = new LdapDN( result.getName() );
+ LdapDN rdn = ( LdapDN ) dn.getSuffix( base.size() );
+ ModificationItem[] mods = ( ModificationItem[] ) list.toArray( EMPTY );
+ userContext.modifyAttributes( rdn, mods );
+ monitor.profileUpdated( this, updated, mods );
+ }
+
+
+ public void setMonitor( StoreMonitor monitor )
+ {
+ this.monitor = monitor;
+ }
+
+
+ // ------------------------------------------------------------------------
+ // private utility methods
+ // ------------------------------------------------------------------------
+
+
+ private long getLong( String id, Attributes attrs ) throws NamingException
+ {
+ Attribute attr = attrs.get( id );
+ if ( attr == null || attr.size() == 0 )
+ {
+ throw new NamingException( "Attribute not found or does not have a value!" );
+ }
+ return Long.parseLong( ( String ) attr.get() );
+ }
+
+
+ private int getInt( String id, Attributes attrs ) throws NamingException
+ {
+ Attribute attr = attrs.get( id );
+ if ( attr == null || attr.size() == 0 )
+ {
+ throw new NamingException( "Attribute not found or does not have a value!" );
+ }
+ return Integer.parseInt( ( String ) attr.get() );
+ }
+
+
+ private String getString( String id, Attributes attrs ) throws NamingException
+ {
+ Attribute attr = attrs.get( id );
+
+ if ( attr == null || attr.size() == 0 )
+ {
+ throw new NamingException( "Attribute not found or does not have a value!" );
+ }
+
+ if ( attr.get() instanceof String )
+ {
+ return ( String ) attr.get();
+ }
+ else if ( attr.get() instanceof byte[] )
+ {
+ return new String( ( byte[] ) attr.get() );
+ }
+
+ return attr.get().toString();
+ }
+}
Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileObjectFactory.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileObjectFactory.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileObjectFactory.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileObjectFactory.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.safehaus.triplesec.store;
+
+
+import java.util.Hashtable;
+
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.spi.DirObjectFactory;
+
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.shared.ldap.util.StringTools;
+import org.safehaus.profile.BaseServerProfileModifier;
+
+
+/**
+ * An ObjectFactory that resusitates objects from directory attributes.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class ProfileObjectFactory implements DirObjectFactory
+{
+ private static boolean parseBoolean( String bool )
+ {
+ if ( bool.toLowerCase().equals( "true" ) )
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ public Object getObjectInstance( Object obj, Name name, Context nameCtx, Hashtable environment, Attributes attrs ) throws NamingException
+ {
+ if ( attrs == null || attrs.get( "objectClass" ) == null || ! attrs.get( "objectClass" ).contains( "safehausProfile" ) )
+ {
+ return null;
+ }
+
+ BaseServerProfileModifier modifier = new BaseServerProfileModifier();
+ modifier.setUserId( ( String ) attrs.get( "safehausUid" ).get() );
+ modifier.setRealm( ( String ) attrs.get( "safehausRealm" ).get() );
+ modifier.setLabel( ( String ) attrs.get( "safehausLabel" ).get() );
+ modifier.setTokenPin( ( String ) attrs.get( "safehausTokenPin" ).get() );
+ modifier.setFactor( Long.parseLong( ( String ) attrs.get( "safehausFactor" ).get() ) );
+
+ if ( attrs.get( KerberosAttribute.ACCOUNT_DISABLED ) != null )
+ {
+ modifier.setDisabled( parseBoolean( ( ( String )
+ attrs.get( KerberosAttribute.ACCOUNT_DISABLED ).get() ).toLowerCase() ) );
+ }
+
+ Object secret = attrs.get( "safehausSecret" ).get();
+ if ( secret instanceof String )
+ {
+ modifier.setSecret( StringTools.getBytesUtf8( ( String ) secret ) );
+ }
+ else
+ {
+ modifier.setSecret( ( byte[] ) secret );
+ }
+
+ Object password = attrs.get( "userPassword" ).get();
+ if ( password instanceof String )
+ {
+ modifier.setPassword( StringTools.getBytesUtf8( ( String ) password ) );
+ }
+ else
+ {
+ modifier.setPassword( ( byte[] ) password );
+ }
+
+ modifier.setFailuresInEpoch( Integer.parseInt( ( String ) attrs.get( "safehausFailuresInEpoch" ).get() ) );
+ modifier.setResynchCount( Integer.parseInt( ( String ) attrs.get( "safehausResynchCount" ).get() ) );
+
+ if ( attrs.get( "safehausInfo" ) != null )
+ {
+ modifier.setInfo( ( String ) attrs.get( "safehausInfo" ).get() );
+ }
+
+ if ( attrs.get( "safehausActivationKey" ) != null )
+ {
+ modifier.setActivationKey( ( String ) attrs.get( "safehausActivationKey" ).get() );
+ }
+
+ return modifier.getServerProfile();
+ }
+
+
+ public Object getObjectInstance( Object obj, Name name, Context nameCtx, Hashtable environment ) throws Exception
+ {
+ throw new UnsupportedOperationException( "Attributes required to resusitate an OTP account!" );
+ }
+}
Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileStateFactory.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileStateFactory.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileStateFactory.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ProfileStateFactory.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.safehaus.triplesec.store;
+
+
+import java.util.Hashtable;
+
+import javax.naming.Name;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.NamingEnumeration;
+import javax.naming.spi.DirStateFactory;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.Attribute;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosKey;
+
+import org.safehaus.profile.ServerProfile;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
+
+
+/**
+ * A StateFactory for a server profile.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class ProfileStateFactory implements DirStateFactory
+{
+ /** the krb5kdc schema key for a krb5KDCEntry */
+ private static final String KEY_ATTR = "krb5Key";
+
+ /** the krb5kdc schema key encryption type for a krb5KDCEntry */
+ private static final String TYPE_ATTR = "krb5EncryptionType";
+
+ /** the krb5kdc schema principal name for a krb5KDCEntry */
+ private static final String PRINCIPAL_ATTR = "krb5PrincipalName";
+
+ /** the krb5kdc schema key version identifier for a krb5KDCEntry */
+ private static final String VERSION_ATTR = "krb5KeyVersionNumber";
+
+
+ public Result getStateToBind( Object obj, Name name, Context nameCtx, Hashtable environment, Attributes inAttrs ) throws NamingException
+ {
+ ServerProfile p = ( ServerProfile ) obj;
+
+ Attributes outAttrs = new BasicAttributes( true );
+
+ if ( inAttrs != null )
+ {
+ NamingEnumeration list = inAttrs.getIDs();
+
+ while ( list.hasMore() )
+ {
+ String id = ( String ) list.next();
+
+ outAttrs.put( ( Attribute ) inAttrs.get( id ).clone() );
+ }
+ }
+
+ // process the objectClass attribute
+
+ Attribute oc = outAttrs.get( "objectClass" );
+
+ if ( oc == null )
+ {
+ oc = new LockableAttributeImpl( "objectClass" );
+
+ outAttrs.put( oc );
+ }
+
+ if ( ! oc.contains( "top" ) )
+ {
+ oc.add( "top" );
+ }
+
+ if ( ! oc.contains( "uidObject" ) )
+ {
+ oc.add( "uidObject" );
+
+ outAttrs.put( "uid", p.getUserId() );
+ }
+
+ if ( ! oc.contains( "extensibleObject" ) )
+ {
+ oc.add( "extensibleObject" );
+
+ outAttrs.put( "apacheSamType", "7" );
+ }
+
+ if ( ! oc.contains( "person" ) )
+ {
+ oc.add( "person" );
+
+ // @todo look into adding sn and cn to ServerProfiles
+ // shoot I think we're going to need to have these properties to
+ // enforce person usage - or we can blow chunks ????
+
+ outAttrs.put( "sn", p.getUserId() );
+
+ outAttrs.put( "cn", p.getUserId() );
+ }
+
+ if ( ! oc.contains( "organizationalPerson" ) )
+ {
+ oc.add( "organizationalPerson" );
+ }
+
+ if ( ! oc.contains( "inetOrgPerson" ) )
+ {
+ oc.add( "inetOrgPerson" );
+ }
+
+ if ( ! oc.contains( "krb5KDCEntry" ) )
+ {
+ oc.add( "krb5KDCEntry" );
+ String pw = p.getUserId();
+
+ if ( p.getPassword() == null )
+ {
+ outAttrs.put( "userpassword", p.getUserId() );
+ }
+
+ StringBuffer buf = new StringBuffer();
+ buf.append( p.getUserId() );
+ buf.append( "@" );
+ buf.append( p.getRealm() );
+ KerberosPrincipal principal = new KerberosPrincipal( buf.toString() );
+
+ KerberosKey key = new KerberosKey( principal, pw.toCharArray(), "DES" );
+ outAttrs.put( PRINCIPAL_ATTR, principal.getName() );
+ byte[] encodedKey = key.getEncoded();
+ outAttrs.put( KEY_ATTR, encodedKey );
+ outAttrs.put( VERSION_ATTR, Integer.toString( key.getVersionNumber() ) );
+ outAttrs.put( TYPE_ATTR, Integer.toString( key.getKeyType() ) );
+ }
+
+ if ( ! oc.contains( "safehausProfile" ) )
+ {
+ oc.add( "safehausProfile" );
+ }
+
+ // process the Profile specific attributes
+
+ outAttrs.put( new LockableAttributeImpl( "safehausUid", p.getUserId() ) );
+ outAttrs.put( new LockableAttributeImpl( "safehausRealm", p.getRealm() ) );
+ outAttrs.put( new LockableAttributeImpl( "safehausFactor", String.valueOf( p.getFactor() ) ) );
+ outAttrs.put( new LockableAttributeImpl( "safehausSecret", p.getSecret() ) );
+ outAttrs.put( new LockableAttributeImpl( "safehausLabel", p.getLabel() ) );
+ outAttrs.put( new LockableAttributeImpl( "safehausTokenPin", p.getTokenPin() ) );
+ outAttrs.put( new LockableAttributeImpl( "safehausNotifyBy", p.getNotifyBy() ) );
+ outAttrs.put( new LockableAttributeImpl( KerberosAttribute.ACCOUNT_DISABLED, String.valueOf( p.isDisabled() ).toUpperCase() ) );
+ outAttrs.put( new LockableAttributeImpl( "userPassword", p.getPassword() ) );
+
+ if ( p.getActivationKey() != null )
+ {
+ outAttrs.put( new LockableAttributeImpl( "safehausActivationKey", p.getActivationKey() ) );
+ }
+
+ outAttrs.put( new LockableAttributeImpl( "safehausResynchCount", Integer.toString( p.getResynchCount() ) ) );
+ outAttrs.put( new LockableAttributeImpl( "safehausFailuresInEpoch", Integer.toString( p.getFailuresInEpoch() ) ) );
+ if ( p.getInfo() != null )
+ {
+ outAttrs.put( new BasicAttribute( "safehausInfo", p.getInfo() ) );
+ }
+
+ Result r = new Result( obj, outAttrs );
+ return r;
+ }
+
+
+ public Object getStateToBind( Object obj, Name name, Context nameCtx, Hashtable environment ) throws NamingException
+ {
+ throw new UnsupportedOperationException( "Structural objectClass needed for safehausProfile within additional attributes!" );
+ }
+}
Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ServerProfileStore.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ServerProfileStore.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ServerProfileStore.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/ServerProfileStore.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.safehaus.triplesec.store;
+
+
+import javax.naming.NamingException;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.safehaus.profile.ServerProfile;
+
+
+/**
+ * A server store's interface.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public interface ServerProfileStore
+{
+ /** the krb5kdc schema principal name for a krb5KDCEntry */
+ String PRINCIPAL_ATTR = "krb5PrincipalName";
+
+ /**
+ * The store might need to be initialized before use. We recommend calling
+ * this method before making calls to other methods.
+ */
+ public void init() throws NamingException;
+
+ /**
+ * Gets a server's Hotp account profile.
+ *
+ * @param principal the KerberosPrincipal associated with the profile
+ * @return the Hotp ServerProfile
+ * @throws javax.naming.NamingException if the Profile cannot be accessed
+ */
+ ServerProfile getProfile( KerberosPrincipal principal ) throws NamingException;
+
+ /**
+ * Checks to see if a Profile exists within a realm in the Profile store.
+ *
+ * @param principal the KerberosPrincipal associated with the profile
+ * @return true if the Profile exists within the store, false if it does not
+ * @throws javax.naming.NamingException if the store is not available
+ */
+ public boolean hasProfile( KerberosPrincipal principal ) throws NamingException;
+
+ /**
+ * Adds a hotp account Profile to the store.
+ *
+ * @param profile the account ServerProfile to add
+ * @throws javax.naming.NamingException if the Profile cannot be added
+ */
+ public void add( ServerProfile profile ) throws NamingException;
+
+ /**
+ * Deletes an item by name within the list and on the store.
+ *
+ * @param principal the KerberosPrincipal associated with the profile
+ * @throws javax.naming.NamingException if the ServerProfile cannot be deleted
+ */
+ public void delete( KerberosPrincipal principal ) throws NamingException;
+
+ /**
+ * Updates the store by removing the old copy of the profile and creating
+ * the new one. So really this is a simple delete and add operation.
+ *
+ * @param oldPrincipal the original KerberosPrincipal for the profile
+ * @param updated the altered profile
+ */
+ public void update( KerberosPrincipal oldPrincipal, ServerProfile updated ) throws NamingException;
+
+ /**
+ * Sets the monitor whose callbacks are invoked to notify of events in the
+ * store.
+ *
+ * @param monitor the monitor to set for the store
+ */
+ public void setMonitor( StoreMonitor monitor );
+}
Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitor.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitor.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitor.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitor.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.safehaus.triplesec.store;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.Attributes;
+
+import org.safehaus.profile.ServerProfile;
+import org.safehaus.triplesec.store.ServerProfileStore;
+
+
+/**
+ * A monitor for a ServerProfile store which receives callbacks to represent
+ * notable events within a ServerProfileStore.
+ *
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public interface StoreMonitor
+{
+ void profileAdded( ServerProfileStore store, ServerProfile profile );
+
+ void profileDeleted( ServerProfileStore store, ServerProfile profile );
+
+ void profileAccessed( ServerProfileStore store, ServerProfile profile );
+
+ void profileUpdated( ServerProfileStore store, ServerProfile profile, ModificationItem[] mods );
+
+ void profileImported( ServerProfileStore store, String dn, Attributes attributes );
+
+ void profileNotImported( ServerProfileStore store, String dn, Attributes attributes );
+
+ void info( ServerProfileStore store, String s );
+
+ void storeInitialized( ServerProfileStore store );
+
+ void storeFailure( ServerProfileStore store );
+
+ void storeFailure( ServerProfileStore store, Throwable t );
+
+ void storeFailure( ServerProfileStore store, KerberosPrincipal principal );
+
+ void storeFailure( ServerProfileStore store, KerberosPrincipal principal, Throwable t );
+
+ void storeFailure( ServerProfileStore store, KerberosPrincipal principal, String s );
+}
Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitorAdapter.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitorAdapter.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitorAdapter.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/StoreMonitorAdapter.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.safehaus.triplesec.store;
+
+
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.Attributes;
+
+import org.safehaus.profile.ServerProfile;
+import org.safehaus.triplesec.store.ServerProfileStore;
+import org.safehaus.triplesec.store.StoreMonitor;
+
+
+/**
+ * An adapter for the StoreMonitor interface.
+ *
+ * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
+ * @version $Rev$
+ */
+public class StoreMonitorAdapter implements StoreMonitor
+{
+ public void profileAdded( ServerProfileStore store, ServerProfile profile )
+ {
+ }
+
+
+ public void profileDeleted( ServerProfileStore store, ServerProfile profile )
+ {
+ }
+
+
+ public void profileAccessed( ServerProfileStore store, ServerProfile profile )
+ {
+ }
+
+
+ public void profileUpdated( ServerProfileStore store, ServerProfile profile, ModificationItem[] mods )
+ {
+ }
+
+
+ public void storeInitialized( ServerProfileStore store )
+ {
+ }
+
+
+ public void storeFailure( ServerProfileStore store )
+ {
+ }
+
+
+ public void storeFailure( ServerProfileStore store, Throwable t )
+ {
+ if ( t == null )
+ {
+ return;
+ }
+
+ t.printStackTrace( System.err );
+ }
+
+
+ public void storeFailure( ServerProfileStore store, KerberosPrincipal principal )
+ {
+ }
+
+
+ public void storeFailure( ServerProfileStore store, KerberosPrincipal principal, Throwable t )
+ {
+ if ( t == null )
+ {
+ return;
+ }
+
+ t.printStackTrace( System.err );
+ }
+
+
+ public void profileImported( ServerProfileStore store, String dn, Attributes attributes )
+ {
+ }
+
+
+ public void profileNotImported( ServerProfileStore store, String dn, Attributes attributes )
+ {
+ }
+
+
+ public void info( ServerProfileStore store, String s )
+ {
+ }
+
+
+ public void storeFailure( ServerProfileStore store, KerberosPrincipal principal, String s )
+ {
+ }
+}
Added: directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/ApplicationAciManager.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/ApplicationAciManager.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/ApplicationAciManager.java (added)
+++ directory/trunks/triplesec/store/src/main/java/org/safehaus/triplesec/store/interceptor/ApplicationAciManager.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,492 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.safehaus.triplesec.store.interceptor;
+
+
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
+import org.apache.directory.shared.ldap.message.LockableAttributesImpl;
+import org.apache.directory.shared.ldap.name.LdapDN;
+import org.apache.directory.shared.ldap.schema.AttributeType;
+import org.apache.directory.shared.ldap.util.AttributeUtils;
+import org.apache.directory.shared.ldap.util.NamespaceTools;
+import org.apache.directory.shared.ldap.exception.LdapNameAlreadyBoundException;
+import org.apache.directory.server.core.invocation.InvocationStack;
+import org.apache.directory.server.core.partition.PartitionNexusProxy;
+import org.apache.directory.server.core.schema.AttributeTypeRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+
+import java.util.*;
+
+
+/**
+ * Automatically manages ACI creation, deletion and updates when applications are
+ * created, deleted, renamed and moved.
+ *
+ * @author Alex Karasulu
+ * @version $Rev: 64 $, $Date: 2005-11-03 01:43:49 -0500 (Thu, 03 Nov 2005) $
+ */
+public class ApplicationAciManager
+{
+ private static final Logger log = LoggerFactory.getLogger( ApplicationAciManager.class );
+ private static final String APP_ACITAG_SUFFIX = "Aci";
+ private static final String APP_ACITAG_SUFFIX_LOWER = APP_ACITAG_SUFFIX.toLowerCase();
+ private static final String APPADMIN_ACITAG_SUFFIX = "AdminAci";
+ private static final String APPADMIN_GROUP_SUFFIX = "AdminGroup";
+ private static final String APPADMIN_GROUP_SUFFIX_LOWWER = APPADMIN_GROUP_SUFFIX.toLowerCase();
+ private static final String[] RETURN_ADMINROLE = new String[] { "administrativeRole" };
+
+ private static final Collection ADD_BYPASS;
+ private static final Collection DEL_BYPASS;
+ static final Collection LOOKUP_BYPASS;
+
+ static
+ {
+ Collection c = new HashSet();
+ c.add( "normalizationService" );
+ c.add( "authenticationService" );
+ c.add( "defaultAuthorizationService" );
+ c.add( "schemaService" );
+ c.add( "policyProtectionService" );
+ c.add( "collectiveAttributeService" );
+ ADD_BYPASS = Collections.unmodifiableCollection( c );
+ DEL_BYPASS = Collections.unmodifiableCollection( c );
+
+ c = new HashSet();
+ c.addAll( PartitionNexusProxy.LOOKUP_BYPASS );
+ c.add( "policyProtectionService" );
+ LOOKUP_BYPASS = Collections.unmodifiableCollection( c );
+ }
+
+ /** LUT of normalized DNs for existing ou=groups entries of suffixes */
+ private final Set groupsLut = new HashSet();
+ /** LUT of normalized suffix DNs which are already ASCAs */
+ private final Set acsaLut = new HashSet();
+
+ private final AttributeTypeRegistry registry;
+ private final AttributeType administrativeRoleType;
+
+
+ public ApplicationAciManager( AttributeTypeRegistry registry ) throws NamingException
+ {
+ this.registry = registry;
+ administrativeRoleType = registry.lookup( "administrativeRole" );
+ }
+
+
+ /**
+ * Creates an ACIItem in a new subentry and adds it to the topmost AAA for application's to fully
+ * access their subtree. This method should be invoked immediately after the application entry
+ * is created.
+ *
+ * @param upDn the user provided DN string for the entry being added
+ * @param appDn the normalized DN for the entry being added
+ */
+ public void appAdded( LdapDN appDn ) throws NamingException
+ {
+ // get the current invocation object's proxy to access it's nexus proxy
+ PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+ addApplicationAdminGroup( proxy, appDn );
+ addApplicationSubentry( proxy, appDn );
+ }
+
+
+ /**
+ * Deletes the access control subentry added to the top most AAA for application access. This
+ * method should be invoked immediately after the application entry is removed.
+ */
+ public void appRemoved( LdapDN appDn ) throws NamingException
+ {
+ // get the current invocation object's proxy to access it's nexus proxy
+ PartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+ removeApplicationAdminGroup( proxy, appDn );
+ removeApplicationSubentry( proxy, appDn );
+ }
+
+
+ /**
+ * Adjusts subtree specifications and ACI when applications are renamed or moved in the DIT. This
+ * operation is a little bit tricky. Any name change on an application will remove it from the
+ * scope of the AC subentry's subtreeSpecification. It will also invalidate the ACIItem designed
+ * to grant the application at it's old DN. The subentry subsystem will automatically remove the
+ * operational attributes for relating the entry at the old DN to this AC subentry. At this point
+ * it's just best to remove the old AC subentry with it's ACIItem and create the new one. So this
+ * boil's down to an add and a delete.
+ *
+ * When the number of entries under the application entry are large this will mean a big change.
+ * All entries will have the old subentries operational attributes removed. The new operational
+ * entries will then be added. The cost of this change is expensive. However not that it is a
+ * management operation that happens relatively infrequently.
+ */
+// public void appNameChange( Name oldDn, String newUpDn, Name newDn ) throws NamingException
+// {
+// // get the current invocation object's proxy to access it's nexus proxy
+// DirectoryPartitionNexusProxy proxy = InvocationStack.getInstance().peek().getProxy();
+//
+// // we don't need to mess around with deleting and adding the admin group (don't want to loose info either)
+// removeApplicationSubentry( proxy, oldDn );
+// addApplicationSubentry( proxy, newUpDn, newDn );
+// }
+
+
+ private void removeApplicationAdminGroup( PartitionNexusProxy proxy, LdapDN appDn ) throws NamingException
+ {
+ // bypass all interceptors and ask for the partition suffix for this application's entry
+ // use the suffix to build the normalized DN for the administrator group for the application
+ LdapDN suffix = proxy.getSuffix( appDn, PartitionNexusProxy.BYPASS_ALL_COLLECTION );
+ String appName = NamespaceTools.getRdnValue( appDn.get( appDn.size() - 1 ) );
+ LdapDN groupDn = ( LdapDN ) suffix.clone();
+ groupDn.add( "ou=groups" );
+ StringBuffer buf = new StringBuffer();
+ buf.setLength( 0 );
+ buf.append( "cn=" );
+ buf.append( appName.toLowerCase() );
+ buf.append( APPADMIN_GROUP_SUFFIX_LOWWER );
+ groupDn.add( buf.toString() );
+
+ // blow away the group entry
+ groupDn.normalize( registry.getNormalizerMapping() );
+ proxy.delete( groupDn, DEL_BYPASS );
+ }
+
+
+ /**
+ * Adds an administrator group specifically for the application. The group name is inferred
+ * from the name of the application.
+ *
+ * @param proxy the nexus proxy to perform an add operation if need be
+ * @param appDn the normalized name for the application
+ * @throws NamingException if add operations fail
+ */
+ private void addApplicationAdminGroup( PartitionNexusProxy proxy, LdapDN appDn ) throws NamingException
+ {
+ // bypass all interceptors and ask for the partition suffix for this application's entry
+ // the suffix entry will be used as the administrative point for a ACSA starting at it
+ LdapDN suffix = proxy.getSuffix( appDn, PartitionNexusProxy.BYPASS_ALL_COLLECTION );
+ String appUpName = NamespaceTools.getRdnValue( appDn.getRdn().getUpName() );
+
+ // calculate the names of the group container and create ou=groups if we have to
+ LdapDN groupDn = ( LdapDN ) suffix.clone();
+ groupDn.add( "ou=groups" );
+ groupDn.normalize( registry.getNormalizerMapping() );
+ createGroupsContainer( proxy, groupDn );
+
+ // continue building the name for the new group entry off of ou=groups
+ StringBuffer buf = new StringBuffer();
+ Attribute cnAttr = new LockableAttributeImpl( "cn" );
+ buf.append( appUpName );
+ buf.append( APPADMIN_GROUP_SUFFIX );
+ cnAttr.add( buf.toString() );
+ buf.insert( 0, "cn=" );
+ groupDn.add( buf.toString() );
+ groupDn.normalize( registry.getNormalizerMapping() );
+
+ // create the admin group entry
+ Attributes group = new LockableAttributesImpl();
+ group.put( "objectClass", "top" );
+ group.get( "objectClass" ).add( "groupOfUniqueNames" );
+ group.put( cnAttr );
+ // not need since admin can do anything but we need one member at least
+ group.put( "uniqueMember", "uid=admin,ou=system" );
+ proxy.add( groupDn, group, ADD_BYPASS );
+ }
+
+
+ /**
+ * Creates the group container ou=groups if it does not exist.
+ *
+ * @param proxy the nexus proxy to perform an add operation if need be
+ * @param groupDn the normalized name for ou=groups under a suffix
+ * @throws NamingException if add operations fail
+ */
+ private void createGroupsContainer( PartitionNexusProxy proxy, LdapDN groupDn ) throws NamingException
+ {
+ if ( groupsLut.contains( groupDn.getNormName() ) )
+ {
+ return;
+ }
+
+ Attributes groups = new LockableAttributesImpl();
+ groups.put( "objectClass", "top" );
+ groups.get( "objectClass" ).add( "organizationalUnit" );
+ groups.put( "ou", "Groups" );
+
+ try
+ {
+ proxy.add( groupDn, groups, ADD_BYPASS );
+ }
+ catch ( LdapNameAlreadyBoundException e )
+ {
+ if ( log.isInfoEnabled() )
+ {
+ log.info( "Could not add " + groupDn + " since it exists ... adding it to exists LUT.");
+ }
+ }
+ groupsLut.add( groupDn.getNormName() );
+ }
+
+
+
+ void removeApplicationSubentry( PartitionNexusProxy proxy, LdapDN appDn ) throws NamingException
+ {
+ // bypass all interceptors and ask for the partition suffix for this application's entry
+ // then calculate the normalized dn of the subentry to delete for this application
+ LdapDN suffix = proxy.getSuffix( appDn, PartitionNexusProxy.BYPASS_ALL_COLLECTION );
+ String appName = NamespaceTools.getRdnValue( appDn.get( appDn.size() - 1 ) );
+ StringBuffer buf = new StringBuffer();
+ buf.append( "cn=" );
+ buf.append( appName.toLowerCase() );
+ buf.append( APP_ACITAG_SUFFIX_LOWER );
+ LdapDN subentryDn = ( LdapDN ) suffix.clone();
+ subentryDn.add( buf.toString() );
+
+ // delete the access control subentry
+ subentryDn.normalize( registry.getNormalizerMapping() );
+ proxy.delete( subentryDn, DEL_BYPASS );
+ }
+
+
+ /**
+ * Adds an accessControl subentry to the suffix of the partition containing the entry. If the suffix
+ * is not the Administrative Point for the ACSA then it is promoted to one.
+ *
+ * @param proxy the nexus proxy to perform an add operation if need be
+ * @param appDn the normalized name for the application entry being added
+ * @throws NamingException if add operations fail
+ */
+ void addApplicationSubentry( PartitionNexusProxy proxy, LdapDN appDn ) throws NamingException
+ {
+ // bypass all interceptors and ask for the partition suffix for this application's entry
+ // the suffix entry will be used as the administrative point for a ACSA starting at it
+ LdapDN suffix = proxy.getSuffix( appDn, PartitionNexusProxy.BYPASS_ALL_COLLECTION );
+ String appUpName = NamespaceTools.getRdnValue( appDn.getRdn().getUpName() );
+ String appName = NamespaceTools.getRdnValue( appDn.get( appDn.size() - 1 ) );
+ createAccessControlArea( proxy, suffix );
+
+ // calculate the application admin group name for the application
+ LdapDN groupDn = ( LdapDN ) suffix.clone();
+ groupDn.add( "ou=Groups" );
+ StringBuffer groupRdn = new StringBuffer();
+ groupRdn.append( "cn=" );
+ groupRdn.append( appUpName );
+ groupRdn.append( APPADMIN_GROUP_SUFFIX );
+ groupDn.add( groupRdn.toString() );
+ groupDn.normalize( registry.getNormalizerMapping() );
+
+ // calculate the name for the new subentry to create
+ StringBuffer buf = new StringBuffer();
+ buf.append( appName );
+ buf.append( APP_ACITAG_SUFFIX );
+ String aciTag = buf.toString();
+
+ // calculate subentry attributes with both app user ACI and
+ // app admin group ACI in same subentry
+ Attributes subentry = new LockableAttributesImpl();
+ subentry.put( "objectClass", "top" );
+ subentry.get( "objectClass" ).add( "subentry" );
+ subentry.get( "objectClass" ).add( "accessControlSubentry" );
+ subentry.put( "cn", aciTag );
+ subentry.put( "subtreeSpecification", createSubtreeSpecification( suffix, appDn ) );
+ subentry.put( "prescriptiveACI", createApplicationACIItem( aciTag, appDn ) );
+
+ // now add another prescriptiveACI to the same subentry to allow
+ // read/write access by the admin group
+ buf.setLength( 0 );
+ buf.append( appName );
+ buf.append( APPADMIN_ACITAG_SUFFIX );
+ subentry.get( "prescriptiveACI" ).add( createApplicationAdminACIItem( buf.toString(), groupDn ) );
+
+ // create the subentry
+ buf.setLength( 0 );
+ buf.append( "cn=" );
+ buf.append( appUpName );
+ buf.append( APP_ACITAG_SUFFIX );
+ LdapDN subentryDn = ( LdapDN ) suffix.clone();
+ subentryDn.add( buf.toString() );
+ subentryDn.normalize( registry.getNormalizerMapping() );
+ proxy.add( subentryDn, subentry, ADD_BYPASS );
+ }
+
+
+ /**
+ * Checks cache to see if the entry at apDn is an access control specific area (ACSA), if
+ * not the entry is accessed to check if it is an administrative point for an ACSA. If
+ * the entry is an ACSA AP, then the cache is updated. If the entry is NOT an ACSA AP then
+ * the entry at apDn is promoted to an ACSA.
+ *
+ * @param apDn
+ * @throws NamingException
+ */
+ private void createAccessControlArea( PartitionNexusProxy proxy, LdapDN apDn ) throws NamingException
+ {
+ if ( acsaLut.contains( apDn.getNormName() ) )
+ {
+ return;
+ }
+
+ Attributes acsa = proxy.lookup( apDn, RETURN_ADMINROLE, LOOKUP_BYPASS );
+ Attribute administrativeRole = AttributeUtils.getAttribute( acsa, administrativeRoleType );
+ if ( administrativeRole != null )
+ {
+ for ( int ii = 0; ii < administrativeRole.size(); ii++ )
+ {
+ String role = ( String ) administrativeRole.get( ii );
+ if ( role.equalsIgnoreCase( "accessControlSpecificArea" ) )
+ {
+ acsaLut.add( apDn.toString() );
+ return;
+ }
+ }
+ }
+
+ Attributes mods = new LockableAttributesImpl();
+ mods.put( "administrativeRole", "accessControlSpecificArea" );
+ proxy.modify( apDn, DirContext.ADD_ATTRIBUTE, mods );
+ acsaLut.add( apDn.getNormName() );
+ }
+
+
+ /**
+ * Creates the subtreeSpecification for the accessControlSubentry for the application.
+ *
+ * @param adminPointDn the normalized DN of the AP (Administrative Point) for the entry
+ * @param appDn the normalized DN of the application entry
+ * @return the subtreeSpecification of a subtree which starts at the application entry and
+ * descends to all leaf entries
+ * @throws NamingException if the AP is (wrong) not an ancestor of the application entry
+ */
+ private String createSubtreeSpecification( LdapDN adminPointDn, LdapDN appDn ) throws NamingException
+ {
+ LdapDN baseRdn = ( LdapDN ) NamespaceTools.getRelativeName( adminPointDn, appDn );
+ StringBuffer buf = new StringBuffer();
+ buf.append( "{ base \"" );
+ buf.append( baseRdn.getNormName() );
+ buf.append( "\" }" );
+ return buf.toString();
+ }
+
+
+ /**
+ * Creates an ACIItem for the application user to have <b>READ-ONLY</b> access to policy
+ * information for itself (the application). The application user will be granted the
+ * following permissions on all entries and their attributes under the application's subtree:
+ *
+ * <ul>
+ * <li>Read</li>
+ * <li>ReturnDN</li>
+ * <li>Browse</li>
+ * <li>DiscloseOnError</li>
+ * <li>Compare</li>
+ * </ul>
+ *
+ * Here's what the ACIItem looks like for application appname=abc,ou=applications,dc=example,dc=com:
+ * <pre>
+ * {
+ * identificationTag "abcAci"
+ * precedence 14,
+ * authenticationLevel simple,
+ * itemOrUserFirst userFirst: {
+ * userClasses { name { "appName=abc,ou=applications,dc=example,dc=com" } },
+ * userPermissions
+ * { {
+ * protectedItems {entry, allUserAttributeTypesAndValues},
+ * grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError, grantCompare } }
+ * } }
+ * }
+ * </pre>
+ *
+ * @param aciTag the identificationTag for the ACIItem produced
+ * @param appDn the normalized DN for the application
+ * @return the ACIItem for the application user
+ */
+ private String createApplicationACIItem( String aciTag, LdapDN appDn )
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "{ identificationTag \"" );
+ buf.append( aciTag );
+ buf.append( "\", precedence 14, authenticationLevel simple, itemOrUserFirst userFirst: { " );
+ buf.append( "userClasses { name { \"" );
+ buf.append( appDn.getNormName() );
+ buf.append( "\" } }, userPermissions { { protectedItems {entry, allUserAttributeTypesAndValues}, ");
+ buf.append("grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError, grantCompare } } } } }" );
+ return buf.toString();
+ }
+
+
+ /**
+ * Creates an ACIItem for an adminstrative group with full access to alter policy information
+ * for an application. The group will be granted the following permissions on all entries and
+ * their attributes under the application subtree:
+ *
+ * <ul>
+ * <li>Read</li>
+ * <li>ReturnDN</li>
+ * <li>Browse</li>
+ * <li>DiscloseOnError</li>
+ * <li>Compare</li>
+ * <li>Add</li>
+ * <li>Rename</li>
+ * <li>Remove</li>
+ * <li>Modify</li>
+ * <li>Import</li>
+ * <li>Export</li>
+ * </ul>
+ *
+ * Here's what the ACIItem looks like for application appName=abc,ou=applications,dc=example,dc=com and the
+ * admin group cn=abcAdminGroup,ou=groups,dc=example,dc=com:
+ * <pre>
+ * {
+ * identificationTag "abcAdminAci"
+ * precedence 14,
+ * authenticationLevel simple,
+ * itemOrUserFirst userFirst: {
+ * userClasses { userGroup { "cn=abcApplicationAdminGroup,ou=groups,dc=example,dc=com" } },
+ * userPermissions
+ * { {
+ * protectedItems {entry, allUserAttributeTypesAndValues},
+ * grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError, grantCompare,
+ * grantAdd, grantRename, grantRemove, grantModify, grantImport, grantExport } }
+ * } }
+ * }
+ * </pre>
+ *
+ * @param aciTag the identificationTag for the ACIItem produced
+ * @param adminGroupDn the normalized DN for the application's admin group
+ * @return the ACIItem for the administrative user's group for an application
+ */
+ private String createApplicationAdminACIItem( String aciTag, LdapDN adminGroupDn )
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "{ identificationTag \"" );
+ buf.append( aciTag );
+ buf.append( "\", precedence 14, authenticationLevel simple, itemOrUserFirst userFirst: { " );
+ buf.append( "userClasses { userGroup { \"" );
+ buf.append( adminGroupDn.getNormName() );
+ buf.append( "\" } }, userPermissions { { protectedItems {entry, allUserAttributeTypesAndValues}, ");
+ buf.append( "grantsAndDenials { grantRead, grantReturnDN, grantBrowse, grantDiscloseOnError, grantCompare, ");
+ buf.append( "grantAdd, grantRename, grantRemove, grantModify, grantImport, grantExport } } } } }" );
+ return buf.toString();
+ }
+}