You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by tb...@apache.org on 2006/12/12 16:24:14 UTC
svn commit: r486187 [38/49] - in /directory/trunks/triplesec: ./ admin-api/
admin-api/src/ admin-api/src/main/ admin-api/src/main/java/
admin-api/src/main/java/org/ admin-api/src/main/java/org/safehaus/
admin-api/src/main/java/org/safehaus/triplesec/ a...
Added: directory/trunks/triplesec/verifier/src/test/java/org/safehaus/triplesec/verifier/hotp/HotpSamVerifierITest.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/verifier/src/test/java/org/safehaus/triplesec/verifier/hotp/HotpSamVerifierITest.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/verifier/src/test/java/org/safehaus/triplesec/verifier/hotp/HotpSamVerifierITest.java (added)
+++ directory/trunks/triplesec/verifier/src/test/java/org/safehaus/triplesec/verifier/hotp/HotpSamVerifierITest.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,361 @@
+/*
+ * 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.verifier.hotp;
+
+
+import java.io.IOException;
+import java.io.File;
+import java.util.*;
+
+import javax.naming.NamingException;
+import javax.naming.Context;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.security.auth.kerberos.KerberosKey;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionEngine;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionEngineFactory;
+import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
+import org.apache.directory.server.kerberos.shared.exceptions.KerberosException;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedDataEncoder;
+import org.apache.directory.server.kerberos.shared.io.encoder.EncryptedTimestampEncoder;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedData;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStamp;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptedTimeStampModifier;
+import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
+import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
+import org.apache.directory.server.kerberos.sam.SamException;
+import org.apache.directory.server.kerberos.sam.TimestampChecker;
+import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
+import org.apache.directory.shared.ldap.message.LockableAttributesImpl;
+import org.apache.directory.server.core.configuration.Configuration;
+import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
+import org.apache.directory.server.core.configuration.MutableStartupConfiguration;
+import org.apache.directory.server.core.configuration.ShutdownConfiguration;
+import org.apache.directory.server.core.schema.bootstrap.ApacheSchema;
+import org.apache.directory.server.core.schema.bootstrap.CoreSchema;
+import org.apache.directory.server.core.schema.bootstrap.CosineSchema;
+import org.apache.directory.server.core.schema.bootstrap.InetorgpersonSchema;
+import org.apache.directory.server.core.schema.bootstrap.Krb5kdcSchema;
+import org.apache.directory.server.core.schema.bootstrap.SystemSchema;
+import org.apache.directory.server.protocol.shared.store.Krb5KdcEntryFilter;
+import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
+import org.safehaus.otp.Hotp;
+import org.safehaus.otp.HotpErrorConstants;
+import org.safehaus.otp.ResynchParameters;
+import org.safehaus.profile.ServerProfile;
+import org.safehaus.triplesec.store.DefaultServerProfileStore;
+import org.safehaus.triplesec.store.ServerProfileStore;
+import org.safehaus.triplesec.store.ProfileStateFactory;
+import org.safehaus.triplesec.store.ProfileObjectFactory;
+import org.safehaus.triplesec.store.schema.SafehausSchema;
+
+
+/**
+ * Test cases for the HOTP SAM verifier class.
+ *
+ * @version $Rev$
+ */
+public class HotpSamVerifierITest extends TestCase
+{
+ DirContext userContext;
+ DefaultServerProfileStore store;
+
+ /**
+ * Creates the hotp verifier test class.
+ */
+ public HotpSamVerifierITest()
+ {
+ super();
+ }
+
+
+ protected void setUp() throws Exception
+ {
+ File workingDirectory = new File ( System.getProperty( "workingDirectory" ) );
+ if ( ! workingDirectory.exists() )
+ {
+ workingDirectory.mkdirs();
+ }
+ FileUtils.forceDelete( workingDirectory );
+
+ MutableStartupConfiguration config = new MutableStartupConfiguration();
+ config.setWorkingDirectory( workingDirectory );
+ config.setShutdownHookEnabled( false );
+ MutablePartitionConfiguration partConfig = new MutablePartitionConfiguration();
+ partConfig.setName( "example" );
+
+ HashSet indices = new HashSet();
+ indices.add( "dc" );
+ indices.add( "ou" );
+ indices.add( "objectClass" );
+ indices.add( "krb5PrincipalName" );
+ indices.add( "uid" );
+ partConfig.setIndexedAttributes( indices );
+
+ partConfig.setSuffix( "dc=example,dc=com" );
+
+ LockableAttributesImpl attrs = new LockableAttributesImpl();
+ LockableAttributeImpl attr = new LockableAttributeImpl( "objectClass" );
+ attr.add( "top" );
+ attr.add( "domain" );
+ attrs.put( attr );
+ attrs.put( "dc", "example" );
+ partConfig.setContextEntry( attrs );
+
+ Set schemas = new HashSet();
+ schemas.add( new SystemSchema() );
+ schemas.add( new SafehausSchema() );
+ schemas.add( new ApacheSchema() );
+ schemas.add( new CoreSchema() );
+ schemas.add( new CosineSchema() );
+ schemas.add( new InetorgpersonSchema() );
+ schemas.add( new Krb5kdcSchema() );
+ config.setBootstrapSchemas( schemas );
+ config.setContextPartitionConfigurations( Collections.singleton( partConfig ) );
+
+ Hashtable env = new Hashtable();
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+ env.put( Context.PROVIDER_URL, "dc=example,dc=com" );
+ env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+ env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+ env.put( Context.SECURITY_CREDENTIALS, "secret" );
+ env.put( Configuration.JNDI_KEY, config );
+ env.put( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+ env.put( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+
+ userContext = new InitialDirContext( env );
+ try
+ {
+ userContext = ( DirContext ) userContext.lookup( "ou=users" );
+ }
+ catch ( NamingException e )
+ {
+ Attributes users = new BasicAttributes( "objectClass", "top", true );
+ users.get( "objectClass" ).add( "organizationalUnit" );
+ attrs.put( "ou", "users" );
+ userContext = userContext.createSubcontext( "ou=users", attrs );
+ }
+
+ store = new DefaultServerProfileStore( userContext );
+ store.init();
+
+ List filters = Collections.singletonList( new Krb5KdcEntryFilter() );
+ LdifFileLoader loader = new LdifFileLoader( userContext, new File( "safehaus.ldif" ), filters, getClass().getClassLoader() );
+ loader.execute();
+ }
+
+
+ protected void tearDown() throws Exception
+ {
+ userContext.close();
+ ShutdownConfiguration config = new ShutdownConfiguration();
+ Hashtable env = new Hashtable();
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "org.apache.directory.server.core.jndi.CoreContextFactory" );
+ env.put( Context.PROVIDER_URL, "dc=example,dc=com" );
+ env.put( Context.SECURITY_PRINCIPAL, "uid=admin,ou=system" );
+ env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+ env.put( Context.SECURITY_CREDENTIALS, "secret" );
+ env.put( Configuration.JNDI_KEY, config );
+ env.put( Context.STATE_FACTORIES, ProfileStateFactory.class.getName() );
+ env.put( Context.OBJECT_FACTORIES, ProfileObjectFactory.class.getName() );
+ new InitialDirContext( env );
+
+ userContext = null;
+ store = null;
+ }
+
+
+ /**
+ * Generates the encrypted time stamp using a KerberosKey to mimic clients
+ *
+ * @param kerberosKey the kerberos key (from hotp value)
+ * @param time the kerberos time for timestamp
+ * @return the encrypted time stamp
+ */
+ private byte[] generateSad( KerberosKey kerberosKey, KerberosTime time )
+ {
+ EncryptionType keyType = EncryptionType.getTypeByOrdinal( kerberosKey.getKeyType() );
+ EncryptionKey key = new EncryptionKey( keyType, kerberosKey.getEncoded() );
+ byte[] sad = null;
+
+ try
+ {
+ // Create the Timestamp
+ EncryptedTimeStampModifier modifier = new EncryptedTimeStampModifier();
+ modifier.setKerberosTime( time );
+ EncryptedTimeStamp timeStamp = modifier.getEncryptedTimestamp();
+
+ // Encode the Timestamp into ASN.1
+ EncryptedTimestampEncoder encoder = new EncryptedTimestampEncoder();
+ byte[] timeBytes = encoder.encode( timeStamp );
+
+ // Encrypt the Timestamp
+ EncryptionEngine engine = EncryptionEngineFactory.getEncryptionEngineFor( key );
+ EncryptedData encryptedData = engine.getEncryptedData( key, timeBytes );
+
+ // Encode the EncryptedData
+ sad = EncryptedDataEncoder.encode( encryptedData );
+ }
+ catch (IOException ioe)
+ {
+ ioe.printStackTrace();
+ }
+ catch (KerberosException ke)
+ {
+ ke.printStackTrace();
+ }
+
+ return sad;
+ }
+
+
+ /**
+ * Tests that accounts lock out and ones that are locked from start do not
+ * succeed.
+ */
+ public void testLockedOut() throws SamException, IOException, NamingException
+ {
+ DefaultHotpSamVerifier samVerifierDefault = new DefaultHotpSamVerifier();
+ samVerifierDefault.setUserContext( userContext );
+ samVerifierDefault.setIntegrityChecker( new TimestampChecker() );
+ samVerifierDefault.startup();
+
+ assertNotNull( samVerifierDefault );
+ KerberosPrincipal lockedout = new KerberosPrincipal( "lockedout@EXAMPLE.COM" );
+
+ try
+ {
+ char[] hotp = "123456".toCharArray();
+ KerberosKey key = new KerberosKey( lockedout, hotp, "DES" );
+ byte[] sad = generateSad( key, new KerberosTime() );
+ samVerifierDefault.verify( lockedout, sad );
+ fail( "should not get here due to exception" );
+ }
+ catch ( SamException e )
+ {
+// assertEquals( HotpErrorConstants.LOCKEDOUT_MSG, e.getMessage() );
+ }
+
+ // --------------------------------------------------------------------
+ // Ok let's now try to lock out an unlocked existing account: akarauslu
+ // --------------------------------------------------------------------
+
+ KerberosPrincipal akarasulu = new KerberosPrincipal( "akarasulu@EXAMPLE.COM" );
+ int ii = 0;
+ final int limit = ResynchParameters.DEFAULTS.getLockoutCount();
+
+ for (; ii < limit; ii++ )
+ {
+ char[] hotp = "123456".toCharArray();
+ KerberosKey key = new KerberosKey( akarasulu, hotp, "DES" );
+ byte[] sad = generateSad( key, new KerberosTime() );
+
+ try
+ {
+ samVerifierDefault.verify( akarasulu, sad );
+ }
+ catch( SamException e )
+ {
+ assertEquals( HotpErrorConstants.HOTPAUTH_FAILURE_MSG, e.getMessage() );
+ }
+ }
+
+ assertEquals( limit, ii );
+
+ // this next attempt with a bad hotp value should take us over the limit
+
+ try
+ {
+ char[] hotp = "123456".toCharArray();
+ KerberosKey key = new KerberosKey( akarasulu, hotp, "DES" );
+ byte[] sad = generateSad( key, new KerberosTime() );
+ samVerifierDefault.verify( akarasulu, sad );
+ fail( "should not get here due to lockout" );
+ }
+ catch ( SamException e )
+ {
+ assertEquals( limit, ii );
+ assertEquals( HotpErrorConstants.LOCKEDOUT_MSG, e.getMessage() );
+ }
+ }
+
+
+ public void testResynch() throws SamException, NamingException, IOException
+ {
+ DefaultHotpSamVerifier samVerifierDefault = new DefaultHotpSamVerifier();
+ samVerifierDefault.setUserContext( userContext );
+ samVerifierDefault.setIntegrityChecker( new TimestampChecker() );
+ samVerifierDefault.startup();
+ ServerProfileStore s = samVerifierDefault.getStore();
+ KerberosPrincipal principal = new KerberosPrincipal( "akarasulu@EXAMPLE.COM" );
+ ServerProfile p = s.getProfile( principal );
+ long factor = p.getFactor() + 5;
+ byte[] secret = p.getSecret();
+ assertNotNull( samVerifierDefault );
+
+ try
+ {
+ char[] hotp = Hotp.generate( secret, factor, DefaultHotpSamVerifier.HOTP_SIZE ).toCharArray();
+ KerberosKey key = new KerberosKey( principal, hotp, "DES" );
+ byte[] sad = generateSad( key, new KerberosTime() );
+ samVerifierDefault.verify( principal, sad );
+ fail( "should not get here due to resynch" );
+ }
+ catch ( SamException e )
+ {
+ assertEquals( HotpErrorConstants.RESYNCH_STARTING_MSG, e.getMessage() );
+ }
+
+ char[] hotp = Hotp.generate( secret, factor + 1, DefaultHotpSamVerifier.HOTP_SIZE ).toCharArray();
+ KerberosKey key = new KerberosKey( principal, hotp, "DES" );
+ byte[] sad = generateSad( key, new KerberosTime() );
+ assertNotNull( samVerifierDefault.verify( principal, sad ) );
+ }
+
+
+ public void testNormal() throws SamException, NamingException, IOException
+ {
+ DefaultHotpSamVerifier samVerifierDefault = new DefaultHotpSamVerifier();
+ samVerifierDefault.setUserContext( userContext );
+ samVerifierDefault.setIntegrityChecker( new TimestampChecker() );
+ samVerifierDefault.startup();
+ assertNotNull( samVerifierDefault );
+
+ ServerProfileStore s = samVerifierDefault.getStore();
+ KerberosPrincipal principal = new KerberosPrincipal( "akarasulu@EXAMPLE.COM" );
+ ServerProfile p = s.getProfile( principal );
+ long factor = p.getFactor();
+ byte[] secret = p.getSecret();
+ for ( int ii = 0; ii < 100; ii++ )
+ {
+ char[] hotp = Hotp.generate( secret, factor + ii, DefaultHotpSamVerifier.HOTP_SIZE ).toCharArray();
+ KerberosKey key = new KerberosKey( principal, hotp, "DES" );
+ byte[] sad = generateSad( key, new KerberosTime() );
+ assertNotNull( samVerifierDefault.verify( principal, sad ) );
+ }
+ }
+}
Added: directory/trunks/triplesec/verifier/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/verifier/src/test/resources/log4j.properties?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/verifier/src/test/resources/log4j.properties (added)
+++ directory/trunks/triplesec/verifier/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/verifier/src/test/resources/safehaus.ldif
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/verifier/src/test/resources/safehaus.ldif?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/verifier/src/test/resources/safehaus.ldif (added)
+++ directory/trunks/triplesec/verifier/src/test/resources/safehaus.ldif Tue Dec 12 07:23:31 2006
@@ -0,0 +1,203 @@
+# -------------------------------------------------------------------
+#
+# 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.
+#
+#
+# EXAMPLE.COM is freely and reserved for testing according to this RFC:
+#
+# http://www.rfc-editor.org/rfc/rfc2606.txt
+#
+# -------------------------------------------------------------------
+
+dn: ou=Users, dc=example, dc=com
+objectclass: top
+objectclass: organizationalunit
+ou: Users
+
+dn: uid=akarasulu, ou=Users, dc=example,dc=com
+cn: Alex Karasulu
+sn: Karasulu
+givenname: Alex
+objectclass: top
+objectclass: uidObject
+objectclass: person
+objectclass: organizationalPerson
+objectclass: extensibleObject
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+objectclass: safehausProfile
+ou: Directory
+ou: Users
+l: Jacksonville
+uid: akarasulu
+krb5PrincipalName: akarasulu@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: akarasulu@example.com
+telephonenumber: +1 904 982 6882
+facsimiletelephonenumber: +1 904 982 6883
+roomnumber: 666
+apacheSamType: 7
+safehausUid: akarasulu
+safehausRealm: EXAMPLE.COM
+safehausLabel: example realm
+safehausFactor: 27304238
+safehausSecret:: aaaabbbbccccdddd
+safehausFailuresInEpoch: 0
+safehausResynchCount: -1
+safehausInfo: test account
+safehausTokenPin: 1234
+safehausNotifyBy: sms
+userpassword: maxwell
+
+dn: uid=lockedout, ou=Users, dc=example,dc=com
+cn: Risky
+sn: Lockedout
+givenname: Unlucky
+objectclass: top
+objectclass: uidObject
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+objectclass: safehausProfile
+ou: Directory
+ou: Users
+l: DummyCity
+uid: lockedout
+krb5PrincipalName: lockedout@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: lockedout@example.com
+telephonenumber: +1 904 982 6882
+facsimiletelephonenumber: +1 904 982 6883
+roomnumber: 699
+safehausUid: lockedout
+safehausRealm: EXAMPLE.COM
+safehausLabel: example realm
+safehausFactor: 101347012
+safehausSecret:: (Q-H23BQ#SDsdkf3o&81923r
+safehausFailuresInEpoch: 20
+safehausResynchCount: -1
+safehausInfo: unlucky account
+safehausTokenPin: 1234
+safehausNotifyBy: sms
+userpassword: asdfasdf
+
+dn: uid=erodriguez, ou=Users, dc=example,dc=com
+cn: Enrique Rodriguez
+sn: Rodriguez
+givenname: Enrique
+objectclass: top
+objectclass: uidObject
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+objectclass: safehausProfile
+ou: Directory
+ou: Users
+l: Boston
+uid: erodriguez
+krb5PrincipalName: erodriguez@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+safehausUid: erodriguez
+safehausRealm: EXAMPLE.COM
+safehausLabel: example realm
+safehausFactor: 917483720127847
+safehausSecret:: xcJqp45S80e8fahs&@rq1I98awg8)^*
+safehausFailuresInEpoch: 0
+safehausResynchCount: -1
+safehausInfo: test account
+safehausTokenPin: 1234
+safehausNotifyBy: sms
+userpassword: noices
+
+dn: uid=krbtgt, ou=Users, dc=example,dc=com
+cn: Kerberos Server
+sn: Server
+givenname: Kerberos
+objectclass: top
+objectclass: uidObject
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: Users
+l: Boston
+uid: krbtgt
+krb5PrincipalName: krbtgt/EXAMPLE.COM@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: kahuna
+
+dn: uid=hostssh, ou=Users, dc=example,dc=com
+cn: SSH Service
+sn: Service
+givenname: SSH
+objectclass: top
+objectclass: uidObject
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: Users
+l: Boston
+uid: hostssh
+krb5PrincipalName: host/www.example.com@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: randall
+
+dn: uid=hostssh2, ou=Users, dc=example,dc=com
+cn: SSH Service
+sn: Service
+givenname: SSH
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+objectclass: krb5Principal
+objectclass: krb5KDCEntry
+ou: Directory
+ou: Users
+l: Boston
+uid: hostssh
+krb5PrincipalName: host/kerberos.example.com@EXAMPLE.COM
+krb5KeyVersionNumber: 0
+mail: erodriguez@example.com
+telephonenumber: +1 408 555 9187
+facsimiletelephonenumber: +1 408 555 8473
+roomnumber: 667
+userpassword: randall
+
Added: directory/trunks/triplesec/webapp-activation/pom.xml
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/pom.xml?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/pom.xml (added)
+++ directory/trunks/triplesec/webapp-activation/pom.xml Tue Dec 12 07:23:31 2006
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project>
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.safehaus.triplesec</groupId>
+ <artifactId>build</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>triplesec-webapp-activation</artifactId>
+ <name>Triplesec Webapp for Activation of Accounts</name>
+ <packaging>war</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>triplesec-utils-hauskeys</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>triplesec-otp</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>triplesec-sms</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>triplesec-configuration</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.activation</groupId>
+ <artifactId>activation</artifactId>
+ <version>1.1</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ <version>1.4</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>ant</groupId>
+ <artifactId>ant</artifactId>
+ <version>1.6.5</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.0</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-httpclient</groupId>
+ <artifactId>commons-httpclient</artifactId>
+ <version>2.0.2</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>tomcat</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>5.5.12</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>nlog4j</artifactId>
+ <version>1.2.25</version>
+ <scope>provided</scope>
+ </dependency>
+
+ </dependencies>
+ <build>
+ <finalName>triplesec-webapp-activation</finalName>
+ </build>
+</project>
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivateAccountFilter.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivateAccountFilter.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivateAccountFilter.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivateAccountFilter.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,628 @@
+/*
+ * 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.activation;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.LdapContext;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.safehaus.otp.HotpAttributes;
+import org.safehaus.otp.HotpAttributesCipher;
+import org.safehaus.triplesec.configuration.ActivationConfiguration;
+import org.safehaus.triplesec.configuration.SmsConfiguration;
+import org.safehaus.triplesec.configuration.SmtpConfiguration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * The account activation filter activates user accounts.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class ActivateAccountFilter implements Filter, Runnable
+{
+ private static final int MIN_NOTIFICATION_AGE = 15000;
+ private static final String SMTP_PASSWORD_PARAM = "smtpPassword";
+ private static final String SMTP_USERNAME_PARAM = "smtpUsername";
+ private static final String SMTP_SUBJECT_PARAM = "smtpSubject";
+ private static final String SMTP_FROM_PARAM = "smtpFrom";
+ private static final String SMTP_HOST_PARAM = "smtpHost";
+ private static final String SMS_TRANSPORT_URL_PARAM = "smsTransportUrl";
+ private static final String SMS_PASSWORD_PARAM = "smsPassword";
+ private static final String SMS_USERNAME_PARAM = "smsUsername";
+ private static final String SMS_ACCOUNT_PARAM = "smsAccountName";
+
+ private static final String MIDLET_NAME_ATTRIBUTE_PARAM = "midletNameAttribute";
+ private static final String OTP_LENGTH_PARAM = "otpLength";
+ private static final String ENABLE_DECOY_MIDLET_PARAM = "enableDecoyMidlet";
+ private static final String PRESENTATION_BASE_URL_PARAM = "presentationBaseUrl";
+
+ private static final Logger log = LoggerFactory.getLogger( ActivateAccountFilter.class );
+ private static final String DEFAULT_MIDLETNAME = "HausKeys";
+
+ private LdapContext ctx;
+ private File baseDir;
+ private SmsConfiguration smsConfig = null;
+ private SmtpConfiguration smtpConfig = null;
+ private ActivationConfiguration actConfig = null;
+ private NotificationQueue msgQueue = new NotificationQueue();
+ private Thread notifierThread;
+ private boolean keepRunning = true;
+
+
+ // -----------------------------------------------------------------------
+ // Filter Initialization Methods
+ // -----------------------------------------------------------------------
+
+
+ /**
+ * Initializes the LDAP context using security settings from web.xml.
+ *
+ * @param config the filter's configuration
+ */
+ private void initLdapContext( FilterConfig config ) throws ServletException
+ {
+ String ldapHost = config.getInitParameter( "ldapHost" );
+ String ldapPrincipalDn = config.getInitParameter( "ldapPrincipalDn" );
+ String ldapCredentials = config.getInitParameter( "ldapCredentials" );
+ String ldapBaseDn = config.getInitParameter( "ldapBaseDn" );
+ int ldapPort = Integer.parseInt( config.getInitParameter( "ldapPort" ) );
+
+ Hashtable env = new Hashtable();
+ env.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" );
+
+ // calculate the provider url from components
+ StringBuffer buf = new StringBuffer();
+ buf.append( "ldap://" ).append( ldapHost ).append( ":" ).append( ldapPort );
+ buf.append( "/" ).append( ldapBaseDn );
+ env.put( Context.PROVIDER_URL, buf.toString() );
+
+ env.put( Context.SECURITY_AUTHENTICATION, "simple" );
+ env.put( Context.SECURITY_CREDENTIALS, ldapCredentials );
+ env.put( Context.SECURITY_PRINCIPAL, ldapPrincipalDn );
+
+ try
+ {
+ ctx = ( LdapContext ) new InitialLdapContext( env, null ).lookup( "" );
+ }
+ catch ( NamingException e )
+ {
+ log.error( "Failed to initialize DirContext for LDAP service.", e );
+ }
+ }
+
+
+ /**
+ * Intializes the SMS settings from web.xml needed to send messages.
+ *
+ * @param config the filter's configuration
+ */
+ private void initSmsConfiguration( FilterConfig config )
+ {
+ smsConfig = new SmsConfiguration();
+ smsConfig.setSmsAccountName( config.getInitParameter( SMS_ACCOUNT_PARAM ) );
+ smsConfig.setSmsUsername( config.getInitParameter( SMS_USERNAME_PARAM ) );
+ smsConfig.setSmsPassword( config.getInitParameter( SMS_PASSWORD_PARAM ) );
+ smsConfig.setSmsTransportUrl( config.getInitParameter( SMS_TRANSPORT_URL_PARAM ) );
+ }
+
+
+ /**
+ * Initializes the mail server settings from web.xml to send messages.
+ *
+ * @param config the filter's configuration
+ */
+ private void initSmtpConfiguration( FilterConfig config )
+ {
+ smtpConfig = new SmtpConfiguration();
+ smtpConfig.setSmtpHost( config.getInitParameter( SMTP_HOST_PARAM ) );
+ smtpConfig.setSmtpFrom( config.getInitParameter( SMTP_FROM_PARAM ) );
+ smtpConfig.setSmtpSubject( config.getInitParameter( SMTP_SUBJECT_PARAM ) );
+ if ( config.getInitParameter( SMTP_USERNAME_PARAM ) == null )
+ {
+ smtpConfig.setSmtpAuthenticate( false );
+ }
+ else
+ {
+ smtpConfig.setSmtpAuthenticate( true );
+ smtpConfig.setSmtpUsername( config.getInitParameter( SMTP_USERNAME_PARAM ) );
+ smtpConfig.setSmtpPassword( config.getInitParameter( SMTP_PASSWORD_PARAM ) );
+ }
+ }
+
+
+ /**
+ * Initializes activation behavior settings from the web.xml to send messages.
+ *
+ * @param config the filter's configuration
+ */
+ private void initActivationConfiguration( FilterConfig config )
+ {
+ actConfig = new ActivationConfiguration();
+
+ String presentationBaseUrl = config.getInitParameter( PRESENTATION_BASE_URL_PARAM );
+ if ( presentationBaseUrl != null && ! presentationBaseUrl.equals( "undefined" ) )
+ {
+ actConfig.setActivationBaseUrl( presentationBaseUrl + "/activation" );
+ }
+
+ if ( config.getInitParameter( ENABLE_DECOY_MIDLET_PARAM ) != null )
+ {
+ String val = config.getInitParameter( ENABLE_DECOY_MIDLET_PARAM );
+ actConfig.setEnableDecoyMidlet( val.equals( "1" ) || val.equalsIgnoreCase( "true" ) );
+ }
+
+ if ( config.getInitParameter( OTP_LENGTH_PARAM ) != null )
+ {
+ actConfig.setOtpLength( Integer.parseInt( config.getInitParameter( OTP_LENGTH_PARAM ) ) );
+ }
+
+ if ( config.getInitParameter( MIDLET_NAME_ATTRIBUTE_PARAM ) != null )
+ {
+ actConfig.setMidletNameAttribute( config.getInitParameter( MIDLET_NAME_ATTRIBUTE_PARAM ) );
+ }
+ }
+
+
+ /**
+ * Filter's main init method called by the servlet container.
+ *
+ * @param config the filter's configuration
+ */
+ public void init( FilterConfig config ) throws ServletException
+ {
+ initLdapContext( config );
+ initSmsConfiguration( config );
+ initSmtpConfiguration( config );
+ initActivationConfiguration( config );
+
+ try
+ {
+ baseDir = new File( config.getServletContext().getRealPath( "" ) ).getCanonicalFile();
+ }
+ catch ( IOException e )
+ {
+ log.error( "Could not get cannonical file for base path", e );
+ throw new ServletException( e );
+ }
+
+ notifierThread = new Thread( this, "notifier" );
+ notifierThread.start();
+ }
+
+
+ // -----------------------------------------------------------------------
+ // Main Filter Processing Method and Helpers
+ // -----------------------------------------------------------------------
+
+
+ /**
+ * The following sequence of events are handed by this filter:
+ * <ol>
+ * <li>Client receives SMS and follows link to a URL for Jad or Jar under this filter.</li>
+ * <li>Servlet filter constructs Jad or midlet Jar for client request.</li>
+ * <li>
+ * If a Jar was requested an SMS message is sent to the client with an a message
+ * to activate the account and an activation URL. The activation URL is the same
+ * as the first URL except the activation code is prefixed with an 'a' for activate.
+ * </li>
+ * <li>Client receives 2nd activation SMS and navigates to the embedded activation URL.</li>
+ * <li>Filter then destroys the activation directory and activates the account.</li>
+ * <li>A third final SMS is sent to notify the client that the account has been activated.</li>
+ * </ol>
+ *
+ */
+ public void doFilter( ServletRequest sreq, ServletResponse sresp, FilterChain chain )
+ throws IOException, ServletException
+ {
+ HttpServletRequest req = ( HttpServletRequest ) sreq;
+ HttpServletResponse resp = ( HttpServletResponse ) sresp;
+
+ String activationKey = ActivationUtils.getActivationKey( req.getRequestURI() );
+
+ if ( req.getRequestURI().endsWith( "activate.txt" ) ||
+ req.getRequestURI().endsWith( "activate.wml" ) ||
+ req.getRequestURI().endsWith( "activate.html" ) )
+ {
+ processActivation( activationKey, req, resp, chain );
+ }
+ else
+ {
+ processDownload( activationKey, req, resp, chain );
+ }
+ }
+
+
+ private void sendResponse( String msg, HttpServletRequest req, HttpServletResponse resp )
+ throws IOException, ServletException
+ {
+ String contentType = null;
+ StringBuffer content = new StringBuffer();
+
+ if ( req.getRequestURI().endsWith( ".txt" ) )
+ {
+ contentType = "text/plain";
+ content.append( msg );
+ }
+ else if ( req.getRequestURI().endsWith( ".html" ) )
+ {
+ contentType = "text/html";
+ content.append( "<html><body><p>" ).append( msg ).append( "</p></body></html>" );
+ }
+ else if ( req.getRequestURI().endsWith( ".wml" ) )
+ {
+ contentType = "text/vnd.wap.wml";
+ content.append( "<wml> <card id=\"main\" title=\"Safehaus\"><p align=\"left\">" );
+ content.append( msg ).append( "</p></card></wml>" );
+ }
+ else
+ {
+ String errmsg = "Unrecognized file extension in request URI: " + req.getRequestURI();
+ log.error( errmsg );
+ throw new IllegalStateException( errmsg );
+ }
+
+ resp.setContentType( contentType );
+ resp.getWriter().write( content.toString() );
+ resp.getWriter().flush();
+ resp.getWriter().close();
+ return;
+ }
+
+
+ private void processActivation( String activationKey, HttpServletRequest req, HttpServletResponse resp, FilterChain chain )
+ throws IOException, ServletException
+ {
+ // -------------------------------------------------------------------
+ // Lookup the account associated with this activation key
+ // -------------------------------------------------------------------
+
+ HotpAccount account = null;
+ try
+ {
+ account = ActivationUtils.getAccountHotpAccount( ( LdapContext ) ctx.lookup( "ou=users" ), activationKey );
+ }
+ catch ( NamingException e )
+ {
+ String message = "Failed to access safehaus account information for activation key: " + activationKey;
+ log.error( message, e );
+ sendResponse( message, req, resp );
+ return;
+ }
+
+ // -------------------------------------------------------------------
+ // Delete the safehausActivationKey attribute for the user to activate the account
+ // -------------------------------------------------------------------
+
+ BasicAttributes mods = new BasicAttributes( "safehausActivationKey", null, true );
+ try
+ {
+ ctx.modifyAttributes( account.getName() + ",ou=users", DirContext.REMOVE_ATTRIBUTE, mods );
+ }
+ catch ( NamingException e )
+ {
+ String errmsg = "Failed to activate account for " + account.getName();
+ log.error( errmsg, e );
+ sendResponse( errmsg, req, resp );
+ return;
+ }
+
+ // -------------------------------------------------------------------
+ // Delete the directory with jar that was generated for this key
+ // -------------------------------------------------------------------
+
+ File activationDirectory = new File( baseDir, activationKey );
+ ActivationUtils.deleteActivationDirectory( activationDirectory );
+
+ // -------------------------------------------------------------------
+ // Send informational message back confirming activation using
+ // account's notification medium: sms or email.
+ // -------------------------------------------------------------------
+
+ Notification msg = null;
+ if ( account.isDeliveryMethodSms() )
+ {
+ msg = new SmsNotification( account.getCellularNumber(), account.getCarrier(), activationKey,
+ "Your account for realm " + account.getRealm() + " has been activated!" );
+ try
+ {
+ NotificationUtil.send( account.getRealm(), msg, smsConfig );
+ }
+ catch ( Exception e )
+ {
+ log.error( "Failed to send sms message: " + msg, e );
+ }
+ }
+ else
+ {
+ msg = new EmailNotification( account.getEmailAddress(), activationKey,
+ "Your account for realm " + account.getRealm() + " has been activated!" );
+ try
+ {
+ NotificationUtil.send( account.getRealm(), msg, smtpConfig );
+ }
+ catch ( Exception e )
+ {
+ log.error( "Failed to send email message: " + msg, e );
+ }
+ }
+
+ // -------------------------------------------------------------------
+ // Return a text response back as well (should work for all clients)
+ // -------------------------------------------------------------------
+
+ sendResponse( "Your account for realm " + account.getRealm() + " has been activated!", req, resp );
+ return;
+ }
+
+
+ private void processDownload( String activationKey, HttpServletRequest req,
+ HttpServletResponse resp, FilterChain chain ) throws IOException, ServletException
+ {
+ if ( req.getRequestURI().indexOf( ".jar" ) == -1 )
+ {
+ String message = "No such resource associated with the activation key: " + activationKey;
+ log.info( message );
+ resp.setContentType( "text/plain" );
+ resp.getWriter().write( message );
+ resp.getWriter().close();
+ return;
+ }
+
+ // -------------------------------------------------------------------
+ // Lookup the account associated with this activation key
+ // -------------------------------------------------------------------
+
+ HotpAccount account = null;
+ try
+ {
+ account = ActivationUtils.getAccountHotpAccount( ( LdapContext ) ctx.lookup( "ou=users" ), activationKey );
+ }
+ catch ( NamingException e )
+ {
+ String message = "Failed to access safehaus account information for activation key: " + activationKey;
+ log.error( message, e );
+ resp.setContentType( "text/plain" );
+ resp.getWriter().write( message );
+ resp.getWriter().close();
+ return;
+ }
+
+ // -------------------------------------------------------------------
+ // If the activation directory exists that means the jar is there
+ // already and this is not the 1st request so far for the jar file
+ // so we want to expedite the delivery of the ActivationMessage for
+ // this activation.
+ // -------------------------------------------------------------------
+
+ File activationDirectory = new File( baseDir, activationKey );
+ if ( activationDirectory.exists() )
+ {
+ chain.doFilter( req, resp );
+
+ // Dequeue the message if it has not been delivered already and send
+ // instead of waiting for the message to be delivered after the delay
+ Notification msg = null;
+ synchronized ( msgQueue )
+ {
+ msg = msgQueue.dequeue( activationKey );
+ }
+ if ( msg != null )
+ {
+ try
+ {
+ NotificationUtil.send( account.getRealm(), msg, smsConfig );
+ }
+ catch ( Exception e )
+ {
+ log.error( "Failed to deliver message: " + msg, e );
+ }
+ }
+
+ return;
+ }
+
+ // -------------------------------------------------------------------
+ // First jar request builds the midlet
+ // -------------------------------------------------------------------
+
+ ActivationUtils.createActivationDirectory( activationDirectory );
+ String hotpInfo = null;
+ String midletName = DEFAULT_MIDLETNAME;
+ if ( ! actConfig.isEnableDecoyMidlet() && account == null )
+ {
+ String message = "No such safehaus account with activation key: " + activationKey;
+ log.info( message );
+ resp.setContentType( "text/plain" );
+ resp.getWriter().write( message );
+ resp.getWriter().close();
+ return;
+ }
+
+ if ( account == null )
+ {
+ hotpInfo = ActivationUtils.getBogusHotpInfo();
+ }
+ else
+ {
+ HotpAttributes hotpAttrs = new HotpAttributes( actConfig.getOtpLength(),
+ account.getFactor(), account.getSecret() );
+ hotpInfo = HotpAttributesCipher.encrypt( account.getPin(), hotpAttrs );
+ }
+
+ midletName = account.getMidletName();
+ File appSrc = new File( baseDir, "HausKeys.jar" );
+ File appDest = new File( activationDirectory, "HausKeys.jar" );
+ try
+ {
+ ActivationUtils.buildMidlet( midletName, hotpInfo, appSrc, appDest );
+ }
+ catch ( Exception e )
+ {
+ String message = "Failed to create midlet jar for activation key " + activationKey;
+ log.error( message, e );
+ resp.setContentType( "text/plain" );
+ resp.getWriter().write( message );
+ resp.getWriter().close();
+ ActivationUtils.deleteActivationDirectory( activationDirectory );
+ return;
+ }
+
+ chain.doFilter( req, resp );
+
+ Notification msg = null;
+ StringBuffer content = new StringBuffer();
+ if ( actConfig.getActivationBaseUrl() == null || actConfig.getActivationBaseUrl().equals( "undefined" ) )
+ {
+ content.append( "To activate your new account navigate to the following URL: http://" );
+ content.append( req.getServerName() ).append( ":" ).append( req.getServerPort() );
+ content.append( "/activation/" ).append( activationKey ).append( "/activate" );
+ }
+ else
+ {
+ content.append( "To activate your new account navigate to the following URL: " );
+ content.append( actConfig.getActivationBaseUrl() );
+ content.append( "/" ).append( activationKey ).append( "/activate" );
+ }
+
+ if ( account.isDeliveryMethodSms() )
+ {
+ content.append( ".wml" );
+ msg = new SmsNotification( account.getCellularNumber(), account.getCarrier(),
+ activationKey, content.toString() );
+ }
+ else
+ {
+ content.append( ".html" );
+ msg = new EmailNotification( account.getEmailAddress(), activationKey, content.toString() );
+ }
+
+ synchronized ( msgQueue )
+ {
+ // enqueue the activation request message for the account
+ msgQueue.enqueue( msg );
+ msgQueue.notifyAll();
+ }
+ }
+
+
+ public void destroy()
+ {
+ keepRunning = false;
+
+ while ( notifierThread.isAlive() )
+ {
+ synchronized ( msgQueue )
+ {
+ msgQueue.notifyAll();
+ }
+
+ try
+ {
+ notifierThread.join( 250 );
+ }
+ catch ( InterruptedException e )
+ {
+ log.error( "encountered failure while waiting for notifier thread to die", e );
+ }
+ }
+ }
+
+
+ public void run()
+ {
+ while ( keepRunning )
+ {
+ synchronized ( msgQueue )
+ {
+ if ( msgQueue.available( MIN_NOTIFICATION_AGE ) )
+ {
+ Notification[] msgs = msgQueue.dequeue( MIN_NOTIFICATION_AGE );
+ for ( int ii = 0; ii < msgs.length; ii++ )
+ {
+ if ( msgs[ii] instanceof SmsNotification )
+ {
+ try
+ {
+ NotificationUtil.send( "unknown", msgs[ii], smsConfig );
+ }
+ catch ( Exception e )
+ {
+ log.error( "Failed to send sms notification: " + msgs[ii], e );
+ }
+ }
+ else
+ {
+ try
+ {
+ NotificationUtil.send( "unknown", msgs[ii], smtpConfig );
+ }
+ catch ( Exception e )
+ {
+ log.error( "Failed to send email notification: " + msgs[ii], e );
+ }
+ }
+ }
+ }
+
+ long waitMillis = msgQueue.getWaitMillis();
+ try
+ {
+ if ( waitMillis == Long.MAX_VALUE )
+ {
+ msgQueue.wait();
+ }
+ else if ( waitMillis == 0 )
+ {
+ continue;
+ }
+ else
+ {
+ msgQueue.wait( waitMillis );
+ }
+ }
+ catch ( InterruptedException e )
+ {
+ log.error( "got interrupted exception while attempting to wait for "
+ + waitMillis + " milliseconds", e );
+ }
+ }
+ }
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivationUtils.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivationUtils.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivationUtils.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ActivationUtils.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,182 @@
+/*
+ * 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.activation;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.SearchResult;
+
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.commons.lang.math.RandomUtils;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Delete;
+import org.apache.tools.ant.taskdefs.ManifestException;
+import org.apache.tools.ant.taskdefs.Mkdir;
+
+import org.safehaus.otp.HotpAttributes;
+import org.safehaus.otp.HotpAttributesCipher;
+import org.safehaus.triplesec.utils.hauskeys.HauskeysMidletBuilder;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Utility functions for activating an account.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class ActivationUtils
+{
+ /** logger for this class */
+ private static final Logger log = LoggerFactory.getLogger( ActivationUtils.class );
+ /** index of the act key when spliting the request URI */
+ private static final int ACTKEY_INDEX = 2;
+
+
+ /**
+ * Extracts the activation key from a request's URI string.
+ *
+ * @param requestURI the request's URI string
+ * @return the safehaus activation key
+ * @throws IllegalArgumentException if the request URI is null or does not contain "/"
+ */
+ public static String getActivationKey( String requestURI )
+ {
+ if ( requestURI == null || requestURI.indexOf( "/" ) == -1 || !requestURI.startsWith( "/" ) )
+ {
+ throw new IllegalArgumentException( "Not a valid URI string: " + requestURI );
+ }
+
+ String[] comps = requestURI.split( "/" );
+ return comps[ACTKEY_INDEX];
+ }
+
+
+ public static File createActivationDirectory( File activationDirectory )
+ {
+ Project project = new Project();
+
+ // if old directory exists blow it away
+ if ( activationDirectory.exists() )
+ {
+ Delete delete = new Delete();
+ delete.setProject( project );
+ delete.setDir( activationDirectory );
+ delete.execute();
+ }
+
+ Mkdir mkdir = new Mkdir();
+ mkdir.setProject( project );
+ mkdir.setDir( activationDirectory );
+ mkdir.execute();
+ return activationDirectory;
+ }
+
+
+ public static void deleteActivationDirectory( File activationDirectory )
+ {
+ if ( activationDirectory == null )
+ {
+ throw new IllegalArgumentException( "activationDirectory cannot be null" );
+ }
+
+ Project project = new Project();
+ Delete delete = new Delete();
+ delete.setProject( project );
+ delete.setDir( activationDirectory );
+ delete.execute();
+ }
+
+
+ public static HotpAccount getAccountHotpAccount( DirContext ctx, String activationKey ) throws NamingException
+ {
+ NamingEnumeration list = null;
+
+ try
+ {
+ list = ctx.search( "", new BasicAttributes( "safehausActivationKey", activationKey, true ) );
+ if ( list.hasMore() )
+ {
+ SearchResult result = ( SearchResult ) list.next();
+ Attributes entry = result.getAttributes();
+ HotpAccountModifier modifier = new HotpAccountModifier();
+ modifier.setCarrier( ( String ) entry.get( "safehausMobileCarrier" ).get() );
+ modifier.setCellularNumber( ( String ) entry.get( "mobile" ).get() );
+ modifier.setDeliveryMethodSms( ( String ) entry.get( "safehausNotifyBy" ).get() );
+ modifier.setEmailAddress( ( String ) entry.get( "mail" ).get() );
+ modifier.setFactor( Long.parseLong( ( String ) entry.get( "safehausFactor" ).get() ) );
+ modifier.setPin( ( String ) entry.get( "safehausTokenPin" ).get() );
+ modifier.setSecret( entry.get( "safehausSecret" ).get() );
+ modifier.setMidletName( ( String ) entry.get( "safehausMidletName" ).get() );
+ modifier.setName( result.getName() );
+ modifier.setRealm( ( String ) entry.get( "safehausRealm" ).get() );
+ return modifier.create();
+ }
+ }
+ finally
+ {
+ if ( list != null ) { try { list.close(); } catch ( Exception e ){ log.error( "can't close enum" ); } };
+ }
+
+ return null;
+ }
+
+
+ public static String getBogusHotpInfo()
+ {
+ // max length 16, min length 8 bytes
+ int length = 8 + RandomUtils.nextInt() % 8;
+ byte[] secret = RandomStringUtils.random( length ).getBytes();
+ HotpAttributes hotpAttrs = new HotpAttributes( 6, RandomUtils.nextLong(), secret );
+ try
+ {
+ return HotpAttributesCipher.encrypt( RandomStringUtils.randomAlphanumeric(4), hotpAttrs );
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ log.error( "could not make hotp info", e );
+ return null;
+ }
+ }
+
+
+ public static void buildMidlet( String midletName, String hotpInfo, File appSrc, File appDest )
+ throws IOException, ManifestException
+ {
+ File tmp = new File( System.getProperty( "java.io.tmpdir" ) );
+ HauskeysMidletBuilder builder = new HauskeysMidletBuilder();
+ builder.setTmpDirectory( tmp );
+ builder.setHauskeysSrcFile( appSrc );
+ builder.setHauskeysDstFile( appDest );
+ builder.setHotpInfo( hotpInfo );
+ builder.setMidletName( midletName );
+ builder.build();
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/EmailNotification.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/EmailNotification.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/EmailNotification.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/EmailNotification.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,45 @@
+/*
+ * 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.activation;
+
+
+/**
+ * Email based notification message.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class EmailNotification extends Notification
+{
+ private final String emailAddress;
+
+
+ protected EmailNotification( String emailAddress, String activationKey, String message )
+ {
+ super( activationKey, message );
+ this.emailAddress = emailAddress;
+ }
+
+
+ public String getEmailAddress()
+ {
+ return emailAddress;
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccount.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccount.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccount.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccount.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,121 @@
+/*
+ * 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.activation;
+
+
+import org.safehaus.sms.Carrier;
+
+/**
+ * Bean representing HOTP centric account information.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class HotpAccount
+{
+ private final Carrier carrier;
+ private final String cellularNumber;
+ private final String emailAddress;
+ private final String midletName;
+ private final boolean deliveryMethodSms;
+ private final String realm;
+ private final String name;
+ private final byte[] secret;
+ private final String pin;
+ private final long factor;
+
+
+ public HotpAccount( Carrier carrier, String cellularNumber, String emailAddress, String midletName,
+ boolean deliveryMethodSms, String realm, String name, byte[] secret, String pin, long factor )
+ {
+ this.carrier = carrier;
+ this.cellularNumber = cellularNumber;
+ this.emailAddress = emailAddress;
+ this.midletName = midletName;
+ this.deliveryMethodSms = deliveryMethodSms;
+ this.realm = realm;
+ this.name = name;
+ this.secret = secret;
+ this.pin = pin;
+ this.factor = factor;
+ }
+
+
+ public Carrier getCarrier()
+ {
+ return carrier;
+ }
+
+
+ public String getCellularNumber()
+ {
+ return cellularNumber;
+ }
+
+
+ public String getEmailAddress()
+ {
+ return emailAddress;
+ }
+
+
+ public String getMidletName()
+ {
+ return midletName;
+ }
+
+
+ public boolean isDeliveryMethodSms()
+ {
+ return deliveryMethodSms;
+ }
+
+
+ public String getRealm()
+ {
+ return realm;
+ }
+
+
+ public String getName()
+ {
+ return name;
+ }
+
+
+ public byte[] getSecret()
+ {
+ byte[] copy = new byte[secret.length];
+ System.arraycopy( secret, 0, copy, 0, secret.length );
+ return copy;
+ }
+
+
+ public String getPin()
+ {
+ return pin;
+ }
+
+
+ public long getFactor()
+ {
+ return factor;
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccountModifier.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccountModifier.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccountModifier.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/HotpAccountModifier.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,238 @@
+/*
+ * 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.activation;
+
+
+import java.io.UnsupportedEncodingException;
+
+import org.safehaus.sms.Carrier;
+
+
+/**
+ * A modifier for a HotpAccount.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class HotpAccountModifier
+{
+ private Carrier carrier;
+ private String cellularNumber;
+ private String emailAddress;
+ private String midletName;
+ private boolean deliveryMethodSms;
+ private String realm;
+ private String name;
+ private byte[] secret;
+ private String pin;
+ private long factor;
+
+
+ public void setCarrier( String carrierName )
+ {
+ if ( carrierName.equalsIgnoreCase( Carrier.ATT.getName() ) )
+ {
+ carrier = Carrier.ATT;
+ }
+ else if ( carrierName.equalsIgnoreCase( Carrier.CINGULAR.getName() ) )
+ {
+ carrier = Carrier.CINGULAR;
+ }
+ else if ( carrierName.equalsIgnoreCase( Carrier.NEXTEL.getName() ) )
+ {
+ carrier = Carrier.NEXTEL;
+ }
+ else if ( carrierName.equalsIgnoreCase( Carrier.SPRINT.getName() ) )
+ {
+ carrier = Carrier.SPRINT;
+ }
+ else if ( carrierName.equalsIgnoreCase( Carrier.T_MOBILE.getName() ) )
+ {
+ carrier = Carrier.T_MOBILE;
+ }
+ else if ( carrierName.equalsIgnoreCase( Carrier.VERIZON.getName() ) )
+ {
+ carrier = Carrier.VERIZON;
+ }
+ else
+ {
+ throw new IllegalArgumentException( "Unrecognized carrier name: " + carrierName );
+ }
+ }
+
+
+ public Carrier getCarrier()
+ {
+ return carrier;
+ }
+
+
+ public void setCellularNumber( String cellularNumber )
+ {
+ this.cellularNumber = cellularNumber;
+ }
+
+
+ public String getCellularNumber()
+ {
+ return cellularNumber;
+ }
+
+
+ public void setEmailAddress( String emailAddress )
+ {
+ this.emailAddress = emailAddress;
+ }
+
+
+ public String getEmailAddress()
+ {
+ return emailAddress;
+ }
+
+
+ public void setMidletName( String midletName )
+ {
+ this.midletName = midletName;
+ }
+
+
+ public String getMidletName()
+ {
+ return midletName;
+ }
+
+
+ public void setDeliveryMethodSms( boolean deliveryMethodSms )
+ {
+ this.deliveryMethodSms = deliveryMethodSms;
+ }
+
+
+ public void setDeliveryMethodSms( String deliveryMethodSmsName )
+ {
+ if ( deliveryMethodSmsName.equalsIgnoreCase( "sms" ) )
+ {
+ this.deliveryMethodSms = true;
+ }
+ else
+ {
+ this.deliveryMethodSms = false;
+ }
+ }
+
+
+ public boolean isDeliveryMethodSms()
+ {
+ return deliveryMethodSms;
+ }
+
+
+ public void setRealm( String realm )
+ {
+ this.realm = realm;
+ }
+
+
+ public String getRealm()
+ {
+ return realm;
+ }
+
+
+ public void setName( String name )
+ {
+ this.name = name;
+ }
+
+
+ public String getName()
+ {
+ return name;
+ }
+
+
+ public void setSecret( Object secret )
+ {
+ if ( secret instanceof byte[] )
+ {
+ this.secret = ( byte[] ) secret;
+ }
+ else if ( secret instanceof String )
+ {
+ try
+ {
+ this.secret = ( ( String ) secret ).getBytes( "UTF-8" );
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ e.printStackTrace();
+ }
+ }
+ else
+ {
+ try
+ {
+ this.secret = String.valueOf( secret ).getBytes( "UTF-8" );
+ }
+ catch ( UnsupportedEncodingException e )
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ public byte[] getSecret()
+ {
+ return secret;
+ }
+
+
+ public void setPin( String pin )
+ {
+ this.pin = pin;
+ }
+
+
+ public String getPin()
+ {
+ return pin;
+ }
+
+
+ public void setFactor( long factor )
+ {
+ this.factor = factor;
+ }
+
+
+ public long getFactor()
+ {
+ return factor;
+ }
+
+
+ public HotpAccount create()
+ {
+ return new HotpAccount( carrier, cellularNumber, emailAddress, midletName,
+ deliveryMethodSms, realm, name, secret, pin, factor );
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/Notification.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/Notification.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/Notification.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/Notification.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,83 @@
+/*
+ * 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.activation;
+
+
+import java.util.Date;
+
+
+/**
+ * A message to be delivered to the account owner requesting the activation of
+ * their account. ActivationMessages are delivered to the owner after some
+ * delay, hence the need to encapsulate the message and enqueue it for future
+ * delivery. An ActivationMessage generally contains the URL used to activate
+ * the account.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public abstract class Notification
+{
+ private final long timestamp;
+ private final String message;
+ private final String activationKey;
+
+
+ protected Notification( String activationKey, String message )
+ {
+ this.timestamp = System.currentTimeMillis();
+ this.message = message;
+ this.activationKey = activationKey;
+ }
+
+
+ public long getTimestamp()
+ {
+ return timestamp;
+ }
+
+
+ public String getMessage()
+ {
+ return message;
+ }
+
+
+ public String getActivationKey()
+ {
+ return activationKey;
+ }
+
+
+ public int hashCode()
+ {
+ return activationKey.hashCode();
+ }
+
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( "\t timestamp = " ).append( new Date( timestamp ) ).append( "\n" );
+ buf.append( "\t message = " ).append( message ).append( "\n" );
+ buf.append( "\tactivationKey = " ).append( activationKey ).append( "\n" );
+ return buf.toString();
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationQueue.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationQueue.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationQueue.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationQueue.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,143 @@
+/*
+ * 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.activation;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * An activation message queue which allows the dequeue of messages based on age
+ * or based on an activationKey. Not synchronized.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class NotificationQueue
+{
+ LinkedList msgList = new LinkedList();
+ Map msgMap = new HashMap();
+
+
+ public void enqueue( Notification message )
+ {
+ msgList.addFirst( message );
+ msgMap.put( message.getActivationKey(), message );
+ }
+
+
+ /**
+ * Dequeues an array of ActivationMessages from the message queue based on age.
+ *
+ * @param ageMillis remove all messages that have been in the queue for this
+ * time or more in milliseconds
+ * @return an array of messages of this age with the oldest first
+ */
+ public Notification[] dequeue( long ageMillis )
+ {
+ List ready = null;
+
+ if ( msgList.size() == 0 )
+ {
+ return new Notification[0];
+ }
+
+ ready = new ArrayList();
+ while( msgList.size() > 0 )
+ {
+ Notification msg = ( Notification ) msgList.getLast();
+ if ( System.currentTimeMillis() - msg.getTimestamp() >= ageMillis )
+ {
+ msgList.removeLast();
+ msgMap.remove( msg.getActivationKey() );
+ ready.add( msg );
+ }
+ }
+
+ Notification[] array = new Notification[ready.size()];
+ return ( Notification[] ) ready.toArray( array );
+ }
+
+
+ /**
+ * Dequeues a specific message regardless of it's age from this message queue.
+ *
+ * @param activationKey the activation key of the message to remove
+ * @return the activation message with the key or null if none exists
+ */
+ public Notification dequeue( String activationKey )
+ {
+ Notification msg = ( Notification ) msgMap.remove( activationKey );
+ if ( msg == null )
+ {
+ return null;
+ }
+ msgList.remove( msg );
+ return msg;
+ }
+
+
+ public boolean isEmpty()
+ {
+ return msgList.isEmpty();
+ }
+
+
+ public boolean available( long ageMillis )
+ {
+ if ( msgList.isEmpty() )
+ {
+ return false;
+ }
+
+ Notification msg = ( Notification ) msgList.getLast();
+ if ( msg == null )
+ {
+ return false;
+ }
+ return System.currentTimeMillis() - msg.getTimestamp() >= ageMillis;
+ }
+
+
+ public long getWaitMillis()
+ {
+ if ( msgList.isEmpty() )
+ {
+ return Long.MAX_VALUE;
+ }
+
+ Notification msg = ( Notification ) msgList.getLast();
+ if ( msg == null )
+ {
+ return Long.MAX_VALUE;
+ }
+
+ long waitMillis = 60000 - ( System.currentTimeMillis() - msg.getTimestamp() );
+ if ( waitMillis < 0 )
+ {
+ return 0;
+ }
+ return waitMillis;
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationUtil.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationUtil.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationUtil.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/NotificationUtil.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,147 @@
+/*
+ * 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.activation;
+
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.Properties;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.safehaus.triplesec.configuration.SmsConfiguration;
+import org.safehaus.triplesec.configuration.SmtpConfiguration;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.mail.smtp.SMTPTransport;
+
+
+/**
+ * Utility methods for sending notifications via sms and email.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class NotificationUtil
+{
+ // Request parameters for NMSI SMS POST
+ private static final String MSG_REQPARAM = "msg";
+ private static final String MOBILE_REQPARAM = "CellNumber";
+ private static final String ACCOUNTNAME_REQPARAM = "Campaign";
+ private static final String PASSWORD_REQPARAM = "PWD";
+ private static final String USERNAME_REQPARAM = "UID";
+ private static final String CARRIERCODE_REQPARAM = "Carrier";
+
+ /** logger for this class */
+ private static final Logger log = LoggerFactory.getLogger( ActivationUtils.class );
+
+
+ static void send( String realm, Notification msg, Object config ) throws Exception
+ {
+ if ( msg instanceof SmsNotification )
+ {
+ sendSms( realm, ( SmsNotification ) msg, ( SmsConfiguration ) config );
+ }
+ else
+ {
+ sendEmail( realm, ( EmailNotification ) msg, ( SmtpConfiguration ) config );
+ }
+ }
+
+
+ static void sendSms( String realm, SmsNotification msg, SmsConfiguration smsConfig ) throws IOException
+ {
+ HttpClient client = new HttpClient();
+ HttpMethod method = new PostMethod( smsConfig.getSmsTransportUrl() );
+ int carrierCode = msg.getCarrier().getValue();
+ String mobile = msg.getCellularNumber();
+
+ if ( mobile.startsWith( "+" ) )
+ {
+ mobile = mobile.substring( 1 );
+ }
+
+ if ( mobile.startsWith( "1" ) )
+ {
+ mobile = mobile.substring( 1 );
+ if ( mobile.length() < 10 )
+ {
+ throw new RuntimeException( "mobile phone number was not 10 digits in length" );
+ }
+ }
+
+ NameValuePair[] params = new NameValuePair[] {
+ new NameValuePair( CARRIERCODE_REQPARAM, String.valueOf( carrierCode ) ),
+ new NameValuePair( USERNAME_REQPARAM, smsConfig.getSmsUsername() ),
+ new NameValuePair( PASSWORD_REQPARAM, smsConfig.getSmsPassword() ),
+ new NameValuePair( ACCOUNTNAME_REQPARAM, smsConfig.getSmsAccountName() ),
+ new NameValuePair( MOBILE_REQPARAM, mobile ),
+ new NameValuePair( MSG_REQPARAM, msg.getMessage() )
+ };
+ method.setQueryString( params );
+ client.executeMethod( method );
+ }
+
+
+ static void sendEmail( String realm, EmailNotification msg, SmtpConfiguration smtpConfig )
+ throws MessagingException
+ {
+ Properties props = new Properties();
+ props.setProperty( "mail.smtp.host", smtpConfig.getSmtpHost() );
+ Session session = Session.getInstance( props, null );
+
+ MimeMessage mimeMsg = new MimeMessage( session );
+ if ( smtpConfig.getSmtpFrom() != null )
+ {
+ mimeMsg.setFrom( new InternetAddress( smtpConfig.getSmtpFrom() ) );
+ }
+ else
+ {
+ mimeMsg.setFrom();
+ }
+
+ mimeMsg.setRecipients( Message.RecipientType.TO, InternetAddress.parse( msg.getEmailAddress(), false ) );
+ mimeMsg.setSubject( smtpConfig.getSmtpSubject() );
+ mimeMsg.setText( msg.getMessage() );
+ mimeMsg.setHeader( "X-Mailer", "triplesec-acivation" );
+ mimeMsg.setSentDate( new Date() );
+ SMTPTransport transport = ( SMTPTransport ) session.getTransport( "smtp" );
+ if ( smtpConfig.isSmtpAuthenticate() )
+ {
+ transport.connect( smtpConfig.getSmtpHost(), smtpConfig.getSmtpUsername(), smtpConfig.getSmtpPassword() );
+ }
+ else
+ {
+ transport.connect();
+ }
+ transport.sendMessage( mimeMsg, mimeMsg.getAllRecipients() );
+ log.info( "mail server response: " + transport.getLastServerResponse() );
+ }
+}
Added: directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ServletUtils.java
URL: http://svn.apache.org/viewvc/directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ServletUtils.java?view=auto&rev=486187
==============================================================================
--- directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ServletUtils.java (added)
+++ directory/trunks/triplesec/webapp-activation/src/main/java/org/safehaus/triplesec/activation/ServletUtils.java Tue Dec 12 07:23:31 2006
@@ -0,0 +1,106 @@
+/*
+ * 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.activation;
+
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+
+/**
+ * Useful servlet utilities.
+ *
+ * @author <a href="mailto:akarasulu@safehaus.org">Alex Karasulu</a>
+ * @version $Rev$
+ */
+public class ServletUtils
+{
+ public static void printRequest( HttpServletRequest req, PrintWriter out )
+ {
+ out.println( "-[characterEncoding: " + req.getCharacterEncoding() + "]-" );
+ out.println( "-[contentLength: " + req.getContentLength() + "]-" );
+ out.println( "-[contentType: " + req.getContentType() + "]-" );
+ out.println( "-[contentPath: " + req.getContextPath() + "]-" );
+ out.println( "-[method: " + req.getMethod() + "]-" );
+ out.println( "-[pathInfo: " + req.getPathInfo() + "]-" );
+ out.println( "-[pathTranslated: " + req.getPathTranslated() + "]-" );
+ out.println( "-[protocol: " + req.getProtocol() + "]-" );
+ out.println( "-[queryString: " + req.getQueryString() + "]-" );
+ out.println( "-[remoteAddr" + req.getRemoteAddr() + "]-" );
+ out.println( "-[remoteHost: " + req.getRemoteHost() + "]-" );
+ out.println( "-[remotePort: " + req.getRemotePort() + "]-" );
+ out.println( "-[remoteUser: " + req.getRemoteUser() + "]-" );
+ out.println( "-[requestedSessionId: " + req.getRequestedSessionId() + "]-" );
+ out.println( "-[requestURI: " + req.getRequestURI() + "]-" );
+ out.println( "-[requestURL: " + req.getRequestURL() + "]-" );
+ out.println( "-[scheme: " + req.getScheme() + "]-" );
+ out.println( "-[serverName: " + req.getServerName() + "]-" );
+ out.println( "-[serverPort: " + req.getServerPort() + "]-" );
+ out.println( "-[servletPath: " + req.getServletPath() + "]-" );
+ out.println( "-[locale: " + req.getLocale() + "]-" );
+
+ Enumeration list = req.getAttributeNames();
+ out.println( "--> requestAttributes <-- " );
+ while ( list.hasMoreElements() )
+ {
+ String name = (String) list.nextElement();
+ out.println( "\t-[" + name + ": " + req.getAttribute( name ) + "]-" );
+ }
+
+ list = req.getParameterNames();
+ out.println( "--> parameters <-- " );
+ while ( list.hasMoreElements() )
+ {
+ String name = (String) list.nextElement();
+ out.println( "\t-[" + name + ": " + req.getParameter( name ) + "]-" );
+ }
+
+ list = req.getHeaderNames();
+ out.println( "--> headers <-- " );
+ while ( list.hasMoreElements() )
+ {
+ String name = (String) list.nextElement();
+ out.println( "\t-[" + name + ": " + req.getHeader( name ) + "]-" );
+ }
+ }
+
+
+ public static String getNotNull( HttpServletRequest req, HttpServletResponse resp, String param ) throws ServletException, IOException
+ {
+ String value = req.getParameter( param );
+
+ if ( value == null )
+ {
+ String msg = "error: value for parameter " + param + " was not found";
+ resp.getWriter().print( msg );
+ resp.getWriter().close();
+ throw new ServletException( msg );
+ }
+ else
+ {
+ return value;
+ }
+ }
+}