Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 27145 invoked from network); 12 Dec 2006 15:26:20 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 12 Dec 2006 15:26:20 -0000 Received: (qmail 48061 invoked by uid 500); 12 Dec 2006 15:26:28 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 48027 invoked by uid 500); 12 Dec 2006 15:26:28 -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 48016 invoked by uid 99); 12 Dec 2006 15:26:28 -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:26:28 -0800 X-ASF-Spam-Status: No, hits=-8.4 required=10.0 tests=ALL_TRUSTED,INFO_TLD,MAILTO_TO_SPAM_ADDR,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:26:14 -0800 Received: by eris.apache.org (Postfix, from userid 65534) id 327BC1A981C; Tue, 12 Dec 2006 07:24:40 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r486187 [17/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... 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: <20061212152440.327BC1A981C@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: directory/trunks/triplesec/main/pom.xml URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/pom.xml?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/pom.xml (added) +++ directory/trunks/triplesec/main/pom.xml Tue Dec 12 07:23:31 2006 @@ -0,0 +1,170 @@ + + + + 4.0.0 + + org.safehaus.triplesec + build + 1.0-SNAPSHOT + + triplesec-main + Triplesec Main + jar + + + ${pom.groupId} + triplesec-configuration-io + ${pom.version} + + + ${pom.groupId} + triplesec-configuration + ${pom.version} + + + ${pom.groupId} + triplesec-verifier + ${pom.version} + + + ${pom.groupId} + triplesec-otp + ${pom.version} + + + ${pom.groupId} + triplesec-testdata + ${pom.version} + + + commons-daemon + commons-daemon + 1.0.1 + provided + + + org.apache.directory.server + apacheds-core-unit + 1.0-SNAPSHOT + test + + + org.apache.directory.server + apacheds-server-jndi + 1.0-SNAPSHOT + + + org.apache.directory.server + apacheds-server-ssl + 1.0-SNAPSHOT + + + org.apache.directory.server + apacheds-core + 1.0-SNAPSHOT + + + org.apache.directory.shared + shared-ldap + 0.9.5.3-SNAPSHOT + + + org.apache.directory.daemon + daemon-bootstrappers + 1.0-SNAPSHOT + + + jetty + org.mortbay.jetty + 5.1.8 + + + xerces + xmlParserAPIs + 2.6.2 + + + xerces + xercesImpl + 2.0.2 + + + tomcat + servlet-api + 5.0.18 + + + tomcat + jsp-api + 5.0.18 + + + tomcat + jasper-runtime + 5.5.9 + + + tomcat + jasper-compiler + 5.5.9 + + + tomcat + jasper-compiler-jdt + 5.5.9 + + + commons-el + commons-el + 1.0 + + + ant + ant + 1.6.5 + + + org.slf4j + nlog4j + 1.2.25 + provided + + + springframework + spring-core + 1.2.6 + + + springframework + spring-beans + 1.2.6 + + + commons-logging + commons-logging + 1.0.4 + + + springframework + spring-context + 1.2.6 + + + Added: directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingHotpMonitor.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingHotpMonitor.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingHotpMonitor.java (added) +++ directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingHotpMonitor.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,140 @@ +/* + * 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; + + +import javax.security.auth.kerberos.KerberosPrincipal; + +import org.safehaus.triplesec.verifier.hotp.HotpMonitor; +import org.safehaus.profile.ServerProfile; +import org.safehaus.otp.ResynchParameters; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * A Log4J based logging HOTP verifier monitor. + * + * @author Apache Directory Project + * @version $Rev$ + */ +public class LoggingHotpMonitor implements HotpMonitor +{ + /** underlying logger to use for logging events */ + private final Log log; + + + public LoggingHotpMonitor( Log log ) + { + if ( log == null ) + { + this.log = LogFactory.getLog( "HotpMonitor" ); + + return; + } + + this.log = log; + } + + + public LoggingHotpMonitor() + { + this.log = LogFactory.getLog( "HotpMonitor" ); + } + + + public void verificationFailed( ServerProfile p, ResynchParameters params ) + { + if ( log.isInfoEnabled() ) + { + log.info( "verification failed for " + p + " with " + params ); + } + } + + + public void initiatingResynch( ServerProfile p, ResynchParameters params ) + { + if ( log.isInfoEnabled() ) + { + log.info( "resynch initiated for " + p + " with " + params ); + } + } + + + public void checkingLookahead( ServerProfile p, ResynchParameters params ) + { + if ( log.isInfoEnabled() ) + { + log.info( "checking lookahead for " + p + " with " + params ); + } + } + + + public void integrityCheckFailed( ServerProfile p ) + { + if ( log.isInfoEnabled() ) + { + log.info( "integrity check failed for " + p ); + } + } + + + public void resynchCompleted( ServerProfile p, ResynchParameters params ) + { + if ( log.isInfoEnabled() ) + { + log.info( "resynch completed for " + p + " with " + params ); + } + } + + + public void resynchInProgress( ServerProfile p, ResynchParameters params ) + { + if ( log.isInfoEnabled() ) + { + log.info( "resynch in progress for " + p + " with " + params ); + } + } + + + public void integrityCheckPassed( ServerProfile p ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "verification passed for " + p ); + } + } + + + public void accountLocked( ServerProfile p, ResynchParameters params ) + { + log.warn( "account locked for " + p + " with " + params ); + } + + + public void verifying( KerberosPrincipal principal ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "attempting verification for " + principal ); + } + } +} Added: directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingStoreMonitor.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingStoreMonitor.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingStoreMonitor.java (added) +++ directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/LoggingStoreMonitor.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,200 @@ +/* + * 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; + + +import javax.naming.directory.Attributes; +import javax.security.auth.kerberos.KerberosPrincipal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.safehaus.profile.ServerProfile; +import org.safehaus.triplesec.store.ServerProfileStore; +import org.safehaus.triplesec.store.StoreMonitorAdapter; + + +/** + * A log4j based logging StoreMonitor. + * + * @author Apache Directory Project + * @version $Rev$ + */ +public class LoggingStoreMonitor extends StoreMonitorAdapter +{ + /** underlying logger used for logging store events */ + private final Log log; + + + public LoggingStoreMonitor( Log log ) + { + if ( log == null ) + { + this.log = LogFactory.getLog( "StoreMonitor" ); + + return; + } + + this.log = log; + } + + + public LoggingStoreMonitor() + { + this.log = LogFactory.getLog( "StoreMonitor" ); + } + + + // ------------------------------------------------------------------------ + // Callbacks + // ------------------------------------------------------------------------ + + + public void profileAdded( ServerProfileStore store, ServerProfile profile ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "store added profile " + profile.getUserId() + " to realm " + profile.getRealm() ); + } + } + + + public void info( ServerProfileStore store, String s ) + { + log.info( s ); + } + + + public void profileImported( ServerProfileStore store, String dn, Attributes attributes ) + { + if ( log.isInfoEnabled() ) + { + log.info( "store imported profile - " + dn + " with attributes " + attributes ); + } + } + + + public void profileNotImported( ServerProfileStore store, String dn, Attributes attributes ) + { + if ( log.isInfoEnabled() ) + { + log.info( "store did not imported profile - " + dn + " because it already existed" ); + } + } + + + /* + public void profileDeleted( ServerProfileStore store, KerberosPrincipal principal, ServerProfile profile ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "store deleted profile " + profile.getUserId() + " from realm " + profile.getRealm() ); + } + } + + + public void profileAccessed( ServerProfileStore store, KerberosPrincipal principal, ServerProfile profile ) + { + if ( log.isDebugEnabled() ) + { + log.debug( "store accessed profile " + profile.getUserId() + " in realm " + profile.getRealm() ); + } + } + + + public void profileUpdated( ServerProfileStore store, ServerProfile profile, ModificationItem[] mods ) + { + String report = getModificationReport( mods ); + + log.info( "store updated profile " + profile.getUserId() + " in realm " + profile.getRealm() + ": " + report ); + } + + + private String getModificationType( ModificationItem item ) + { + switch( item.getModificationOp() ) + { + case( DirContext.REMOVE_ATTRIBUTE ): + + return "remove"; + + case( DirContext.REPLACE_ATTRIBUTE ): + + return "replace"; + + case( DirContext.ADD_ATTRIBUTE ): + + return "add"; + + default: + throw new IllegalArgumentException( "unknown mod type" ); + } + } + + + private String getModificationReport( ModificationItem[] mods ) + { + StringBuffer buf = new StringBuffer(); + + for ( int ii = 0; ii < mods.length; ii++ ) + { + buf.append( "modop: " ).append( getModificationType( mods[ii] ) ); + + buf.append( "attribute: " ).append( mods[ii].getAttribute() ); + } + + return buf.toString(); + } + */ + + public void storeInitialized( ServerProfileStore store ) + { + log.info( "store initialized" ); + } + + + public void storeFailure( ServerProfileStore store ) + { + log.error( "store failed - no further info available" ); + } + + + public void storeFailure( ServerProfileStore store, Throwable t ) + { + log.error( "store failed - " + t.getMessage(), t ); + } + + + public void storeFailure( ServerProfileStore store, KerberosPrincipal principal ) + { + log.error( "store failed - operation for principal " + principal.getName() ); + } + + + public void storeFailure( ServerProfileStore store, KerberosPrincipal principal, Throwable t ) + { + log.error( "store failed - on principal " + principal.getName() + " - " + t.getMessage(), t ); + } + + + public void storeFailure( ServerProfileStore store, KerberosPrincipal principal, String s ) + { + log.error( "store failed - on principal " + principal.getName() + " - " + s ); + } +} Added: directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/Service.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/Service.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/Service.java (added) +++ directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/Service.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,597 @@ +/* + * 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; + + +import java.io.File; +import java.io.FileFilter; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.security.auth.kerberos.KerberosPrincipal; + +import org.apache.directory.daemon.DaemonApplication; +import org.apache.directory.daemon.InstallationLayout; +import org.apache.directory.server.core.configuration.ShutdownConfiguration; +import org.apache.directory.server.core.configuration.SyncConfiguration; +import org.apache.directory.server.kerberos.sam.SamSubsystem; +import org.apache.directory.shared.ldap.name.LdapDN; +import org.apache.directory.shared.ldap.util.PropertiesUtils; + +import org.mortbay.http.SocketListener; +import org.mortbay.jetty.Server; +import org.mortbay.jetty.servlet.WebApplicationContext; + +import org.safehaus.profile.BaseServerProfileModifier; +import org.safehaus.profile.ProfileTestData; +import org.safehaus.profile.ServerProfile; +import org.safehaus.triplesec.configuration.MutableTriplesecStartupConfiguration; +import org.safehaus.triplesec.store.DefaultServerProfileStore; +import org.safehaus.triplesec.store.ServerProfileStore; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.FileSystemXmlApplicationContext; + + +/** + * The Triplesec service launched by daemons. + * + * @author Alex Karasulu + * @version $Rev$ + */ +public class Service implements DaemonApplication +{ + /** this is the value the port override members have when they are not defined */ + private static final int UNDEFINED_PORT_OVERRIDE = -2; + /** The period in milliseconds to wait before flushing db buffer cache */ + private static final int SYNCPERIOD_MILLIS = 20000; + /** THe default port for the HTTP service */ + private static final String HTTP_PORT_DEFAULT = "8383"; + + /** Setup a logger */ + private static Logger log = LoggerFactory.getLogger( TriplesecUberjarMain.class ); + + /** The environment setting used to start the server */ + private Properties env; + /** the time the server was started */ + private long startTime; + /** a possible override port to use instead of what is present within the configuration */ + private int httpPortOverride = UNDEFINED_PORT_OVERRIDE; + /** a possible override port to use instead of what is present within the configuration */ + private int ldapPortOverride = UNDEFINED_PORT_OVERRIDE; + /** a possible override port to use instead of what is present within the configuration */ + private int ldapsPortOverride = UNDEFINED_PORT_OVERRIDE; + /** a possible override port to use instead of what is present within the configuration */ + private int krb5PortOverride = UNDEFINED_PORT_OVERRIDE; + /** a possible override port to use instead of what is present within the configuration */ + private int changepwPortOverride = UNDEFINED_PORT_OVERRIDE; + /** a possible override port to use instead of what is present within the configuration */ + private int ntpPortOverride = UNDEFINED_PORT_OVERRIDE; + /** embedded Jetty httpService */ + private Server httpService; + /** setting to disable the shutdown hook for test cases: enabled by default */ + private boolean enableShutdownHook = true; + /** setting to disable embedded HTTP web service: enabled by default */ + private boolean enableHttpService = true; + + private TriplesecInstallationLayout layout; + private Thread workerThread = null; + private SynchWorker worker = new SynchWorker(); + private boolean startNoWait = false; + + + /** + * Sets the override port for the HTTP server. If this value is set then the value is + * used for the HTTP server port instead of what is set within the configuration. + * + * @param httpPortOverride the HTTP port to use + */ + public void setHttpPortOverride( int httpPortOverride ) + { + this.httpPortOverride = httpPortOverride; + } + + + /** + * Sets the override port for the LDAP server. If this value is set then the value is + * used for the LDAP server port instead of what is set within the configuration. + * + * @param ldapPortOverride the LDAP port to use + */ + public void setLdapPortOverride( int ldapPortOverride ) + { + this.ldapPortOverride = ldapPortOverride; + } + + + /** + * Sets the override port for the LDAPS server. If this value is set then the value is + * used for the LDAPS server port instead of what is set within the configuration. + * + * @param ldapsPortOverride the LDAP port to use + */ + public void setLdapsPortOverride( int ldapsPortOverride ) + { + this.ldapsPortOverride = ldapsPortOverride; + } + + + /** + * Sets the override port for the Kerberos KDC. If this value is set then the value is + * used for the KDC server port instead of what is set within the configuration. + * + * @param krb5PortOverride the Kerberos port to use + */ + public void setKrb5PortOverride( int krb5PortOverride ) + { + this.krb5PortOverride = krb5PortOverride; + } + + + /** + * Sets the override port for the Changepw Server. If this value is set then the value is + * used for the Changepw server port instead of what is set within the configuration. + * + * @param changepwPortOverride the Changepw port to use + */ + public void setChangepwPortOverride( int changepwPortOverride ) + { + this.changepwPortOverride = changepwPortOverride; + } + + + /** + * Sets the override port for the NTP server. If this value is set then the value is + * used for the NTP server port instead of what is set within the configuration. + * + * @param ntpPortOverride the NTP port to use + */ + public void setNtpPortOverride( int ntpPortOverride ) + { + this.ntpPortOverride = ntpPortOverride; + } + + + private void configure( TriplesecInstallationLayout layout ) throws Exception + { + File configWebapp = new File( layout.getWebappsDirectory(), "config" ); + File configured = new File( configWebapp, "configured" ); + + // if the configuration application is not present then don't bother + if ( ! configWebapp.exists() ) + { + return; + } + + // if config app is present along with configured file inside then we exit too + if ( configured.exists() ) + { + return; + } + + // below here the server has not been configured and has the config app + httpService = new Server(); + SocketListener listener = new SocketListener(); + listener.setPort( Integer.parseInt( HTTP_PORT_DEFAULT ) ); + httpService.addListener( listener ); + httpService.addWebApplication( "config", configWebapp.getCanonicalPath() ); + httpService.start(); + + // warn to let user know server is waiting and to make sure user sees the message + if ( log.isWarnEnabled() ) + { + log.warn( "Waiting for admin to configure the server on port " + HTTP_PORT_DEFAULT ); + } + + // waits until the webapp places a 'configured' file after the user completes configuration + while ( ! configured.exists() ) + { + // check every second and a half for the directory's presence + Thread.sleep( 1500 ); + } + + if ( log.isInfoEnabled() ) + { + log.info( "Configuration stage completed ... starting normal initialization" ); + } + + httpService.stop(); + httpService.destroy(); + httpService = null; + } + + + public void init( InstallationLayout installationLayout, String[] args ) throws Exception + { + MutableTriplesecStartupConfiguration cfg; + + log.debug( "init(InstallationLayout,String[]) called" ); + + if ( ! ( installationLayout instanceof TriplesecInstallationLayout ) ) + { + layout = new TriplesecInstallationLayout ( installationLayout.getBaseDirectory() ); + } + else + { + layout = ( TriplesecInstallationLayout ) installationLayout; + } + + configure( layout ); + startTime = System.currentTimeMillis(); + + if ( layout != null ) + { + log.info( "server: loading settings from ", layout.getConfigurationFile() ); + ApplicationContext factory = null; + factory = new FileSystemXmlApplicationContext( layout.getConfigurationFile().toURL().toString() ); + cfg = ( MutableTriplesecStartupConfiguration ) factory.getBean( "configuration" ); + env = ( Properties ) factory.getBean( "environment" ); + } + else if ( args.length > 0 && new File( args[0] ).exists() ) // hack that takes server.xml file argument + { + log.info( "server: loading settings from ", args[0] ); + ApplicationContext factory = null; + factory = new FileSystemXmlApplicationContext( new File( args[0] ).toURL().toString() ); + cfg = ( MutableTriplesecStartupConfiguration ) factory.getBean( "configuration" ); + env = ( Properties ) factory.getBean( "environment" ); + } + else + { + throw new Exception( "Can't figure out where to fine my installation." ); + } + + cfg.setShutdownHookEnabled( enableShutdownHook ); + cfg.setEnableHttp( enableHttpService ); + cfg.setLdifDirectory( layout.getConfigurationDirectory().getAbsoluteFile() ); + + if ( httpPortOverride != UNDEFINED_PORT_OVERRIDE ) + { + cfg.setHttpPort( httpPortOverride ); + } + + if ( ldapPortOverride != UNDEFINED_PORT_OVERRIDE ) + { + cfg.setLdapPort( ldapPortOverride ); + } + + if ( ldapsPortOverride != UNDEFINED_PORT_OVERRIDE ) + { + cfg.setLdapsPort( ldapsPortOverride ); + } + + if ( layout != null ) + { + cfg.setWorkingDirectory( layout.getPartitionsDirectory() ); + } + + env.setProperty( Context.PROVIDER_URL, "ou=system" ); + env.setProperty( Context.INITIAL_CONTEXT_FACTORY, TriplsecContextFactory.class.getName() ); + env.putAll( cfg.toJndiEnvironment() ); + + if ( krb5PortOverride != UNDEFINED_PORT_OVERRIDE ) + { + cfg.setEnableKerberos( true ); + env.put( "kdc.ipPort", Integer.toString( krb5PortOverride ) ); + env.put( "kdc.ipPort", Integer.toString( krb5PortOverride ) ); + } + + if ( changepwPortOverride != UNDEFINED_PORT_OVERRIDE ) + { + cfg.setEnableChangePassword( true ); + env.put( "changepw.ipPort", Integer.toString( changepwPortOverride ) ); + env.put( "changepw.ipPort", Integer.toString( changepwPortOverride ) ); + } + + if ( ntpPortOverride != UNDEFINED_PORT_OVERRIDE ) + { + cfg.setEnableNtp( true ); + env.put( "ntp.ipPort", Integer.toString( ntpPortOverride ) ); + env.put( "ntp.ipPort", Integer.toString( ntpPortOverride ) ); + } + + // ------------------------------------------------------------------- + // Get and/or create the userContext where profiles are subordinates + // ------------------------------------------------------------------- + + DirContext userContext = null; + try + { + LdapDN dn = new LdapDN( env.getProperty( "safehaus.entry.basedn" ) ); + dn.remove( dn.size() - 1 ); + env.setProperty( Context.PROVIDER_URL, dn.toString() ); + userContext = new InitialDirContext( env ); + } + catch ( NamingException e ) + { + e.printStackTrace(); + System.exit( -5 ); + } + + // set the user context for the sam subsystem + SamSubsystem.getInstance().setUserContext( userContext, "ou=users" ); + + // setup demo profiles + try + { + if ( PropertiesUtils.get( env, "safehaus.load.testdata", true ) ) + { + ServerProfileStore store; + store = new DefaultServerProfileStore( ( DirContext ) userContext.lookup( "ou=Users" ) ); + addDemoProfiles( store, env.getProperty( "kdc.primary.realm" ) ); + } + } + catch ( NamingException e ) + { + e.printStackTrace(); + System.exit( -7 ); + } + + try + { + if ( cfg.isEnableHttp() ) + { + setupHttpService( cfg.getHttpPort() ); + } + } + catch ( Exception e ) + { + e.printStackTrace(); + System.exit( -8 ); + } + + workerThread = new Thread( worker, "SynchWorkerThread" ); + + if ( log.isInfoEnabled() ) + { + log.info( "server: started in {} milliseconds", ( System.currentTimeMillis() - startTime ) + "" ); + } + } + + + public void setupHttpService( int port ) throws Exception + { + httpService = new Server(); + httpService.setStatsOn( true ); + httpService.setTrace( true ); + + log.warn( "Setting trace and stats to on for Jetty" ); + + SocketListener listener = new SocketListener(); + listener.setPort( port ); + httpService.addListener( listener ); + + File webappsDir = new File( layout.getBaseDirectory(), "webapps" ); + File[] webapps = webappsDir.listFiles( new FileFilter() { + public boolean accept( File dir ) { return dir.isDirectory(); } + } ); + + if ( webapps == null ) + { + return; + } + + for ( int ii = 0; ii < webapps.length; ii++ ) + { + WebApplicationContext appContext = null; + + if ( webapps[ii].getName().equals( "ROOT" ) ) + { + appContext = httpService.addWebApplication( "/", webapps[ii].getCanonicalPath() ); + } + else if ( webapps[ii].getName().equals( "config" ) ) + { + continue; + } + else + { + appContext = httpService.addWebApplication( webapps[ii].getName(), webapps[ii].getCanonicalPath() ); + } + + appContext.setExtractWAR( true ); + appContext.setMimeMapping( "jar", "application/java-archive" ); + appContext.setMimeMapping( "jad", "text/vnd.sun.j2me.app-descriptor" ); + appContext.setMimeMapping( "cod", "application/vnd.rim.cod" ); + appContext.setMimeMapping( "wml", "text/vnd.wap.wml" ); + appContext.setMimeMapping( "wmls", "text/vnd.wap.wmlscript" ); + appContext.setMimeMapping( "wbxml", "application/vnd.wap.wbxml" ); + appContext.setMimeMapping( "wmlc", "application/vnd.wap.wmlc" ); + appContext.setMimeMapping( "wmlsc", "application/vnd.wap.wmlscriptc" ); + } + + //httpService.start(); + } + + + public void destroy() + { + log.debug( "destroy() called" ); + } + + + public void start() + { + try + { + if ( httpService != null ) + { + httpService.start(); + } + } + catch ( Exception e ) + { + e.printStackTrace(); + System.exit( -8 ); + } + workerThread.start(); + return; + } + + + public void stop( String[] args ) throws Exception + { + + try + { + if ( httpService != null ) + { + httpService.stop(); + httpService.destroy(); + httpService = null; + } + } + catch ( Exception e ) + { + e.printStackTrace(); + } + worker.stop = true; + synchronized ( worker.lock ) + { + worker.lock.notify(); + } + + while ( startNoWait && workerThread.isAlive() ) + { + log.info( "Waiting for SynchWorkerThread to die." ); + workerThread.join( 500 ); + } + + env.putAll( new ShutdownConfiguration().toJndiEnvironment() ); + new InitialDirContext( env ); + } + + + /** + * Adds profile test/demo data which is also setup on the client + * application. + * + * @param store the store to add the test data to + * @throws NamingException on failures while adding test profiles + */ + private static void addDemoProfiles( ServerProfileStore store, String realm ) throws NamingException + { + for ( int ii = 0; ii < ProfileTestData.PROFILES.length; ii++ ) + { + ServerProfile profile = ProfileTestData.PROFILES[ii]; + BaseServerProfileModifier modifier = new BaseServerProfileModifier(); + modifier.setActivationKey( profile.getActivationKey() ); + modifier.setFactor( profile.getFactor() ); + modifier.setFailuresInEpoch( profile.getFailuresInEpoch() ); + modifier.setInfo( profile.getInfo() ); + modifier.setLabel( profile.getLabel() ); + modifier.setPassword( profile.getPassword() ); + modifier.setRealm( realm ); + modifier.setResynchCount( profile.getResynchCount() ); + modifier.setSecret( profile.getSecret() ); + modifier.setTokenPin( profile.getTokenPin() ); + modifier.setUserId( profile.getUserId() ); + profile = modifier.getServerProfile(); + String name = profile.getUserId() + "@" + realm; + KerberosPrincipal principal = new KerberosPrincipal( name ); + + if ( ! store.hasProfile( principal ) ) + { + store.add( profile ); + } + } + } + + + public void synch() throws Exception + { + env.putAll( new SyncConfiguration().toJndiEnvironment() ); + new InitialDirContext( env ); + } + + + class SynchWorker implements Runnable + { + Object lock = new Object(); + boolean stop = false; + + + public void run() + { + while ( !stop ) + { + synchronized ( lock ) + { + try + { + lock.wait( SYNCPERIOD_MILLIS ); + } + catch ( InterruptedException e ) + { + log.warn( "SynchWorker failed to wait on lock.", e ); + } + } + + try + { + synch(); + } + catch ( Exception e ) + { + log.error( "SynchWorker failed to synch directory.", e ); + } + } + } + } + + public static final String BANNER = " _ _ ____ ____ \n" + + " / \\ _ __ __ _ ___| |__ ___| _ \\/ ___| \n" + + " / _ \\ | '_ \\ / _` |/ __| '_ \\ / _ \\ | | \\___ \\ \n" + + " / ___ \\| |_) | (_| | (__| | | | __/ |_| |___) | \n" + + " /_/ \\_\\ .__/ \\__,_|\\___|_| |_|\\___|____/|____/ \n" + + " |_| \n"; + + + public static void printBanner() + { + System.out.println( BANNER ); + } + + + public void setEnableShutdownHook( boolean enableShutdownHook ) + { + this.enableShutdownHook = enableShutdownHook; + } + + + public boolean isEnableShutdownHook() + { + return enableShutdownHook; + } + + public void setEnableHttpService( boolean enableHttpService ) + { + this.enableHttpService = enableHttpService; + } + + public boolean isEnableHttpService() + { + return enableHttpService; + } +} Added: directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecInstallationLayout.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecInstallationLayout.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecInstallationLayout.java (added) +++ directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecInstallationLayout.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,58 @@ +/* + * 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; + +import java.io.File; + +import org.apache.directory.daemon.InstallationLayout; + + +public class TriplesecInstallationLayout extends InstallationLayout +{ + public TriplesecInstallationLayout( File installBase ) + { + super( installBase ); + } + + + public TriplesecInstallationLayout( String installBase ) + { + super( installBase ); + } + + + public File getWebappsDirectory() + { + return getWebappsDirectory( "webapps" ); + } + + + public File getWebappsDirectory( String webappsPath ) + { + return new File( super.baseDirectory, webappsPath ); + } + + + public void mkdirs() + { + super.mkdirs(); + getWebappsDirectory().mkdirs(); + } +} Added: directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecUberjarMain.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecUberjarMain.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecUberjarMain.java (added) +++ directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplesecUberjarMain.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,61 @@ +/* + * 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; + +import java.io.File; + + + +/** + * The main application entry point for Triplesec as a java application. + * + * @author Alex Karasulu + * @version $Rev$ + */ +public class TriplesecUberjarMain +{ + /** + * Takes a single argument, the path to the installation home, which contains + * the configuration to load with server startup settings. + * + * @param args the arguments + */ + public static void main( String[] args ) throws Exception + { + Service server = new Service(); + + if ( args.length > 0 && new File( args[0] ).isDirectory() ) + { + server.init( new TriplesecInstallationLayout( args[0] ), null ); + server.start(); + } + else if ( args.length > 0 && new File( args[0] ).isFile() ) + { + server.init( null, args ); + server.start(); + } + else + { + server.init( null, null ); + server.start(); + } + } +} + Added: directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplsecContextFactory.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplsecContextFactory.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplsecContextFactory.java (added) +++ directory/trunks/triplesec/main/src/main/java/org/safehaus/triplesec/TriplsecContextFactory.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,48 @@ +/* + * 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; + + +import org.apache.directory.server.jndi.ServerContextFactory; +import org.apache.directory.server.core.DirectoryService; + +import javax.naming.NamingException; + + +/** + * Nothing for now. + * + * @author Alex Karasulu + * @version $Rev$ + */ +public class TriplsecContextFactory extends ServerContextFactory +{ + + public void afterStartup( DirectoryService service ) throws NamingException + { + super.afterStartup( service ); + } + + + public void afterShutdown( DirectoryService service ) + { + super.afterShutdown( service ); + } +} Added: directory/trunks/triplesec/main/src/test/org/safehaus/triplesec/ConfigurationTest.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/test/org/safehaus/triplesec/ConfigurationTest.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/test/org/safehaus/triplesec/ConfigurationTest.java (added) +++ directory/trunks/triplesec/main/src/test/org/safehaus/triplesec/ConfigurationTest.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,259 @@ +/* + * 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; + +import java.io.File; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Set; + +import javax.naming.Context; +import javax.naming.NamingException; + +import org.apache.ldap.common.berlib.asn1.SnickersProvider; +import org.apache.ldap.server.configuration.DirectoryPartitionConfiguration; +import org.apache.ldap.server.configuration.ServerStartupConfiguration; +import org.apache.ldap.server.jndi.ServerContextFactory; +import org.apache.ldap.server.schema.bootstrap.ApacheSchema; +import org.apache.ldap.server.schema.bootstrap.CoreSchema; +import org.apache.ldap.server.schema.bootstrap.CosineSchema; +import org.apache.ldap.server.schema.bootstrap.InetorgpersonSchema; +import org.apache.ldap.server.schema.bootstrap.Krb5kdcSchema; +import org.apache.ldap.server.schema.bootstrap.SystemSchema; +import org.safehaus.triplesec.store.ProfileObjectFactory; +import org.safehaus.triplesec.store.ProfileStateFactory; +import org.safehaus.triplesec.verifier.hotp.DefaultHotpSamVerifier; +import org.safehaus.triplesec.store.schema.SafehausSchema; +import junit.framework.TestCase; + +public class ConfigurationTest extends TestCase +{ + private ConfigurationFactory factory; + + private Hashtable env; + + public void setUp() + { + factory = new ConfigurationFactory(); + + env = new Hashtable(); + + // Put common properties + env.put( Configuration.KDC_PRIMARY_REALM, "EXAMPLE.COM" ); + env.put( Configuration.KDC_PRINCIPAL, "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ); + env.put( Configuration.CHANGEPW_PRINCIPAL, "kadmin/changepw@EXAMPLE.COM" ); + } + + public void testSufficientProperties() + { + factory.getInstance( env ); + } + + public void testInsufficientProperties() + { + env.clear(); + subTestInsufficientProperties(); + + env.clear(); + env.put( Configuration.KDC_PRIMARY_REALM, "EXAMPLE.COM" ); + subTestInsufficientProperties(); + + env.clear(); + env.put( Configuration.KDC_PRIMARY_REALM, "EXAMPLE.COM" ); + env.put( Configuration.KDC_PRINCIPAL, "krbtgt/EXAMPLE.COM@EXAMPLE.COM" ); + subTestInsufficientProperties(); + } + + private void subTestInsufficientProperties() + { + try + { + env = factory.getInstance( env ); + fail( "IllegalArgumentException must be thrown." ); + } + catch( IllegalArgumentException e ) + { + assertTrue( true ); + } + } + + public void testPreserveOriginalProperties() + { + env.put( "keyX", "valueY" ); + env = factory.getInstance( env ); + assertEquals( "valueY", env.get( "keyX" ) ); + } + + public void testJndiProperties() throws Exception + { + env = factory.getInstance( env ); + assertEquals( "dc=example,dc=com", env.get( Context.PROVIDER_URL ) ); + assertEquals( "uid=admin,ou=system", env.get( Context.SECURITY_PRINCIPAL ) ); + assertEquals( "simple", env.get( Context.SECURITY_AUTHENTICATION ) ); + assertEquals( "secret", env.get( Context.SECURITY_CREDENTIALS ) ); + assertEquals( ServerContextFactory.class.getName(), env.get( Context.INITIAL_CONTEXT_FACTORY ) ); + assertEquals( ProfileStateFactory.class.getName(), env.get( Context.STATE_FACTORIES ) ); + assertEquals( ProfileObjectFactory.class.getName(), env.get( Context.OBJECT_FACTORIES ) ); + + // Do additional tests + tearDown(); + setUp(); + + env.put( Configuration.KDC_PRIMARY_REALM, "GLEAMYNODE.NET" ); + env = factory.getInstance( env ); + assertEquals( "dc=gleamynode,dc=net", env.get( Context.PROVIDER_URL ) ); + } + + public void testApacheDsProperties() throws NamingException + { + Hashtable env = factory.getInstance( this.env ); + + ServerStartupConfiguration cfg = ( ServerStartupConfiguration ) ServerStartupConfiguration.toConfiguration( env ); + + assertTrue( cfg.isEnableKerberos() ); + + Set partitions = cfg.getContextPartitionConfigurations(); + + assertEquals( 1, partitions.size() ); + + // let's test that all partition information is correct + + DirectoryPartitionConfiguration partition = ( DirectoryPartitionConfiguration ) partitions.iterator().next(); + + assertEquals( "dc=example,dc=com", partition.getSuffix() ); + + assertTrue( partition.getIndexedAttributes().contains( "ou" ) ); + assertTrue( partition.getIndexedAttributes().contains( "uid" ) ); + assertTrue( partition.getIndexedAttributes().contains( "objectClass" ) ); + assertTrue( partition.getIndexedAttributes().contains( "krb5PrincipalName" ) ); + assertTrue( partition.getIndexedAttributes().contains( "safehausUid" ) ); + assertTrue( partition.getIndexedAttributes().contains( "safehausRealm" ) ); + + assertEquals( "example", partition.getContextEntry().get( "dc" ).get() ); + + assertTrue( partition.getContextEntry().get( "objectClass" ).contains( "top" ) ); + assertTrue( partition.getContextEntry().get( "objectClass" ).contains( "domain" ) ); + + assertEquals( env.get( Configuration.WORK_DIRECTORY ) + + File.separator + "realms" + + File.separator + "example_com", cfg.getWorkingDirectory().getPath() ); + + assertEquals( SnickersProvider.class.getName(), env.get( Configuration.ASN1_BERLIB_PROVIDER ) ); + + assertTrue( cfg.isAllowAnonymousAccess() ); + assertTrue( cfg.isEnableNetworking() ); + + Set schemaTypes = new HashSet(); + Iterator it = cfg.getBootstrapSchemas().iterator(); + while( it.hasNext() ) + { + schemaTypes.add( it.next().getClass() ); + } + + assertTrue( schemaTypes.contains( SystemSchema.class ) ); + assertTrue( schemaTypes.contains( ApacheSchema.class ) ); + assertTrue( schemaTypes.contains( CoreSchema.class ) ); + assertTrue( schemaTypes.contains( CosineSchema.class ) ); + assertTrue( schemaTypes.contains( InetorgpersonSchema.class ) ); + assertTrue( schemaTypes.contains( Krb5kdcSchema.class ) ); + assertTrue( schemaTypes.contains( SafehausSchema.class ) ); + + assertEquals( 389, cfg.getLdapPort() ); + } + + public void testSafeHausProperties() + { + env = factory.getInstance( env ); + assertEquals( ".." + File.separator + "var", env.get( Configuration.WORK_DIRECTORY ) ); + assertEquals( "389", env.get( Configuration.LDAP_PORT ) ); + assertEquals( LoggingHotpMonitor.class.getName(), env.get( Configuration.VERIFIER_MONITOR ) ); + assertEquals( LoggingStoreMonitor.class.getName(), env.get( Configuration.STORE_MONITOR ) ); + assertEquals( "true", env.get( Configuration.LOAD_TEST_DATA ) ); + assertEquals( "ou=Users,dc=example,dc=com", env.get( Configuration.ENTRY_BASEDN ) ); + assertEquals( ".." + File.separator + "conf" + File.separator + "server.ldif", env.get( Configuration.ENTRY_LDIF_FILE ) ); + } + + public void testKerberosProperties() + { + env = factory.getInstance( env ); + assertEquals( DefaultHotpSamVerifier.class.getName(), env.get( Configuration.KERBEROS_SAM_TYPE ) ); + } + + public void testKdcProperties() + { + env = factory.getInstance( env ); + assertEquals( "88", env.get( Configuration.KDC_DEFAULT_PORT ) ); + assertEquals( ".." + File.separator + "conf" + File.separator + "server.ldif", env.get( Configuration.KDC_ENTRY_LDIF_FILE ) ); + assertEquals( "ou=Users,dc=example,dc=com", env.get( Configuration.KDC_ENTRY_BASEDN ) ); + assertEquals( "5", env.get( Configuration.KDC_ALLOWABLE_CLOCKSKEW ) ); + assertEquals( "kerberoskeys.ser", env.get( Configuration.KDC_KEYS_LOCATION ) ); + assertEquals( "des-cbc-md5 des3-cbc-sha1 des3-cbc-md5 " + + "des-cbc-md4 des-cbc-crc", + env.get( Configuration.KDC_ENCRYPTION_TYPES ) ); + assertEquals( "true", env.get( Configuration.KDC_PA_ENC_TIMESTAMP_REQUIRED ) ); + } + + public void testTgsProperties() + { + env = factory.getInstance( env ); + assertEquals( "1440", env.get( Configuration.TGS_MAXIMUM_TICKET_LIFETIME ) ); + assertEquals( "10080", env.get( Configuration.TGS_MAXIMUM_RENEWABLE_LIFETIME ) ); + assertEquals( "true", env.get( Configuration.TGS_EMPTY_ADDRESSES_ALLOWED ) ); + assertEquals( "true", env.get( Configuration.TGS_FORWARDABLE_ALLOWED ) ); + assertEquals( "true", env.get( Configuration.TGS_PROXIABLE_ALLOWED ) ); + assertEquals( "true", env.get( Configuration.TGS_POSTDATE_ALLOWED ) ); + assertEquals( "true", env.get( Configuration.TGS_RENEWABLE_ALLOWED ) ); + } + + public void testInducedProperties() throws Exception + { + env.put( Configuration.KDC_PRIMARY_REALM, "GLEAMYNODE.NET" ); + + env.put( Configuration.LDAP_PORT, "1024" ); + + env = factory.getInstance( env ); + + ServerStartupConfiguration cfg = ( ServerStartupConfiguration ) ServerStartupConfiguration.toConfiguration( env ); + + assertEquals( 1024, cfg.getLdapPort() ); + + assertEquals( env.get( Configuration.KDC_ENTRY_BASEDN ), + env.get( Configuration.ENTRY_BASEDN ) ); + + assertEquals( env.get( Configuration.KDC_ENTRY_LDIF_FILE ), + env.get( Configuration.ENTRY_LDIF_FILE ) ); + + assertTrue( cfg.isEnableNetworking() ); + + tearDown(); + + setUp(); + + // We don't need to do this anymore do we ??? + + env.put( Configuration.LDAP_PORT, "-1" ); + + env = factory.getInstance( env ); + cfg = ( ServerStartupConfiguration ) ServerStartupConfiguration.toConfiguration( env ); + + assertFalse( cfg.isEnableNetworking() ); + } +} Added: directory/trunks/triplesec/main/src/test/resources/log4j.properties URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/main/src/test/resources/log4j.properties?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/main/src/test/resources/log4j.properties (added) +++ directory/trunks/triplesec/main/src/test/resources/log4j.properties Tue Dec 12 07:23:31 2006 @@ -0,0 +1,6 @@ +log4j.rootCategory=ERROR, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=[%d{HH:mm:ss}] %p [%c] - %m%n + Added: directory/trunks/triplesec/otp/pom.xml URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/otp/pom.xml?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/otp/pom.xml (added) +++ directory/trunks/triplesec/otp/pom.xml Tue Dec 12 07:23:31 2006 @@ -0,0 +1,42 @@ + + + + 4.0.0 + + org.safehaus.triplesec + build + 1.0-SNAPSHOT + + triplesec-otp + Triplesec OTP API + + One Time Password Algorithm API + + jar + + + ${project.groupId} + triplesec-crypto + ${project.version} + + + Added: directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Base64.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Base64.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Base64.java (added) +++ directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Base64.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,211 @@ +/* + * 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.otp; + + +/** + * Decoding/Encoding of base64 characters to raw bytes. + * + * @author Alex Karasulu + * @version $Rev$ + */ +public class Base64 +{ + + /** + * passed data array. + * + * @param a_data the array of bytes to encode + * @return base64-coded character array. + */ + public static char[] encode( byte[] a_data ) + { + char [] l_out = new char[( ( a_data.length + 2 ) / 3 ) * 4]; + + // + // 3 bytes encode to 4 chars. Output is always an even + // multiple of 4 characters. + // + for ( int ii = 0, l_index = 0; ii < a_data.length; + ii += 3, l_index += 4 ) + { + boolean l_quad = false; + boolean l_trip = false; + + int l_val = ( 0xFF & ( int ) a_data[ii] ); + l_val <<= 8; + if ( ( ii + 1 ) < a_data.length ) + { + l_val |= ( 0xFF & ( int ) a_data[ii + 1] ); + l_trip = true; + } + + l_val <<= 8; + if ( ( ii + 2 ) < a_data.length ) + { + l_val |= ( 0xFF & ( int ) a_data[ ii + 2] ); + l_quad = true; + } + + l_out[l_index + 3] = + s_alphabet[( l_quad ? ( l_val & 0x3F ) : 64 )]; + l_val >>= 6; + l_out[l_index + 2] = + s_alphabet[( l_trip ? ( l_val & 0x3F ) : 64 )]; + l_val >>= 6; + l_out[l_index + 1] = s_alphabet[l_val & 0x3F]; + l_val >>= 6; + l_out[l_index + 0] = s_alphabet[l_val & 0x3F]; + } + return l_out; + } + + + /** + * Decodes a BASE-64 encoded stream to recover the original + * data. White space before and after will be trimmed away, + * but no other manipulation of the input will be performed. + * + * As of version 1.2 this method will properly handle input + * containing junk characters (newlines and the like) rather + * than throwing an error. It does this by pre-parsing the + * input and generating from that a count of VALID input + * characters. + * + * @param a_data data to decode. + * @return the decoded binary data. + */ + public static byte[] decode( char[] a_data ) + { + // as our input could contain non-BASE64 data (newlines, + // whitespace of any sort, whatever) we must first adjust + // our count of USABLE data so that... + // (a) we don't misallocate the output array, and + // (b) think that we miscalculated our data length + // just because of extraneous throw-away junk + + int l_tempLen = a_data.length; + for ( int ii = 0; ii < a_data.length; ii++ ) + { + if ( ( a_data[ii] > 255 ) || s_codes[ a_data[ii] ] < 0 ) + { + --l_tempLen; // ignore non-valid chars and padding + } + } + // calculate required length: + // -- 3 bytes for every 4 valid base64 chars + // -- plus 2 bytes if there are 3 extra base64 chars, + // or plus 1 byte if there are 2 extra. + + int l_len = ( l_tempLen / 4 ) * 3; + + if ( ( l_tempLen % 4 ) == 3 ) + { + l_len += 2; + } + + if ( ( l_tempLen % 4 ) == 2 ) + { + l_len += 1; + } + + byte[] l_out = new byte[l_len]; + + + + int l_shift = 0; // # of excess bits stored in accum + int l_accum = 0; // excess bits + int l_index = 0; + + // we now go through the entire array (NOT using the 'tempLen' value) + for ( int ii = 0; ii < a_data.length; ii++ ) + { + int l_value = ( a_data[ii] > 255 ) ? -1 : + s_codes[ a_data[ii] ]; + + if ( l_value >= 0 ) // skip over non-code + { + l_accum <<= 6; // bits shift up by 6 each time thru + l_shift += 6; // loop, with new bits being put in + l_accum |= l_value; // at the bottom. whenever there + if ( l_shift >= 8 ) // are 8 or more shifted in, write them + { + l_shift -= 8; // out (from the top, leaving any excess + l_out[l_index++] = // at the bottom for next iteration. + ( byte ) ( ( l_accum >> l_shift ) & 0xff ); + } + } + // we will also have skipped processing a padding null byte ('=') here; + // these are used ONLY for padding to an even length and do not legally + // occur as encoded data. for this reason we can ignore the fact that + // no index++ operation occurs in that special case: the out[] array is + // initialized to all-zero bytes to start with and that works to our + // advantage in this combination. + } + + // if there is STILL something wrong we just have to throw up now! + if ( l_index != l_out.length ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "Miscalculated data length (wrote " ); + buf.append( l_index ); + buf.append( " instead of " ); + buf.append(l_out.length ); + buf.append( ")" ); + throw new Error( buf.toString() ); + } + + return l_out; + } + + /** code characters for values 0..63 */ + private static char[] s_alphabet = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" + .toCharArray(); + + /** lookup table for converting base64 characters to value in range 0..63 */ + private static byte[] s_codes = new byte[256]; + + static { + for ( int ii = 0; ii < 256; ii++ ) + { + s_codes[ii] = -1; + } + + for ( int ii = 'A'; ii <= 'Z'; ii++ ) + { + s_codes[ii] = ( byte ) ( ii - 'A' ); + } + + for ( int ii = 'a'; ii <= 'z'; ii++ ) + { + s_codes[ii] = ( byte ) ( 26 + ii - 'a' ); + } + + for ( int ii = '0'; ii <= '9'; ii++ ) + { + s_codes[ii] = ( byte ) ( 52 + ii - '0' ); + } + + s_codes['+'] = 62; + s_codes['/'] = 63; + } +} + Added: directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Hotp.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Hotp.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Hotp.java (added) +++ directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/Hotp.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,137 @@ +/* + * 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.otp; + + +import org.safehaus.crypto.HMac; +import org.safehaus.crypto.SHA1Digest; +import org.safehaus.crypto.KeyParameter; +import org.safehaus.crypto.CipherParameters; + + +/** + * Generates a one time password using HMAC-SHA1. This OTP algorithm is + * described within an ietf draft. + * + * @author Alex Karasulu + * @version $Rev: 585 $ + */ +public class Hotp +{ + private static final int[] DIGITS_POWER + = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; + + /** the default minimum size for the HOTP value */ + private static final int MIN_DIGITS = 6; + + /** the default maximum size for the HOTP value */ + private static final int MAX_DIGITS = 10; + + + /** + * Generates an HOTP value using a shared secret (K), a counter for the + * moving factor (C), and a HOTP value size (Digits). + * + * @param secret the shared secret key known to the token and the validator + * @param counter the movign factor + * @param digits the number of digits to produce for the HOTP value which + * should be between the range of 6 to 10 inclusive + * @return the generated HOTP value according to the specification + */ + public static String generate( byte[] secret, long counter, int digits ) + { + StringBuffer result = new StringBuffer(); + + if ( MIN_DIGITS < digits || digits > MAX_DIGITS ) + { + throw new IllegalArgumentException( "Number of digits not within range: " + + MIN_DIGITS + " < digits > " + MAX_DIGITS ); + } + + if ( secret == null || secret.length == 0 ) + { + throw new IllegalArgumentException( "Shared secret shouldn't be null or empty" ); + } + + byte[] hash = stepOne( secret, counter ); + + // put selected bytes into result int + int offset = 0; + int binary = ( ( hash[offset] & 0x7f ) << 24 ) | + ( ( hash[offset + 1] & 0xff ) << 16 ) | + ( ( hash[offset + 2] & 0xff ) << 8 ) | + ( hash[offset + 3] & 0xff ); + + int otp = binary % DIGITS_POWER[digits]; + result.append( Integer.toString( otp ) ); + while ( result.length() < digits ) + { + result.insert( 0, "0" ); + } + return result.toString(); + } + + + /** + * The first step to generate the HMAC-SHA-1 value. + * + * @param secretKey the shared secret key + * @param counter the counter value (moving factor C) + * @return the 20 byte HMAC-SHA-1 value + */ + static byte[] stepOne( byte[] secretKey, long counter ) + { + HMac mac = new HMac( new SHA1Digest() ); + byte[] value = new byte[mac.getMacSize()]; + CipherParameters params = new KeyParameter( secretKey ); + mac.init( params ); + mac.update( getCounterBytes( counter ), 0, 8 ); + mac.doFinal( value, 0 ); + return value; + } + + + // ------------------------------------------------------------------------- + // Utility Methods + // ------------------------------------------------------------------------- + + + /** + * Calculates the 8 bytes for the long counter where the 8 most significant + * bits are in the first byte, then next 8 MSBs are in the second byte and + * so on. + * + * @param counter the long counter value (C) + * @return the byte array of length 8 representing the counter + */ + static byte[] getCounterBytes( long counter ) + { + byte[] counterBytes = new byte[8]; + counterBytes[7] = ( byte ) counter; + counterBytes[6] = ( byte ) ( counter >> 8 ); + counterBytes[5] = ( byte ) ( counter >> 16 ); + counterBytes[4] = ( byte ) ( counter >> 24 ); + counterBytes[3] = ( byte ) ( counter >> 32 ); + counterBytes[2] = ( byte ) ( counter >> 40 ); + counterBytes[1] = ( byte ) ( counter >> 48 ); + counterBytes[0] = ( byte ) ( counter >> 56 ); + return counterBytes; + } +} Added: directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributes.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributes.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributes.java (added) +++ directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributes.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,151 @@ +/* + * 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.otp; + + +/** + * Attributes use to calculate a HOTP value. + * + * @author Alex Karasulu + * @version $Rev$ + */ +public final class HotpAttributes +{ + private static final int DEFAULT_SIZE = 6; + + /** the shared secret key */ + private final byte[] secret; + /** the moving factor */ + private final long factor; + /** the size of the value to generate */ + private final int size; + + + /** + * Creates a HotpAttributes object with all three attributes. + * + * @param factor the moving factor + * @param secret the shared secret + */ + public HotpAttributes( long factor, byte[] secret ) + { + this( DEFAULT_SIZE, factor, secret ); + } + + + /** + * Creates a HotpAttributes object with all three attributes. + * + * @param size the size of the value to generate + * @param factor the moving factor + * @param secret the shared secret + */ + public HotpAttributes( int size, long factor, byte[] secret ) + { + if ( 6 > size || size > 10 ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "expecting HOTP value size in range [6,10] but got " ); + buf.append( size ); + throw new IllegalArgumentException( buf.toString() ); + } + + this.size = size; + this.factor = factor; + this.secret = secret; + } + + + /** + * Gets the shared secret key. + * + * @return the shared secret key + */ + public byte[] getSecret() + { + return secret; + } + + + /** + * Gets the moving factor. + * + * @return the moving factor + */ + public long getFactor() + { + return factor; + } + + + /** + * Gets the size of the value to generate. + * + * @return the size of charactor in the generated OTP + */ + public int getSize() + { + return size; + } + + + /** + * Checks to see that another HotpAttributes matches this one exactly. + * + * @param obj the other object to compare this HotpAttributes to + * @return true if the objects are the same for all components + */ + public boolean equals( Object obj ) + { + if ( obj == this ) + { + return true; + } + + if ( ! ( obj instanceof HotpAttributes ) ) + { + return false; + } + + HotpAttributes other = ( HotpAttributes ) obj; + if ( other.getSize() != this.size ) + { + return false; + } + if ( other.getFactor() != this.factor ) + { + return false; + } + if ( other.getSecret().length != this.secret.length ) + { + return false; + } + + for ( int ii = 0; ii < this.secret.length; ii++ ) + { + if ( this.secret[ii] != other.getSecret()[ii] ) + { + return false; + } + } + + return true; + } +} Added: directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributesCipher.java URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributesCipher.java?view=auto&rev=486187 ============================================================================== --- directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributesCipher.java (added) +++ directory/trunks/triplesec/otp/src/main/java/org/safehaus/otp/HotpAttributesCipher.java Tue Dec 12 07:23:31 2006 @@ -0,0 +1,254 @@ +/* + * 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.otp; + + +import org.safehaus.crypto.BlockCipherWrapper; +import org.safehaus.crypto.DESEngine; + +import java.io.UnsupportedEncodingException; + + +/** + * Encypts and decrypts HOTP attributes. HOTP attributes are stored as binary information + * using the following data structure. The result is DES encrypted then base 64 encoded. + *
+ * [[f1 value][f2 value][hotp size][factor][length][secret]]
+ * where ...
+ * f1 value   = 8 bytes        = the hotp value generated using factor-1 (encoded long)
+ * f2 value   = 8 bytes        = the hotp value generated using factor-2 (encoded long)
+ * hotp size  = 1 byte         = the unsigned # of characters in HOTP value within range [6-10]
+ * factor     = 8 bytes        = the current moving factor
+ * length     = 1 byte         = the unsigned length of the shared secret field (max 256)
+ * secret     = length bytes   = the shared secret
+ * 
+ * + * @author Alex Karasulu + * @version $Rev$ + */ +public class HotpAttributesCipher +{ + /** + * Takes HotpAttributes and formats them into a byte[] after calculating f1, f2, hotpSize, + * factor, and length bytes. This formated byte[] is then encrypted using the password and + * a block cipher. The encrypted result is then Base64 encoded for handling as a printable + * String. + * + * @param password the key to use when encrypting the attributes + * @param attributes the HotpAttributes to encrypt + * @return the formated, then encrypted, then base64 encoded string + * @throws UnsupportedEncodingException if the UTF-8 character encoding is not supported + */ + public static String encrypt( String password, HotpAttributes attributes ) + throws UnsupportedEncodingException + { + // encode all the components of the HOTP binary record as specified above in the class level javadoc + byte[] f1 = encodeLong( Long.parseLong( Hotp.generate( attributes.getSecret(), + attributes.getFactor()-1, attributes.getSize() ) ) ); + byte[] f2 = encodeLong( Long.parseLong( Hotp.generate( attributes.getSecret(), + attributes.getFactor()-2, attributes.getSize() ) ) ); + byte hotpSize = encodeUnsignedByte( attributes.getSize() ); + byte[] factor = encodeLong( attributes.getFactor() ); + byte length = encodeUnsignedByte( attributes.getSecret().length ); + + // calculate and build the record by copying all components into their proper positions + int totalLength = f1.length + f2.length + 1 + factor.length + 1 + attributes.getSecret().length; + byte[] input = new byte[totalLength]; + int pos = 0; + + // copy the f1 bytes + System.arraycopy( f1, 0, input, pos, f1.length ); + pos += f1.length; + + // copy the f2 bytes + System.arraycopy( f2, 0, input, pos, f2.length ); + pos += f2.length; + + // copy the hotpSize byte + input[pos] = hotpSize; + pos++; + + // copy the factor bytes + System.arraycopy( factor, 0, input, pos, factor.length ); + pos += factor.length; + + // copy the length bytes + input[pos] = length; + pos++; + + // copy the secret bytes + System.arraycopy( attributes.getSecret(), 0, input, pos, attributes.getSecret().length ); + + + // initialize the cipher engine and encrypt the record + BlockCipherWrapper engine = new BlockCipherWrapper( new DESEngine().getClass() ); + byte[] encrypted = engine.encrypt( password, input ); + + // base64 encode the encrypted record and return the string + return new String( Base64.encode( encrypted ) ); + } + + + /** + * Decrypts the encrypted HotpAtrributes using the provided password. It first Base64 decodes + * the encrypted string into the encrypted byte array. The encrypted array is then decrypted + * using a cypher engine. The decrypted attributes are then decoded based on the format for + * the hotp attributes. If any errors or inconsistancies are found while extracting parameters + * from the decrypted record, null is returned to denote the failure to decrypt. Once all + * parameters are extracted. The HOTP credentials are verified using the f1 and f2 parameters + * to check for integrity. + * + * @param password the password to use for decrypting the encrypted hotp attributes + * @param encrypted the attributes in that are encrypted, format encoded and base64 encoded + * @return a non-null HotpAttribute if the decryption succeed with verification, or null if it did not + * @throws UnsupportedEncodingException if the UTF-8 character encoding is not supported + */ + public static HotpAttributes decrypt( String password, String encrypted ) throws UnsupportedEncodingException + { + // base 64 decode the input + byte[] input = Base64.decode( encrypted.toCharArray() ); + + // initialize the cipher wrapper and decrypt + BlockCipherWrapper engine = new BlockCipherWrapper( new DESEngine().getClass() ); + byte[] decrypted = engine.decrypt( password, input ); + + // check that we have at least 19 bytes in the decrypted output if not return null for failure + if ( decrypted.length < 19 ) + { + return null; + } + + // get the f1 and f2 hotp values as longs for integrity verification + long f1 = decodeLong( decrypted, 0 ); + long f2 = decodeLong( decrypted, 8 ); + + // get the hotpSize and make sure it's within a valid range if not return null for failure + int hotpSize = decodeUnsignedByte( decrypted[16] ); + if ( 6 > hotpSize || hotpSize > 10 ) + { + return null; + } + + // get the moving factor value and the field for the length of the shared secret bytes + // determine if the length is valid, meaning that reading this length will not cause + // an index out of bounds exception by overrunning the size of the decrypted array + long factor = decodeLong( decrypted, 17 ); + int secretLength = decodeUnsignedByte( decrypted[25] ); + if ( secretLength + 26 >= decrypted.length ) + { + return null; + } + + // acquire the secret and begin verification to make sure decryption succeeded + byte[] secret = new byte[secretLength]; + System.arraycopy( decrypted, 26, secret, 0, secretLength ); + if ( f1 != Long.parseLong( Hotp.generate( secret, factor-1, hotpSize ) ) ) + { + return null; + } + if ( f2 != Long.parseLong( Hotp.generate( secret, factor-2, hotpSize ) ) ) + { + return null; + } + + return new HotpAttributes( factor, secret ); + } + + + /** + * Gets unsigned value of a byte as an int. + */ + public static int decodeUnsignedByte( byte bite ) + { + if ( bite > 0 ) + { + return bite; + } + + int value = 0; + value |= ( (int) bite ) & 0x000000ff; + return value; + } + + + /** + * Gets encoded byte value of a int as an unsigned bite. + */ + public static byte encodeUnsignedByte( int unsignedByte ) + { + if ( 0 > unsignedByte || unsignedByte > 255 ) + { + StringBuffer buf = new StringBuffer(); + buf.append( "unsignedByte are was " ); + buf.append( unsignedByte ); + buf.append( ": value must be in range [0-255]" ); + throw new IllegalArgumentException( buf.toString() ); + } + + return ( byte ) unsignedByte; + } + + + /** + * Encodes a long into 8 bytes. + * + * @param source the long to encode + * @return the encoded 8 byte array + */ + public static byte[] encodeLong( long source ) + { + byte[] encoded = new byte[8]; + + encoded[7] = ( byte ) source; + encoded[6] = ( byte ) ( source >> 8 ); + encoded[5] = ( byte ) ( source >> 16 ); + encoded[4] = ( byte ) ( source >> 24 ); + encoded[3] = ( byte ) ( source >> 32 ); + encoded[2] = ( byte ) ( source >> 40 ); + encoded[1] = ( byte ) ( source >> 48 ); + encoded[0] = ( byte ) ( source >> 56 ); + + return encoded; + } + + + /** + * Decodes an 8 byte encoded long into a Java primitive long. + * + * @param bites the bytes containing the encoded long + * @param offset the offset from zero where the bytes begin + * @return the decoded primitive long + */ + public static long decodeLong( byte[] bites, int offset ) + { + long value = 0 ; + + value |= ( ( (long) bites[offset] ) << 56 ) & 0xff00000000000000L; + value |= ( ( (long) bites[offset+1] ) << 48 ) & 0x00ff000000000000L; + value |= ( ( (long) bites[offset+2] ) << 40 ) & 0x0000ff0000000000L; + value |= ( ( (long) bites[offset+3] ) << 32 ) & 0x000000ff00000000L; + value |= ( ( (long) bites[offset+4] ) << 24 ) & 0x00000000ff000000L; + value |= ( ( (long) bites[offset+5] ) << 16 ) & 0x0000000000ff0000L; + value |= ( ( (long) bites[offset+6] ) << 8 ) & 0x000000000000ff00L; + value |= ( (long) bites[offset+7] ) & 0x00000000000000ffL; + + return value ; + } +}