Creating your own authentication scheme
This step by step instruction explains how to extend the Daisy CMS with your own authentication scheme that fits your needs.
Assumption: You already set up a unix system and created your users inside that system. Users will access your system via ssh.
Task: You would like to use your existing user database inside your linux system for authentication at Daisy CMS, too. Any user who is able to log on to your linux system via ssh should be able to log on to Daisy using the same credentials. If this user does not exist inside Daisy, it will be created automatically the first time the user logs on to Daisy CMS.
Your authentication source can and probably will be different from the setting described above (e.g. you could save the credentials of your user in a database and retrieve these data via JDBC, or you could even store the user data inside a Daisy document). Even in that case working through this tutorial should be beneficial. I will point out where to actually have to provide your own logic for authentification.
While this tutorial describes how to get thinks running on a linux system, it should get you started on a windows system, too.
Step 1: Checking out the sources
Download the daisy sources daisy-x.x.x.tar.gz of the latest stable (or milestone) release at the download area (or check out the lastest version from SVN, whatever you prefer). Extract the tarball into a directory on your local machine (although you do not have to create that environment variable, I will refer to that directory as DAISY_SRC during the rest ot this tutorial):
$ tar xvzf daisy-x.x.x.tar.gz
Step 2: Setting up maven
Though we do not have to compile daisy complelety, we have to set up the maven as build tool. In order to do so, follow the instructions given in the first sections of $DAISY_SRC/README.TXT. You can safely skip all sections marked with [skip when building dist only]. Once you are able to compile daisy (like described in the section Compiling), you are ready to proceed with this tutorial.
Step 3: Download, build and install SSHTools
If your authentication scheme is different from that desribed above, you might skip this step.
We use SSHTools in order to check whether a user can log on via ssh to the unix host. SSHTools is a suite of Java SSH applications providing a Java SSH API. Download the latest J2SSH sources tarball (j2ssh-<version>-src.tar.gz) from sourceforge and extract it into a directory at your local machine.
$ tar xzvf j2ssh-<version>-src.tar.gz
Change to that directory and start the build process by simply typing ant (you have to have ant installed, of course):
$ cd j2ssh $ ant
You should end up with a java archive j2ssh-core.jar in the directory dist/lib.
As of version 0.2.7, J2SSH does not compile with Java 1.5, since it uses enum as variable in the source code several times (which serves as keyword in Java 1.5). You have to compile J2SSH 0.2.7 with Java 1.4 SDK or patch the sources.
Create the directory hierarchy j2ssh/jars both inside your local maven-repository (~/.maven/repository/) and inside the lib directory of your daisy binary distribution ($DAISY_HOME/lib). In both places, copy the file j2ssh-core.jar into the newly created directory. Rename the file to j2ssh-core-0.2.7.jar
$ mkdir -p ~/.maven/repository/j2ssh/jars $ cp dist/lib/j2ssh-core.jar ~/.maven/repository/j2ssh/jars/j2ssh-core-0.2.7.jar $ mkdir -p $DAISY_HOME/lib/j2ssh/jars $ cp dist/lib/j2ssh-core.jar $DAISY_HOME/lib/j2ssh/jars/j2ssh-core-0.2.7.jar
Step 3: Copy the sources of the Ntlm-authentication
Descend to the dircectory $DAISY_SRC/services/ and make a copy of the directory ntlm-auth, which contains the sources for the Ntlm authentication scheme. We take these sources as starting point and we will modify them to fit our needs.
$ cp -R ntlm-auth/ ssh-auth
Step 4: Modify property.xml
Inside the newly created directory $DAISY_SRC/services/ssh-auth, edit the file project.xml. Adapt id and name of the project. Remove the dependency to jcifs and add j2ssh as dependency. After editing, project.xml should look like:
<project>
<pomVersion>3</pomVersion>
<groupId>daisy</groupId>
<id>daisy-auth-ssh</id>
<name>Daisy: SSH Authenication</name>
<currentVersion>1.4-dev</currentVersion>
<dependencies>
<dependency>
<groupId>avalon-framework</groupId>
<artifactId>avalon-framework-api</artifactId>
<version>4.1.5</version>
</dependency>
<dependency>
<groupId>daisy</groupId>
<artifactId>daisy-repository-server-spi</artifactId>
<version>1.4-dev</version>
</dependency>
<dependency>
<groupId>daisy</groupId>
<artifactId>daisy-repository-api</artifactId>
<version>1.4-dev</version>
</dependency>
<dependency>
<groupId>j2ssh</groupId>
<artifactId>j2ssh-core</artifactId>
<version>0.2.7</version>
</dependency>
</dependencies>
<!-- build information for the project -->
<build>
<sourceDirectory>${basedir}/src/java</sourceDirectory>
<resources>
<resource>
<directory>${basedir}/src/conf</directory>
<targetPath>BLOCK-INF</targetPath>
<includes>
<include>block.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
Step 5: Modify block.xml
Descend into the directory $DAISY_SRC/services/ssh-auth/src/conf and edit the file block.xml. Set the name of the container to ssh, change the component class attribute to SSHAuthenticationFactory (we will create this class shortly) and put j2ssh-core.jar on the classpath. After editing, block.xml should look like:
<container name="ssh">
<component name="ssh" class="org.outerj.daisy.authentication.impl.SSHAuthenticationFactory">
<configuration>
</configuration>
</component>
<classloader>
<classpath>
<repository>
<resource id="j2ssh:j2ssh-core" version="0.2.7" />
</repository>
</classpath>
</classloader>
</container>
Step 6: Create the java classes for authentication
Step 6a: Rename the existing sourcefiles for AuthenticationFactory and AuthenticationScheme
Descend into the directory
$DAISY_SRC/services/ssh-auth/src/java/org/outerj/daisy/authentication/impl
Rename the both existing sourcefiles for the AuthenticationFactory and the
AuthenticationScheme
$ mv NtlmAuthenticationFactory.java SSHAuthenticationFactory.java $ mv NtlmAuthenticationScheme.java SSHAuthenticationScheme.java
Step 6b: Edit SSHAuthenticationFactory.java
In your favourite editor or IDE, edit the source file for the SSH Authentication Factory and change it to:
package org.outerj.daisy.authentication.impl;
import java.io.IOException;
import org.outerj.daisy.authentication.AuthenticationScheme;
import org.outerj.daisy.authentication.AuthenticationException;
import org.outerj.daisy.authentication.UserCreator;
import org.outerj.daisy.repository.Credentials;
import org.outerj.daisy.repository.user.User;
import org.outerj.daisy.repository.user.UserManager;
import com.sshtools.j2ssh.SshClient;
import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient;
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification;
public class SSHAuthenticationScheme implements AuthenticationScheme {
private final String name;
private final String description;
private final String SSHServerAddress;
private final UserCreator userCreator;
public SSHAuthenticationScheme(String name, String description, String SSHServerAddress, UserCreator userCreator) {
this.name = name;
this.description = description;
this.SSHServerAddress = SSHServerAddress;
this.userCreator = userCreator;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean check(Credentials credentials) throws AuthenticationException {
SshClient ssh = new SshClient();
try {
ssh.connect(SSHServerAddress, new IgnoreHostKeyVerification());
PasswordAuthenticationClient pac = new PasswordAuthenticationClient();
pac.setUsername(credentials.getLogin());
pac.setPassword(credentials.getPassword());
int result = ssh.authenticate(pac);
if(result==AuthenticationProtocolState.FAILED || result==AuthenticationProtocolState.PARTIAL)
return true;
else if(result==AuthenticationProtocolState.COMPLETE)
return true;
else
throw new AuthenticationException("No valid AuthenticationProtocolState delivered");
} catch (IOException e) {
// CANNOT REACH HOST
throw new AuthenticationException("Error authenticating using SSH", e);
}
}
public void clearCaches() {
// do nothing
}
public User createUser(Credentials crendentials, UserManager userManager) throws AuthenticationException {
if (userCreator != null) {
if (check(crendentials)) {
return userCreator.create(crendentials.getLogin(), userManager);
}
}
return null;
}
}
Most of the action happens inside the method check(Credentials c). We use the ssh tools to check whether we can log on to the unix host via SSH. If yes, this method return true.
If you want to implement your own logic for authentication, you basically have to replace the code inside the method check(credentials c) with your own logic which checks whether the credentials are valid in your environment.
Step 6c: Edit SSHAuthenticationScheme.java
In your favourite editor or IDE, edit the source file for the SSH Authentication Scheme and change it to:
package org.outerj.daisy.authentication.impl;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.ServiceException;
import org.outerj.daisy.authentication.*;
/**
* Constructs and registers SSHAuthenticationSchemes with the UserAuthenticator.
*
* @avalon.component version="1.0" name="ssh-authentication-scheme" lifestyle="singleton"
*/
public class SSHAuthenticationFactory extends AbstractAuthenticationFactory implements Configurable {
/**
* @avalon.dependency key="auth-scheme-registrar" type="org.outerj.daisy.authentication.AuthenticationSchemeRegistrar"
*/
public void service(ServiceManager serviceManager) throws ServiceException {
super.service(serviceManager);
}
public void configure(Configuration configuration) throws ConfigurationException {
Configuration[] schemeConfs = configuration.getChildren("scheme");
for (int i = 0; i < schemeConfs.length; i++) {
String name = schemeConfs[i].getAttribute("name");
String description = schemeConfs[i].getAttribute("description");
String sshServerAddress = schemeConfs[i].getChild("SSHServerAddress").getValue();
UserCreator userCreator = UserCreatorFactory.createUser(schemeConfs[i], name);
AuthenticationScheme scheme = new SSHAuthenticationScheme(name, description, sshServerAddress, userCreator);
Configuration cacheConf = schemeConfs[i].getChild("cache");
if (cacheConf.getAttributeAsBoolean("enabled")) {
int maxCacheSize = cacheConf.getAttributeAsInteger("maxCacheSize", 3000);
long maxCacheDuration = cacheConf.getAttributeAsLong("maxCacheDuration", 30 * 60 * 1000); // default: half an hour
scheme = new CachingAuthenticationScheme(scheme, maxCacheDuration, maxCacheSize);
}
schemes.add(scheme);
}
}
}
IP address or hostname of the SSH-Server and other parameters are read from a the configuration file myconfig.xml, which we will create lateron. This information is used inside the constructor of a new instance of our SSHAuthenticationScheme, which we created above.
If you want to compile these files in Eclipse or any othe
Java-IDE, you will need the following jars in the classpath to compile the your
new authentication scheme classes:
avalon-framework-api-<version>.jar
daisy-repository-api-<version>.jar
daisy-repository-server-spi-<version>.jar
j2ssh-core-<version>.jar
Step 7: Building the authentication scheme
Step 7a: Compiling and packing your classes
Now, you should be able to compile your classes and build the jar file of your authentication scheme. We use maven for that purpose. Simple change to your directory $DAISY_SRC/services/ssh-auth and type maven
$ maven
This will compile your classes and create a jar file daisy-auth-ssh-<version>.jar in the directory $DAISY_SRC/services/ssh-auth/target/. If you get compile errors, fix these errors and run maven again
Step 7b: Copying the jar-file into place
Copy the newly created jar file both into your local maven-repository (~/.maven/repository/) and inside the lib directory of your daisy binary distribution ($DAISY_HOME/lib). In both places, copy the file daisy-auth-ssh-<version>.jar into the directory daisy/jars/.
$ cp target/daisy-auth-ssh-<version>.jar ~/.maven/repository/daisy/jars/ $ mkdir -p $DAISY_HOME/lib/j2ssh/jars $ cp target/daisy-auth-ssh-<version>.jar $DAISY_HOME/lib/daisy/jars/
If you don't want to create a binary distribution of daisy it might be sufficient to copy the jar-file to the $DAISY_HOME/lib directory only.
Step 8: Registering and configuring the new component
Step 8a: Registering the new component
Edit the file $DAISY_HOME/repository-server/conf/block.xml. Inside the container named authentication, include your newly created scheme. The element <container> of block.xml now should look like (adapt the versions of your jar files for ntlm and ssh authentication!):
<container name="authentication">
<services>
<service type="org.outerj.daisy.authentication.UserAuthenticator">
<source>authenticator</source>
</service>
<service type="org.outerj.daisy.authentication.AuthenticationSchemeRegistrar">
<source>authenticator</source>
</service>
</services>
<component name="authenticator" class="org.outerj.daisy.authentication.impl.UserAuthenticatorImpl"/>
<component name="daisy-native" class="org.outerj.daisy.authentication.impl.DaisyAuthenticationFactory">
<configuration>
<cache enabled="true" maxCacheSize="3000" maxCacheDuration="1800000"/>
</configuration>
</component>
<component name="ldap" class="org.outerj.daisy.authentication.impl.LdapAuthenticationFactory">
<configuration>
<!-- See myconfig.xml.template for an example configuration -->
</configuration>
</component>
<include name="ntlm" id="daisy:daisy-auth-ntlm" version="1.4-dev"/>
<include name="ssh" id="daisy:daisy-auth-ssh" version="1.4-dev"/>
</container>
Step 8b: Configuring the new component
Inside the directory $DAISY_HOME/repository-server/conf/ you will find a file myconfig.xml.template. Copy this file into the directory conf/ inside your repository and rename it to myconfig.xml
$ cp $DAISY_HOME/repository-server/conf/myconfig.xml.template /path/to/your/repository/conf/myconfig.xml
Edit the newly created file myconfig.xml. Add a target in order to configure your authentication scheme, inside that target, fill in the IP or host name of your SSH-server. Also, in order to enable automatically creation of user accounts once the user logs in to daisy for the first time, define our newly created scheme as authentification scheme for user creation as shown below:
<targets>
<target path="..."
...
</target>
... many more targets
<target path="/daisy/repository/authentication/authenticator">
<configuration>
<!-- Indicates which authentication scheme to use, if any, to automatically create new users. -->
<authenticationSchemeForUserCreation>ssh</authenticationSchemeForUserCreation>
</configuration>
</target>
... many more targets
<target path="/daisy/repository/authentication/ssh/ssh">
<configuration>
<scheme name="ssh" description="SSH to myhost">
<SSHServerAddress>IP or host name of your SSH server</SSHServerAddress>
<cache enabled="true" maxCacheSize="3000" maxCacheDuration="1800000"/>
<autoCreateUser>
<roles>
<role>User</role>
</roles>
<defaultRole>User</defaultRole>
<updateableByUser>false</updateableByUser>
</autoCreateUser>
</scheme>
</configuration>
</target>
</targets>
Step 9: Running and testing the new scheme
Now we are ready to test our new scheme! Restart the repository server and
the daisy wiki.
Try to log on to daisy with any username/password combination which is valid for
logging on to the SSH-Server. Log in should be successfull, you should be logged
on with the default role defined in the myconfig.xml configuration file. Login
as an administrator, invoke the user administration. In the user list, an
account should exist with the username you just logged on as. Also, if you
create a new user or edit an existing user, in the drop-down box for the
authentication scheme, you now should have the choice between the daisy built in
scheme and the scheme we newly created.


