Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 25966 invoked from network); 12 Dec 2006 15:25:33 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 12 Dec 2006 15:25:33 -0000 Received: (qmail 45295 invoked by uid 500); 12 Dec 2006 15:25:39 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 45178 invoked by uid 500); 12 Dec 2006 15:25:38 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 45054 invoked by uid 99); 12 Dec 2006 15:25:38 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 12 Dec 2006 07:25:38 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 12 Dec 2006 07:25:23 -0800 Received: by eris.apache.org (Postfix, from userid 65534) id 25CCD1A9825; Tue, 12 Dec 2006 07:24:39 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r486187 [5/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/ ad... Date: Tue, 12 Dec 2006 15:24:14 -0000 To: commits@directory.apache.org From: tbennett@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20061212152439.25CCD1A9825@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapGroupDao.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapGroupDao.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapGroupDao.java (added) +++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapGroupDao.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,404 @@ +/* + * 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.admin.dao.ldap; + + +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import javax.naming.Context; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SchemaViolationException; +import javax.naming.directory.SearchControls; + +import org.apache.directory.shared.ldap.name.LdapDN; +import org.safehaus.triplesec.admin.Constants; +import org.safehaus.triplesec.admin.ConstraintViolationException; +import org.safehaus.triplesec.admin.DataAccessException; +import org.safehaus.triplesec.admin.EntryAlreadyExistsException; +import org.safehaus.triplesec.admin.Group; +import org.safehaus.triplesec.admin.NoSuchEntryException; +import org.safehaus.triplesec.admin.dao.GroupDao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class LdapGroupDao implements GroupDao, LdapDao, Constants +{ + private static final Logger log = LoggerFactory.getLogger( LdapGroupDao.class ); + private static final String[] ATTRIBUTES = { + COMMON_NAME_ID, CREATORS_NAME_ID, CREATE_TIMESTAMP_ID, MODIFIERS_NAME_ID, MODIFY_TIMESTAMP_ID, UNIQUE_MEMBER_ID + }; + private final DirContext ctx; + private final String baseUrl; + private final String principalName; + + + public LdapGroupDao( DirContext ctx ) throws DataAccessException + { + this.ctx = ctx; + String name = null; + String principal = null; + try + { + name = ctx.getNameInNamespace(); + String principalDn = ( String ) ctx.getEnvironment().get( Context.SECURITY_PRINCIPAL ); + if ( principalDn.equalsIgnoreCase( "uid=admin,ou=system" ) ) + { + principal = "admin"; + } + else + { + principal = ( String ) new LdapDN( principalDn ).getRdn().getValue(); + } + } + catch ( NamingException e ) + { + String msg = "Failed to get name in namespace for base context."; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + finally + { + baseUrl = name; + principalName = principal; + } + } + + + // ----------------------------------------------------------------------- + // GroupDao method implementations + // ----------------------------------------------------------------------- + + + public Group add( String name, Set members ) throws DataAccessException + { + if ( members.size() == 0 ) + { + throw new ConstraintViolationException( "At least one member must be present within a group." ); + } + + BasicAttributes attrs = new BasicAttributes( OBJECT_CLASS_ID, GROUP_OF_UNIQUE_NAMES_OC, true ); + attrs.put( COMMON_NAME_ID, name ); + attrs.put( convertToLdapDns( members ) ); + String rdn = getRelativeDn( name ); + try + { + ctx.createSubcontext( rdn, attrs ); + return new Group( principalName, new Date( System.currentTimeMillis() ), this, name, members ); + } + catch ( NameAlreadyBoundException e ) + { + log.error( "Cannot create group " + rdn, e ); + EntryAlreadyExistsException eaee = new EntryAlreadyExistsException(); + eaee.initCause( e ); + throw eaee; + } + catch ( NamingException e ) + { + log.error( "Unexpected failure", e ); + throw new DataAccessException( e.getMessage() ); + } + } + + + public Group rename( String newName, Group archetype ) throws DataAccessException + { + String oldRdn = getRelativeDn( archetype.getName() ); + String newRdn = getRelativeDn( newName ); + + try + { + ctx.rename( oldRdn, newRdn ); + } + catch ( NameNotFoundException e ) + { + String msg = "Rename failed. Could not find " + oldRdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NameAlreadyBoundException e ) + { + String msg = "Rename failed. Another group already exists at " + newRdn + " under " + baseUrl; + log.error( msg, e ); + throw new EntryAlreadyExistsException( msg ); + } + catch ( SchemaViolationException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " is required by other entities"; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NamingException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " could not be renamed to " + newRdn; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new Group( archetype.getCreatorsName(), archetype.getCreateTimestamp(), principalName, + new Date( System.currentTimeMillis() ), this, newName, archetype.getMembers() ); + } + + + public Group modify( String creatorsName, Date createTimestamp, String name, Set members, ModificationItem[] mods ) + throws DataAccessException + { + if ( members.size() == 0 ) + { + throw new ConstraintViolationException( "At least one member must be present within a group." ); + } + + String rdn = getRelativeDn( name ); + for ( int ii = 0; ii < mods.length; ii++ ) + { + mods[ii] = convertToLdapDns( mods[ii] ); + } + + try + { + ctx.modifyAttributes( rdn, mods ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + msg += " The modification violates constraints."; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NameNotFoundException e ) + { + String msg = "Entry " + rdn + " under " + baseUrl + " does not exist"; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + + return new Group( creatorsName, createTimestamp, this.principalName, + new Date( System.currentTimeMillis() ), this, name, members ); + } + + + public void delete( String name ) throws DataAccessException + { + String rdn = getRelativeDn( name ); + + try + { + ctx.destroySubcontext( rdn ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + msg += ". Other entities depend on " + name; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + public Group load( String name ) throws DataAccessException + { + String rdn = getRelativeDn( name ); + Set members = null; + Attributes attrs = null; + String creatorsName = null; + String modifiersName = null; + Date createTimestamp = null; + Date modifyTimestamp = null; + + try + { + attrs = ctx.getAttributes( rdn, ATTRIBUTES ); + members = convertDnsToUsers( attrs.get( UNIQUE_MEMBER_ID ) ); + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + } + catch ( NameNotFoundException e ) + { + String msg = "Could not find " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NamingException e ) + { + String msg = "Failed to lookup " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new Group( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, name, members ); + } + + + public Iterator iterator() throws DataAccessException + { + String base = "ou=Groups"; + SearchControls controls = new SearchControls(); + controls.setReturningAttributes( ATTRIBUTES ); + controls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + try + { + return new JndiIterator( this, ctx.search( base, + "(objectClass=groupOfUniqueNames)", controls ), null ); + } + catch ( NamingException e ) + { + String msg = "Failed to search " + base + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + // ----------------------------------------------------------------------- + // Private utility methods + // ----------------------------------------------------------------------- + + + private Set convertDnsToUsers( Attribute attribute ) throws NamingException + { + Set members = new HashSet(); + for ( int ii = 0; ii < attribute.size(); ii++ ) + { + LdapDN userDn = new LdapDN( ( String ) attribute.get( ii ) ); + String member = ( String ) userDn.getRdn().getValue(); + members.add( member ); + } + return members; + } + + + private String getRelativeDn( String name ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "cn=" ).append( name ); + buf.append( ",ou=Groups" ); + return buf.toString(); + } + + + private ModificationItem convertToLdapDns( ModificationItem item ) + { + Attribute users = item.getAttribute(); + BasicAttribute userDns = new BasicAttribute( users.getID() ); + for ( int ii = 0; ii < users.size(); ii++ ) + { + StringBuffer buf = new StringBuffer(); + try + { + buf.append( "uid=" ).append( ( String ) users.get( ii ) ); + } + catch ( NamingException e ) + { + log.error( "Could not access attribute value.", e ); + } + buf.append( ",ou=Users," ).append( baseUrl ); + userDns.add( buf.toString() ); + } + + return new ModificationItem( item.getModificationOp(), userDns ); + } + + + private Attribute convertToLdapDns ( Set members ) + { + BasicAttribute attr = new BasicAttribute( UNIQUE_MEMBER_ID ); + for ( Iterator ii = members.iterator(); ii.hasNext(); /**/ ) + { + String userId = ( String ) ii.next(); + StringBuffer buf = new StringBuffer(); + buf.append( "uid=" ).append( userId ); + buf.append( ",ou=Users," ).append( baseUrl ); + attr.add( buf.toString() ); + } + return attr; + } + + + // ----------------------------------------------------------------------- + // LdapDao method implementations + // ----------------------------------------------------------------------- + + + public Object getEntryObject( Object extra, Attributes attrs ) + { + String name = null; + Set members = null; + String creatorsName = null; + String modifiersName = null; + Date createTimestamp = null; + Date modifyTimestamp = null; + + try + { + name = ( String ) attrs.get( COMMON_NAME_ID ).get(); + members = convertDnsToUsers( attrs.get( UNIQUE_MEMBER_ID ) ); + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + } + catch ( NamingException e ) + { + String msg = "Failed to produce object for attributes: " + attrs; + log.error( msg, e ); + } + + return new Group( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, name, members ); + } + + + public void deleteEntry( String rdn ) + { + try + { + ctx.destroySubcontext( rdn ); + } + catch ( NamingException e ) + { + log.error( "Failed to delete " + rdn + " under " + baseUrl, e ); + } + } +} Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapHauskeysUserDao.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapHauskeysUserDao.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapHauskeysUserDao.java (added) +++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapHauskeysUserDao.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,675 @@ +/* + * 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.admin.dao.ldap; + + +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.util.Iterator; + +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.NoPermissionException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SchemaViolationException; +import javax.naming.directory.SearchControls; +import javax.security.auth.kerberos.KerberosKey; +import javax.security.auth.kerberos.KerberosPrincipal; + +import org.apache.commons.lang.RandomStringUtils; +import org.apache.commons.lang.math.RandomUtils; +import org.apache.directory.shared.ldap.util.StringTools; +import org.safehaus.triplesec.admin.Constants; +import org.safehaus.triplesec.admin.ConstraintViolationException; +import org.safehaus.triplesec.admin.DataAccessException; +import org.safehaus.triplesec.admin.EntryAlreadyExistsException; +import org.safehaus.triplesec.admin.HauskeysUser; +import org.safehaus.triplesec.admin.NoSuchEntryException; +import org.safehaus.triplesec.admin.PermissionDeniedException; +import org.safehaus.triplesec.admin.dao.HauskeysUserDao; + + +public class LdapHauskeysUserDao extends AbstractLdapDao implements HauskeysUserDao, Constants +{ + private static final String[] ATTRIBUTES = { "*", "+" }; + + + public LdapHauskeysUserDao( DirContext ctx ) throws DataAccessException + { + super( ctx ); + } + + + public HauskeysUser add( String id, String description, String firstName, + String lastName, String password, String mobile, + String email, String notifyBy, String mobileCarrier, String tokenPin, String midletName, + String failuresInEpoch, String activationKey, String realm, String secret, String label, + String movingFactor, String address1, String address2, + String city, String stateProvRegion, String zipPostalCode, + String country, String company, boolean disabled ) throws DataAccessException + { + StringBuffer buf = new StringBuffer(); + BasicAttributes attrs = new BasicAttributes( OBJECT_CLASS_ID, "top", true ); + attrs.get( OBJECT_CLASS_ID ).add( EXTENSIBLE_OBJECT_OC ); + attrs.get( OBJECT_CLASS_ID ).add( SAFEHAUS_PROFILE_OC ); + attrs.get( OBJECT_CLASS_ID ).add( UID_OBJECT_OC ); + attrs.get( OBJECT_CLASS_ID ).add( KRB5KDCENTRY_OC ); + attrs.get( OBJECT_CLASS_ID ).add( KRB5PRINCIPAL_OC ); + attrs.get( OBJECT_CLASS_ID ).add( INET_ORG_PERSON_OC ); + attrs.get( OBJECT_CLASS_ID ).add( PERSON_OC ); + attrs.get( OBJECT_CLASS_ID ).add( ORGANIZATIONAL_PERSON_OC ); + attrs.put( UID_ID, id ); + attrs.put( SAFEHAUS_ID, id ); + String cn = buf.append( firstName ).append( " " ).append( lastName ).toString(); + buf.setLength( 0 ); + attrs.put( COMMON_NAME_ID, cn ); + attrs.put( GIVENNAME_ID, firstName ); + attrs.put( SURNAME_ID, lastName ); + attrs.put( PASSWORD_ID, StringTools.getBytesUtf8( password ) ); + attrs.put( APACHE_SAM_TYPE_ID, "7" ); + attrs.put( SAFEHAUS_RESYNCH_COUNT_ID, "-1" ); + + if ( tokenPin != null ) + { + attrs.put( TOKEN_PIN_ID, tokenPin ); + } + + if ( description != null ) + { + attrs.put( DESCRIPTION_ID, description ); + } + + if ( mobile != null ) + { + attrs.put( MOBILE_ID, mobile ); + } + + if ( email != null ) + { + attrs.put( EMAIL_ID, email ); + } + + if ( notifyBy != null ) + { + attrs.put( NOTIFY_BY_ID, notifyBy ); + } + + if ( mobileCarrier != null ) + { + attrs.put( MOBILE_CARRIER_ID, mobileCarrier ); + } + + if ( midletName == null ) + { + attrs.put( MIDLE_NAME_ID, this.realm ); + } + else + { + attrs.put( MIDLE_NAME_ID, midletName ); + } + + if ( failuresInEpoch == null ) + { + attrs.put( FAILURES_IN_EPOCH_ID, "0" ); + } + else + { + attrs.put( FAILURES_IN_EPOCH_ID, failuresInEpoch ); + } + + if ( activationKey != null ) + { + attrs.put( ACTIVATION_KEY_ID, activationKey ); + } + + if ( realm == null ) + { + attrs.put( REALM_ID, this.realm ); + } + else + { + attrs.put( REALM_ID, realm ); + } + + if ( label != null ) + { + attrs.put( LABEL_ID, label ); + } + + if ( address1 != null ) + { + attrs.put( STREET_ID, address1 ); + } + + if ( address2 != null ) + { + attrs.put( POSTAL_ADDRESS_ID, address2 ); + } + + if ( city != null ) + { + attrs.put( LOCALITY_NAME_ID, city ); + } + + if ( stateProvRegion != null ) + { + attrs.put( STATE_PROVINCE_ID, stateProvRegion ); + } + + if ( zipPostalCode != null ) + { + attrs.put( ZIP_POSTAL_CODE_ID, zipPostalCode ); + } + + if ( country != null ) + { + attrs.put( COUNTRY_ID, country ); + } + + if ( company != null ) + { + attrs.put( ORGANIZATION_ID, company ); + } + + if ( disabled ) + { + attrs.put( KRB5_DISABLED_ID, "TRUE" ); + } + else + { + attrs.put( KRB5_DISABLED_ID, "FALSE" ); + } + + // ------------------------------------------------------------------- + // Handle HOTP Attributes and Random Generation + // ------------------------------------------------------------------- + + if ( secret == null ) + { + attrs.put( SECRET_ID, getRandomSecret() ); + } + else + { + try + { + attrs.put( SECRET_ID, secret.getBytes( "UTF-8" ) ); + } + catch ( UnsupportedEncodingException e ) + { + throw new RuntimeException( "Failed to decode UTF-8 encoding: " + e.getMessage() ); + } + } + + if ( movingFactor == null ) + { + attrs.put( MOVING_FACTOR_ID, getRandomFactor() ); + } + else + { + attrs.put( MOVING_FACTOR_ID, movingFactor ); + } + + // ------------------------------------------------------------------- + // Handle Kerberos Attributes and Key Encryption + // ------------------------------------------------------------------- + + String krb5PrincipalName = buf.append( id ).append( "@" ).append( this.realm.toUpperCase() ).toString(); + buf.setLength( 0 ); + attrs.put( KRB5PRINCIPAL_NAME_ID, krb5PrincipalName ); + attrs.put( KRB5PRINCIPAL_REALM_ID, this.realm.toUpperCase() ); + + KerberosPrincipal kerberosPrincipal = new KerberosPrincipal( krb5PrincipalName ); + KerberosKey key = new KerberosKey( kerberosPrincipal, password.toCharArray(), "DES" ); + byte[] encodedKey = key.getEncoded(); + attrs.put( KRB5KEY_ID, encodedKey ); + attrs.put( KRB5KEY_VERSION_NUMBER_ID, Integer.toString( key.getVersionNumber() ) ); + attrs.put( KRB5ENCRYPTION_TYPE_ID, Integer.toString( key.getKeyType() ) ); + + String rdn = getRelativeDn( id ); + try + { + ctx.createSubcontext( rdn, attrs ); + return new HauskeysUser( principalName, new Date( System.currentTimeMillis() ), this, + id, description, firstName, lastName, password, mobile, email, notifyBy, + mobileCarrier, tokenPin, midletName, failuresInEpoch, activationKey, + realm, secret, label, movingFactor, address1, address2, city, + stateProvRegion, zipPostalCode, country, company, disabled ); + } + catch ( NameAlreadyBoundException e ) + { + log.error( "Cannot create hauskeys user " + rdn, e ); + EntryAlreadyExistsException eaee = new EntryAlreadyExistsException(); + eaee.initCause( e ); + throw eaee; + } + catch ( NamingException e ) + { + log.error( "Unexpected failure", e ); + throw new DataAccessException( e.getMessage() ); + } + } + + + public HauskeysUser rename( String newId, HauskeysUser archetype ) throws DataAccessException + { + String oldRdn = getRelativeDn( archetype.getId() ); + String newRdn = getRelativeDn( newId ); + + try + { + ctx.rename( oldRdn, newRdn ); + } + catch ( NameNotFoundException e ) + { + String msg = "Rename failed. Could not find " + oldRdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NameAlreadyBoundException e ) + { + String msg = "Rename failed. Another group already exists at " + newRdn + " under " + baseUrl; + log.error( msg, e ); + throw new EntryAlreadyExistsException( msg ); + } + catch ( SchemaViolationException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " is required by other entities"; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NoPermissionException e ) + { + String msg = "Rename failed. Permission denied."; + log.error( msg, e ); + throw new PermissionDeniedException( msg ); + } + catch ( NamingException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " could not be renamed to " + newRdn; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + // ------------------------------------------------------------------- + // Regenerate principal name and key since key depends on principal name + // ------------------------------------------------------------------- + + StringBuffer buf = new StringBuffer(); + BasicAttributes attrs = new BasicAttributes( true ); + String krb5PrincipalName = buf.append( newId ).append( "@" ).append( realm.toUpperCase() ).toString(); + buf.setLength( 0 ); + attrs.put( KRB5PRINCIPAL_NAME_ID, krb5PrincipalName ); + attrs.put( SAFEHAUS_ID, newId ); + + KerberosPrincipal kerberosPrincipal = new KerberosPrincipal( krb5PrincipalName ); + KerberosKey key = new KerberosKey( kerberosPrincipal, archetype.getPassword().toCharArray(), "DES" ); + byte[] encodedKey = key.getEncoded(); + attrs.put( KRB5KEY_ID, encodedKey ); + + try + { + ctx.modifyAttributes( newRdn, DirContext.REPLACE_ATTRIBUTE, attrs ); + } + catch ( NamingException e ) + { + String msg = "Rename partially failed. Could not update kerberos key and principal name for " + newRdn; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new HauskeysUser( archetype.getCreatorsName(), archetype.getCreateTimestamp(), + principalName, new Date( System.currentTimeMillis() ), this, newId, + archetype.getDescription(), archetype.getFirstName(), + archetype.getLastName(), archetype.getPassword(), archetype.getMobile(), archetype.getEmail(), + archetype.getNotifyBy(), archetype.getMobileCarrier(), archetype.getTokenPin(), + archetype.getMidletName(), archetype.getFailuresInEpoch(), archetype.getActivationKey(), + archetype.getRealm(), archetype.getSecret(), archetype.getLabel(), archetype.getMovingFactor(), + archetype.getAddress1(), archetype.getAddress2(), archetype.getCity(), + archetype.getStateProvRegion(), archetype.getZipPostalCode(), archetype.getCountry(), + archetype.getCompany(), archetype.isDisabled() ); + } + + + public HauskeysUser modify( String creatorsName, Date createTimestamp, String id, + String description, String firstName, String lastName, String password, String mobile, + String email, String notifyBy, String mobileCarrier, String tokenPin, String midletName, + String failuresInEpoch, String activationKey, String realm, String secret, String label, + String movingFactor, String address1, String address2, String city, + String stateProvRegion, String zipPostalCode, String country, String company, + boolean disabled, ModificationItem[] mods ) throws DataAccessException + { + for ( int ii = 0; ii < mods.length; ii++ ) + { + if ( mods[ii].getAttribute().getID().equalsIgnoreCase( "userPassword" ) ) + { + StringBuffer buf = new StringBuffer(); + String krb5PrincipalName = buf.append( id ).append( "@" ).append( realm.toUpperCase() ).toString(); + KerberosPrincipal kerberosPrincipal = new KerberosPrincipal( krb5PrincipalName ); + KerberosKey key = new KerberosKey( kerberosPrincipal, password.toCharArray(), "DES" ); + byte[] encodedKey = key.getEncoded(); + Attribute attr = new BasicAttribute( KRB5KEY_ID, encodedKey ); + ModificationItem item = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ); + ModificationItem[] temp = mods; + mods = new ModificationItem[temp.length + 1]; + for ( int jj = 0; jj < temp.length; jj++ ) + { + mods[jj] = temp[jj]; + } + mods[temp.length] = item; + } + } + + String rdn = getRelativeDn( id ); + try + { + ctx.modifyAttributes( rdn, mods ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + msg += " The modification violates constraints."; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NameNotFoundException e ) + { + String msg = "Entry " + rdn + " under " + baseUrl + " does not exist"; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NoPermissionException e ) + { + String msg = "Modify failed. Permission denied to " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new PermissionDeniedException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + + return new HauskeysUser( creatorsName, createTimestamp, this.principalName, + new Date( System.currentTimeMillis() ), this, id, description, firstName, lastName, + password, mobile, email, notifyBy, mobileCarrier, tokenPin, midletName, failuresInEpoch, + activationKey, realm, secret, label, movingFactor, address1, address2, city, + stateProvRegion, zipPostalCode, country, company, disabled ); + } + + + public void delete( String id ) throws DataAccessException + { + String rdn = getRelativeDn( id ); + + try + { + ctx.destroySubcontext( rdn ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + msg += ". Other entities depend on " + id; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NoPermissionException e ) + { + String msg = "Delete failed. Permission denied to delete " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new PermissionDeniedException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + public HauskeysUser load( String id ) throws DataAccessException + { + String rdn = getRelativeDn( id ); + Attributes attrs = null; + String description = null; + String password = null; + String firstName = null; + String lastName = null; + String creatorsName = null; + String modifiersName = null; + Date createTimestamp = null; + Date modifyTimestamp = null; + String mobile = null; + String email = null; + String notifyBy = null; + String mobileCarrier = null; + String tokenPin = null; + String midletName = null; + String failuresInEpoch = null; + String activationKey = null; + String realm = null; + String secret = null; + String label = null; + String movingFactor = null; + String address1 = null; + String address2 = null; + String city = null; + String stateProvRegion = null; + String zipPostalCode = null; + String country = null; + String company = null; + boolean disabled = false; + + try + { + attrs = ctx.getAttributes( rdn, ATTRIBUTES ); + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs ); + lastName = ( String ) attrs.get( SURNAME_ID ).get(); + firstName = LdapUtils.getSingleValued( GIVENNAME_ID, attrs ); + password = LdapUtils.getSingleValued( PASSWORD_ID, attrs ); + disabled = LdapUtils.getBoolean( KRB5_DISABLED_ID, attrs, false ); + + mobile = LdapUtils.getSingleValued( MOBILE_ID, attrs ); + email = LdapUtils.getSingleValued( EMAIL_ID, attrs ); + notifyBy = LdapUtils.getSingleValued( NOTIFY_BY_ID, attrs ); + mobileCarrier = LdapUtils.getSingleValued( MOBILE_CARRIER_ID, attrs ); + tokenPin = LdapUtils.getSingleValued( TOKEN_PIN_ID, attrs ); + midletName = LdapUtils.getSingleValued( MIDLE_NAME_ID, attrs ); + failuresInEpoch = LdapUtils.getSingleValued( FAILURES_IN_EPOCH_ID, attrs ); + activationKey = LdapUtils.getSingleValued( ACTIVATION_KEY_ID, attrs ); + realm = LdapUtils.getSingleValued( REALM_ID , attrs ); + secret = LdapUtils.getSingleValued( SECRET_ID, attrs ); + label = LdapUtils.getSingleValued( LABEL_ID, attrs ); + movingFactor = LdapUtils.getSingleValued( MOVING_FACTOR_ID, attrs ); + + address1 = LdapUtils.getSingleValued( STREET_ID, attrs ); + address2 = LdapUtils.getSingleValued( POSTAL_ADDRESS_ID, attrs ); + city = LdapUtils.getSingleValued( LOCALITY_NAME_ID, attrs ); + stateProvRegion = LdapUtils.getSingleValued( STATE_PROVINCE_ID, attrs ); + zipPostalCode = LdapUtils.getSingleValued( ZIP_POSTAL_CODE_ID, attrs ); + country = LdapUtils.getSingleValued( COUNTRY_ID, attrs ); + company = LdapUtils.getSingleValued( ORGANIZATION_ID, attrs ); + } + catch ( NameNotFoundException e ) + { + String msg = "Could not find " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NamingException e ) + { + String msg = "Failed to lookup " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new HauskeysUser( creatorsName, createTimestamp, modifiersName, modifyTimestamp, + this, id, description, firstName, lastName, password, mobile, email, notifyBy, + mobileCarrier, tokenPin, midletName, failuresInEpoch, activationKey, realm, + secret, label, movingFactor, address1, address2, city, stateProvRegion, + zipPostalCode, country, company, disabled ); + } + + + public Iterator iterator() throws DataAccessException + { + String base = "ou=Users"; + SearchControls controls = new SearchControls(); + controls.setReturningAttributes( ATTRIBUTES ); + controls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + try + { + return new JndiIterator( this, ctx.search( base, + "(& (objectClass=safehausProfile) (apacheSamType=*) )", controls ), null ); + } + catch ( NamingException e ) + { + String msg = "Failed to search " + base + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + public Object getEntryObject( Object extra, Attributes attrs ) + { + String uid = null; + String description = null; + String password = null; + String firstName = null; + String lastName = null; + String creatorsName = null; + String modifiersName = null; + Date createTimestamp = null; + Date modifyTimestamp = null; + String mobile = null; + String email = null; + String notifyBy = null; + String mobileCarrier = null; + String tokenPin = null; + String midletName = null; + String failuresInEpoch = null; + String activationKey = null; + String realm = null; + String secret = null; + String label = null; + String movingFactor = null; + String address1 = null; + String address2 = null; + String city = null; + String stateProvRegion = null; + String zipPostalCode = null; + String country = null; + String company = null; + boolean disabled = false; + + try + { + uid = ( String ) attrs.get( UID_ID ).get(); + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs ); + lastName = ( String ) attrs.get( SURNAME_ID ).get(); + firstName = LdapUtils.getSingleValued( GIVENNAME_ID, attrs ); + password = LdapUtils.getSingleValued( PASSWORD_ID, attrs ); + disabled = LdapUtils.getBoolean( KRB5_DISABLED_ID, attrs, false ); + + mobile = LdapUtils.getSingleValued( MOBILE_ID, attrs ); + email = LdapUtils.getSingleValued( EMAIL_ID, attrs ); + notifyBy = LdapUtils.getSingleValued( NOTIFY_BY_ID, attrs ); + mobileCarrier = LdapUtils.getSingleValued( MOBILE_CARRIER_ID, attrs ); + tokenPin = LdapUtils.getSingleValued( TOKEN_PIN_ID, attrs ); + midletName = LdapUtils.getSingleValued( MIDLE_NAME_ID, attrs ); + failuresInEpoch = LdapUtils.getSingleValued( FAILURES_IN_EPOCH_ID, attrs ); + activationKey = LdapUtils.getSingleValued( ACTIVATION_KEY_ID, attrs ); + realm = LdapUtils.getSingleValued( REALM_ID , attrs ); + secret = LdapUtils.getSingleValued( SECRET_ID, attrs ); + label = LdapUtils.getSingleValued( LABEL_ID, attrs ); + movingFactor = LdapUtils.getSingleValued( MOVING_FACTOR_ID, attrs ); + + address1 = LdapUtils.getSingleValued( STREET_ID, attrs ); + address2 = LdapUtils.getSingleValued( POSTAL_ADDRESS_ID, attrs ); + city = LdapUtils.getSingleValued( LOCALITY_NAME_ID, attrs ); + stateProvRegion = LdapUtils.getSingleValued( STATE_PROVINCE_ID, attrs ); + zipPostalCode = LdapUtils.getSingleValued( ZIP_POSTAL_CODE_ID, attrs ); + country = LdapUtils.getSingleValued( COUNTRY_ID, attrs ); + company = LdapUtils.getSingleValued( ORGANIZATION_ID, attrs ); + } + catch ( NamingException e ) + { + String msg = "Failed to produce object for attributes: " + attrs; + log.error( msg, e ); + } + + return new HauskeysUser( creatorsName, createTimestamp, modifiersName, modifyTimestamp, + this, uid, description, firstName, lastName, password, mobile, email, notifyBy, + mobileCarrier, tokenPin, midletName, failuresInEpoch, activationKey, realm, + secret, label, movingFactor, address1, address2, city, stateProvRegion, + zipPostalCode, country, company, disabled ); + } + + + // ----------------------------------------------------------------------- + // Private Utility Methods + // ----------------------------------------------------------------------- + + + private String getRelativeDn( String uid ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "uid=" ).append( uid ); + buf.append( ",ou=Users" ); + return buf.toString(); + } + + + private String getRandomFactor() + { + return String.valueOf( RandomUtils.nextLong() ); + } + + + private byte[] getRandomSecret() + { + // max length 16, min length 8 bytes + int length = 8 + RandomUtils.nextInt() % 8; + try + { + return RandomStringUtils.random( length ).getBytes( "UTF-8" ); + } + catch ( UnsupportedEncodingException e ) + { + throw new RuntimeException( "Failed to decode UTF-8 encoding: " + e.getMessage() ); + } + } +} Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapLocalUserDao.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapLocalUserDao.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapLocalUserDao.java (added) +++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapLocalUserDao.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,504 @@ +/* + * 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.admin.dao.ldap; + + +import java.util.Date; +import java.util.Iterator; + +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.NoPermissionException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttribute; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SchemaViolationException; +import javax.naming.directory.SearchControls; +import javax.security.auth.kerberos.KerberosKey; +import javax.security.auth.kerberos.KerberosPrincipal; + +import org.apache.directory.shared.ldap.util.StringTools; + +import org.safehaus.triplesec.admin.Constants; +import org.safehaus.triplesec.admin.ConstraintViolationException; +import org.safehaus.triplesec.admin.DataAccessException; +import org.safehaus.triplesec.admin.EntryAlreadyExistsException; +import org.safehaus.triplesec.admin.LocalUser; +import org.safehaus.triplesec.admin.NoSuchEntryException; +import org.safehaus.triplesec.admin.PermissionDeniedException; +import org.safehaus.triplesec.admin.dao.LocalUserDao; + + +public class LdapLocalUserDao extends AbstractLdapDao implements LocalUserDao, Constants +{ + private static final String[] ATTRIBUTES = { "*", "+" }; + + + public LdapLocalUserDao( DirContext ctx ) throws DataAccessException + { + super( ctx ); + } + + + public LocalUser add( String id, String description, String firstName, + String lastName, String password, String address1, String address2, + String city, String stateProvRegion, String zipPostalCode, + String country, String company, String email, boolean disabled ) throws DataAccessException + { + StringBuffer buf = new StringBuffer(); + BasicAttributes attrs = new BasicAttributes( OBJECT_CLASS_ID, "top", true ); + attrs.get( OBJECT_CLASS_ID ).add( EXTENSIBLE_OBJECT_OC ); + attrs.get( OBJECT_CLASS_ID ).add( UID_OBJECT_OC ); + attrs.get( OBJECT_CLASS_ID ).add( KRB5KDCENTRY_OC ); + attrs.get( OBJECT_CLASS_ID ).add( KRB5PRINCIPAL_OC ); + attrs.get( OBJECT_CLASS_ID ).add( INET_ORG_PERSON_OC ); + attrs.get( OBJECT_CLASS_ID ).add( PERSON_OC ); + attrs.get( OBJECT_CLASS_ID ).add( ORGANIZATIONAL_PERSON_OC ); + attrs.put( UID_ID, id ); + String cn = buf.append( firstName ).append( " " ).append( lastName ).toString(); + buf.setLength( 0 ); + attrs.put( COMMON_NAME_ID, cn ); + attrs.put( GIVENNAME_ID, firstName ); + attrs.put( SURNAME_ID, lastName ); + attrs.put( PASSWORD_ID, StringTools.getBytesUtf8( password ) ); + + if ( email != null ) + { + attrs.put( EMAIL_ID, email ); + } + + if ( description != null ) + { + attrs.put( DESCRIPTION_ID, description ); + } + + if ( address1 != null ) + { + attrs.put( STREET_ID, address1 ); + } + + if ( address2 != null ) + { + attrs.put( POSTAL_ADDRESS_ID, address2 ); + } + + if ( city != null ) + { + attrs.put( LOCALITY_NAME_ID, city ); + } + + if ( stateProvRegion != null ) + { + attrs.put( STATE_PROVINCE_ID, stateProvRegion ); + } + + if ( zipPostalCode != null ) + { + attrs.put( ZIP_POSTAL_CODE_ID, zipPostalCode ); + } + + if ( country != null ) + { + attrs.put( COUNTRY_ID, country ); + } + + if ( company != null ) + { + attrs.put( ORGANIZATION_ID, company ); + } + + if ( disabled ) + { + attrs.put( KRB5_DISABLED_ID, "TRUE" ); + } + else + { + attrs.put( KRB5_DISABLED_ID, "FALSE" ); + } + + // ------------------------------------------------------------------- + // Handle Kerberos Attributes and Key Encryption + // ------------------------------------------------------------------- + + String krb5PrincipalName = buf.append( id ).append( "@" ).append( realm.toUpperCase() ).toString(); + buf.setLength( 0 ); + attrs.put( KRB5PRINCIPAL_NAME_ID, krb5PrincipalName ); + attrs.put( KRB5PRINCIPAL_REALM_ID, realm.toUpperCase() ); + + KerberosPrincipal kerberosPrincipal = new KerberosPrincipal( krb5PrincipalName ); + KerberosKey key = new KerberosKey( kerberosPrincipal, password.toCharArray(), "DES" ); + byte[] encodedKey = key.getEncoded(); + attrs.put( KRB5KEY_ID, encodedKey ); + attrs.put( KRB5KEY_VERSION_NUMBER_ID, Integer.toString( key.getVersionNumber() ) ); + attrs.put( KRB5ENCRYPTION_TYPE_ID, Integer.toString( key.getKeyType() ) ); + + String rdn = getRelativeDn( id ); + try + { + ctx.createSubcontext( rdn, attrs ); + return new LocalUser( principalName, new Date( System.currentTimeMillis() ), this, + id, description, firstName, lastName, password, address1, address2, city, + stateProvRegion, zipPostalCode, country, company, email, disabled ); + } + catch ( NameAlreadyBoundException e ) + { + log.error( "Cannot create local user " + rdn, e ); + EntryAlreadyExistsException eaee = new EntryAlreadyExistsException(); + eaee.initCause( e ); + throw eaee; + } + catch ( NamingException e ) + { + log.error( "Unexpected failure", e ); + throw new DataAccessException( e.getMessage() ); + } + } + + + public LocalUser rename( String newId, LocalUser archetype ) throws DataAccessException + { + String oldRdn = getRelativeDn( archetype.getId() ); + String newRdn = getRelativeDn( newId ); + + try + { + ctx.rename( oldRdn, newRdn ); + } + catch ( NameNotFoundException e ) + { + String msg = "Rename failed. Could not find " + oldRdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NameAlreadyBoundException e ) + { + String msg = "Rename failed. Another user already exists at " + newRdn + " under " + baseUrl; + log.error( msg, e ); + throw new EntryAlreadyExistsException( msg ); + } + catch ( SchemaViolationException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " is required by other entities"; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NoPermissionException e ) + { + String msg = "Rename failed. Permission denied."; + log.error( msg, e ); + throw new PermissionDeniedException( msg ); + } + catch ( NamingException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " could not be renamed to " + newRdn; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + // ------------------------------------------------------------------- + // Regenerate principal name and key since key depends on principal name + // ------------------------------------------------------------------- + + StringBuffer buf = new StringBuffer(); + BasicAttributes attrs = new BasicAttributes( true ); + String krb5PrincipalName = buf.append( newId ).append( "@" ).append( realm.toUpperCase() ).toString(); + buf.setLength( 0 ); + attrs.put( KRB5PRINCIPAL_NAME_ID, krb5PrincipalName ); + attrs.put( SAFEHAUS_ID, newId ); + + KerberosPrincipal kerberosPrincipal = new KerberosPrincipal( krb5PrincipalName ); + KerberosKey key = new KerberosKey( kerberosPrincipal, archetype.getPassword().toCharArray(), "DES" ); + byte[] encodedKey = key.getEncoded(); + attrs.put( KRB5KEY_ID, encodedKey ); + + try + { + ctx.modifyAttributes( newRdn, DirContext.REPLACE_ATTRIBUTE, attrs ); + } + catch ( NamingException e ) + { + String msg = "Rename partially failed. Could not update kerberos key and principal name for " + newRdn; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new LocalUser( archetype.getCreatorsName(), archetype.getCreateTimestamp(), + principalName, new Date( System.currentTimeMillis() ), this, newId, + archetype.getDescription(), archetype.getFirstName(), archetype.getLastName(), archetype.getPassword(), + archetype.getAddress1(), archetype.getAddress2(), archetype.getCity(), + archetype.getStateProvRegion(), archetype.getZipPostalCode(), archetype.getCountry(), + archetype.getCompany(), archetype.getEmail(), archetype.isDisabled() ); + } + + + public LocalUser modify( String creatorsName, Date createTimestamp, String id, String description, + String password, String firstName, String lastName, String address1, String address2, String city, + String stateProvRegion, String zipPostalCode, String country, String company, String email, + boolean disabled, ModificationItem[] mods ) throws DataAccessException + { + for ( int ii = 0; ii < mods.length; ii++ ) + { + if ( mods[ii].getAttribute().getID().equalsIgnoreCase( "userPassword" ) ) + { + StringBuffer buf = new StringBuffer(); + String krb5PrincipalName = buf.append( id ).append( "@" ).append( realm.toUpperCase() ).toString(); + KerberosPrincipal kerberosPrincipal = new KerberosPrincipal( krb5PrincipalName ); + KerberosKey key = new KerberosKey( kerberosPrincipal, password.toCharArray(), "DES" ); + byte[] encodedKey = key.getEncoded(); + Attribute attr = new BasicAttribute( KRB5KEY_ID, encodedKey ); + ModificationItem item = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr ); + ModificationItem[] temp = mods; + mods = new ModificationItem[temp.length + 1]; + for ( int jj = 0; jj < temp.length; jj++ ) + { + mods[jj] = temp[jj]; + } + mods[temp.length] = item; + } + } + + String rdn = getRelativeDn( id ); + try + { + ctx.modifyAttributes( rdn, mods ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + msg += " The modification violates constraints."; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NameNotFoundException e ) + { + String msg = "Entry " + rdn + " under " + baseUrl + " does not exist"; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NoPermissionException e ) + { + String msg = "Modify failed. Permission denied to " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new PermissionDeniedException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + + return new LocalUser( creatorsName, createTimestamp, this.principalName, + new Date( System.currentTimeMillis() ), this, id, description, + firstName, lastName, password, address1, address2, city, stateProvRegion, + zipPostalCode, country, company, email, disabled ); + } + + + public void delete( String id ) throws DataAccessException + { + String rdn = getRelativeDn( id ); + + try + { + ctx.destroySubcontext( rdn ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + msg += ". Other entities depend on " + id; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NoPermissionException e ) + { + String msg = "Delete failed. Permission denied to delete " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new PermissionDeniedException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + public LocalUser load( String id ) throws DataAccessException + { + String rdn = getRelativeDn( id ); + Attributes attrs = null; + String description = null; + String password = null; + String firstName = null; + String lastName = null; + String creatorsName = null; + String modifiersName = null; + String address1 = null; + String address2 = null; + String city = null; + String stateProvRegion = null; + String zipPostalCode = null; + String country = null; + String company = null; + String email = null; + Date createTimestamp = null; + Date modifyTimestamp = null; + boolean disabled = false; + + try + { + attrs = ctx.getAttributes( rdn, ATTRIBUTES ); + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs ); + lastName = ( String ) attrs.get( SURNAME_ID ).get(); + firstName = LdapUtils.getSingleValued( GIVENNAME_ID, attrs ); + password = LdapUtils.getSingleValued( PASSWORD_ID, attrs ); + disabled = LdapUtils.getBoolean( KRB5_DISABLED_ID, attrs, false ); + + address1 = LdapUtils.getSingleValued( STREET_ID, attrs ); + address2 = LdapUtils.getSingleValued( POSTAL_ADDRESS_ID, attrs ); + city = LdapUtils.getSingleValued( LOCALITY_NAME_ID, attrs ); + stateProvRegion = LdapUtils.getSingleValued( STATE_PROVINCE_ID, attrs ); + zipPostalCode = LdapUtils.getSingleValued( ZIP_POSTAL_CODE_ID, attrs ); + country = LdapUtils.getSingleValued( COUNTRY_ID, attrs ); + company = LdapUtils.getSingleValued( ORGANIZATION_ID, attrs ); + email = LdapUtils.getSingleValued( EMAIL_ID, attrs ); + } + catch ( NameNotFoundException e ) + { + String msg = "Could not find " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NamingException e ) + { + String msg = "Failed to lookup " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new LocalUser( creatorsName, createTimestamp, modifiersName, modifyTimestamp, + this, id, description, firstName, lastName, password, address1, address2, city, stateProvRegion, + zipPostalCode, country, company, email, disabled ); + } + + + public Iterator iterator() throws DataAccessException + { + String base = "ou=Users"; + SearchControls controls = new SearchControls(); + controls.setReturningAttributes( ATTRIBUTES ); + controls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + try + { + return new JndiIterator( this, ctx.search( base, + "( & (! (apacheSamType=*) ) (objectClass=person) )", controls ), null ); + } + catch ( NamingException e ) + { + String msg = "Failed to search " + base + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + // ----------------------------------------------------------------------- + // Private Utility Methods + // ----------------------------------------------------------------------- + + + private String getRelativeDn( String uid ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "uid=" ).append( uid ); + buf.append( ",ou=Users" ); + return buf.toString(); + } + + + // ----------------------------------------------------------------------- + // LdapDao method implementations + // ----------------------------------------------------------------------- + + + public Object getEntryObject( Object extra, Attributes attrs ) + { + String uid = null; + String description = null; + String firstName = null; + String lastName = null; + String password = null; + String creatorsName = null; + String modifiersName = null; + String address1 = null; + String address2 = null; + String city = null; + String stateProvRegion = null; + String zipPostalCode = null; + String country = null; + String company = null; + String email = null; + Date createTimestamp = null; + Date modifyTimestamp = null; + boolean disabled = false; + + try + { + uid = ( String ) attrs.get( UID_ID ).get(); + lastName = ( String ) attrs.get( SURNAME_ID ).get(); + firstName = LdapUtils.getSingleValued( GIVENNAME_ID, attrs ); + description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs ); + password = LdapUtils.getSingleValued( PASSWORD_ID, attrs ); + disabled = LdapUtils.getBoolean( KRB5_DISABLED_ID, attrs, false ); + address1 = LdapUtils.getSingleValued( STREET_ID, attrs ); + address2 = LdapUtils.getSingleValued( POSTAL_ADDRESS_ID, attrs ); + city = LdapUtils.getSingleValued( LOCALITY_NAME_ID, attrs ); + stateProvRegion = LdapUtils.getSingleValued( STATE_PROVINCE_ID, attrs ); + zipPostalCode = LdapUtils.getSingleValued( ZIP_POSTAL_CODE_ID, attrs ); + country = LdapUtils.getSingleValued( COUNTRY_ID, attrs ); + company = LdapUtils.getSingleValued( ORGANIZATION_ID, attrs ); + email = LdapUtils.getSingleValued( EMAIL_ID, attrs ); + + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + } + catch ( NamingException e ) + { + String msg = "Failed to produce object for attributes: " + attrs; + log.error( msg, e ); + } + + return new LocalUser( creatorsName, createTimestamp, modifiersName, modifyTimestamp, + this, uid, description, firstName, lastName, password, address1, address2, city, + stateProvRegion, zipPostalCode, country, company, email, disabled ); + } +} Added: directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapPermissionDao.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapPermissionDao.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapPermissionDao.java (added) +++ directory/trunks/triplesec/admin-api/src/main/java/org/safehaus/triplesec/admin/dao/ldap/LdapPermissionDao.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,399 @@ +/* + * 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.admin.dao.ldap; + + +import java.util.Date; +import java.util.Iterator; + +import javax.naming.Context; +import javax.naming.NameAlreadyBoundException; +import javax.naming.NameNotFoundException; +import javax.naming.NamingException; +import javax.naming.directory.Attributes; +import javax.naming.directory.BasicAttributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.ModificationItem; +import javax.naming.directory.SchemaViolationException; +import javax.naming.directory.SearchControls; + +import org.apache.directory.shared.ldap.name.LdapDN; +import org.safehaus.triplesec.admin.Constants; +import org.safehaus.triplesec.admin.ConstraintViolationException; +import org.safehaus.triplesec.admin.DataAccessException; +import org.safehaus.triplesec.admin.EntryAlreadyExistsException; +import org.safehaus.triplesec.admin.NoSuchEntryException; +import org.safehaus.triplesec.admin.Permission; +import org.safehaus.triplesec.admin.dao.PermissionDao; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class LdapPermissionDao implements PermissionDao, LdapDao, Constants +{ + public static final String[] ATTRIBUTES = new String[] { + DESCRIPTION_ID, PERM_NAME_ID, "creatorsName", "createTimestamp", "modifiersName", "modifyTimestamp" + }; + private static final Logger log = LoggerFactory.getLogger( LdapPermissionDao.class ); + private final DirContext ctx; + private final String baseUrl; + private final String principalName; + + + public LdapPermissionDao( DirContext ctx ) throws DataAccessException + { + this.ctx = ctx; + + String name = null; + String principal = null; + try + { + name = ctx.getNameInNamespace(); + String principalDn = ( String ) ctx.getEnvironment().get( Context.SECURITY_PRINCIPAL ); + if ( principalDn.equalsIgnoreCase( "uid=admin,ou=system" ) ) + { + principal = "admin"; + } + else + { + principal = ( String ) new LdapDN( principalDn ).getRdn().getValue(); + } + } + catch ( NamingException e ) + { + String msg = "Failed to get name in namespace for base context."; + log.error( msg, e ); + throw new DataAccessException( msg ); + + } + finally + { + baseUrl = name; + principalName = principal; + } + } + + + // ----------------------------------------------------------------------- + // PermissionDao method implementations + // ----------------------------------------------------------------------- + + + public Permission add( String appName, String permName, String description ) + throws DataAccessException + { + BasicAttributes attrs = new BasicAttributes( OBJECT_CLASS_ID, POLICY_PERMISSION_OC, true ); + attrs.put( PERM_NAME_ID, permName ); + if ( description != null ) + { + attrs.put( DESCRIPTION_ID, description ); + } + + String rdn = getRelativeDn( appName, permName ); + try + { + ctx.createSubcontext( rdn, attrs ); + return new Permission( principalName, new Date( System.currentTimeMillis() ), + this, appName, permName, description ); + } + catch ( NameAlreadyBoundException e ) + { + log.error( "Cannot create permission " + rdn, e ); + EntryAlreadyExistsException eaee = new EntryAlreadyExistsException(); + eaee.initCause( e ); + throw eaee; + } + catch ( NamingException e ) + { + log.error( "Unexpected failure", e ); + throw new DataAccessException( e.getMessage() ); + } + } + + + public void delete( String appName, String permName ) + throws DataAccessException + { + String rdn = getRelativeDn( appName, permName ); + + try + { + ctx.destroySubcontext( rdn ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + msg += ". Other entities depend on " + permName; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not delete " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + public Permission modify( String creatorsName, Date createTimestamp, String appName, + String permName, String description, ModificationItem[] mods ) throws DataAccessException + { + String rdn = getRelativeDn( appName, permName ); + + try + { + ctx.modifyAttributes( rdn, mods ); + } + catch ( SchemaViolationException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + msg += " The modification violates constraints."; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NameNotFoundException e ) + { + String msg = "Entry " + rdn + " under " + baseUrl + " does not exist"; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NamingException e ) + { + String msg = "Could not modify " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + + return new Permission( creatorsName, createTimestamp, this.principalName, + new Date( System.currentTimeMillis() ), this, appName, permName, description ); + } + + + public Permission rename( String newPermName, Permission perm ) + throws DataAccessException + { + String oldRdn = getRelativeDn( perm.getApplicationName(), perm.getName() ); + String newRdn = getRelativeDn( perm.getApplicationName(), newPermName ); + + try + { + ctx.rename( oldRdn, newRdn ); + } + catch ( NameNotFoundException e ) + { + String msg = "Rename failed. Could not find " + oldRdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NameAlreadyBoundException e ) + { + String msg = "Rename failed. Another permission already exists at " + newRdn + " under " + baseUrl; + log.error( msg, e ); + throw new EntryAlreadyExistsException( msg ); + } + catch ( SchemaViolationException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " is required by other entities"; + log.error( msg, e ); + throw new ConstraintViolationException( msg ); + } + catch ( NamingException e ) + { + String msg = "Rename failed. " + oldRdn + " under " + baseUrl + " could not be renamed to " + newRdn; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new Permission( perm.getCreatorsName(), perm.getCreateTimestamp(), principalName, + new Date( System.currentTimeMillis() ), + this, perm.getApplicationName(), newPermName, perm.getDescription() ); + } + + + public Permission load( String appName, String permName ) + throws DataAccessException + { + String description = null; + String creatorsName = null; + Date createTimestamp = null; + String modifiersName = null; + Date modifyTimestamp = null; + String rdn = getRelativeDn( appName, permName ); + Attributes attrs = null; + + try + { + attrs = ctx.getAttributes( rdn, ATTRIBUTES ); + description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs ); + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + } + catch ( NameNotFoundException e ) + { + String msg = "Could not find " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new NoSuchEntryException( msg ); + } + catch ( NamingException e ) + { + String msg = "Failed to lookup " + rdn + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + + return new Permission( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, + appName, permName, description ); + } + + + public boolean has( String appName, String permName ) + throws DataAccessException + { + String rdn = getRelativeDn( appName, permName ); + + try + { + ctx.getAttributes( rdn ); + return true; + } + catch ( NameNotFoundException e ) + { + return false; + } + catch ( NamingException e ) + { + return false; + } + } + + + public Iterator permissionNameIterator( String appName ) throws DataAccessException + { + String base = getRelativeDn( appName ); + SearchControls controls = new SearchControls(); + controls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + try + { + return new JndiIterator( this, PERM_NAME_ID, ctx.search( base, + "(& (permName=*) (objectClass=policyPermission) )", controls ), appName ); + } + catch ( NamingException e ) + { + String msg = "Failed to search " + base + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + public Iterator permissionIterator( String appName ) throws DataAccessException + { + String base = getRelativeDn( appName ); + SearchControls controls = new SearchControls(); + controls.setReturningAttributes( ATTRIBUTES ); + controls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); + try + { + return new JndiIterator( this, ctx.search( base, + "(& (permName=*) (objectClass=policyPermission) )", controls ), appName ); + } + catch ( NamingException e ) + { + String msg = "Failed to search " + base + " under " + baseUrl; + log.error( msg, e ); + throw new DataAccessException( msg ); + } + } + + + // ----------------------------------------------------------------------- + // Private utility methods + // ----------------------------------------------------------------------- + + + private String getRelativeDn( String appName, String permName ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "permName=" ).append( permName ); + buf.append( ",ou=Permissions,appName=" ).append( appName ); + buf.append( ",ou=Applications" ); + return buf.toString(); + } + + + private String getRelativeDn( String appName ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "ou=Permissions,appName=" ).append( appName ); + buf.append( ",ou=Applications" ); + return buf.toString(); + } + + + // ----------------------------------------------------------------------- + // LdapDao method implementations + // ----------------------------------------------------------------------- + + + public Object getEntryObject( Object extra, Attributes attrs ) + { + String permName = null; + String description = null; + String creatorsName = null; + Date createTimestamp = null; + String modifiersName = null; + Date modifyTimestamp = null; + + try + { + permName = ( String ) attrs.get( PERM_NAME_ID ).get(); + description = LdapUtils.getSingleValued( DESCRIPTION_ID, attrs ); + creatorsName = LdapUtils.getPrincipal( CREATORS_NAME_ID, attrs ); + createTimestamp = LdapUtils.getDate( CREATE_TIMESTAMP_ID, attrs ); + modifiersName = LdapUtils.getPrincipal( MODIFIERS_NAME_ID, attrs ); + modifyTimestamp = LdapUtils.getDate( MODIFY_TIMESTAMP_ID, attrs ); + } + catch ( NamingException e ) + { + String msg = "Failed to produce object for attributes: " + attrs; + log.error( msg, e ); + } + + return new Permission( creatorsName, createTimestamp, modifiersName, modifyTimestamp, this, + ( String ) extra, permName, description ); + } + + + public void deleteEntry( String rdn ) + { + try + { + ctx.destroySubcontext( rdn ); + } + catch ( NamingException e ) + { + log.error( "Failed to delete " + rdn + " under " + baseUrl, e ); + } + } +}