From 3649f3bf65605413c4e5acafeb053017ccabc71c Mon Sep 17 00:00:00 2001 From: Sanjay Dalal Date: Fri, 26 Jun 2009 22:54:16 +0000 Subject: [PATCH] CSPACE-225, CSPACE-226 added database login provider (extension to JBossDBLoginProvider) with db realm schema, various config to enable authentication in JBoss and webapp, db init creates cspace db, dump of db realm working for authentication test --- services/authentication/nb-configuration.xml | 20 ++ services/authentication/pom.xml | 66 +++++ .../authentication/CSpaceDBLoginModule.java | 256 ++++++++++++++++++ .../resources/config/jboss-login-config.xml | 34 +++ .../config/jboss-web-security-config.xml | 17 ++ .../config/web-security-config.xml.xml | 32 +++ .../resources/db/mysql/authentication.sql | 18 ++ .../main/resources/db/mysql/test_authn.sql | 15 + .../authentication/AppTest.java | 38 +++ .../resources/scripts/db/mysql/init_db.sql | 9 + .../scripts/db/mysql/service-dump.sql | 98 +++++++ 11 files changed, 603 insertions(+) create mode 100644 services/authentication/nb-configuration.xml create mode 100644 services/authentication/pom.xml create mode 100644 services/authentication/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java create mode 100644 services/authentication/src/main/resources/config/jboss-login-config.xml create mode 100644 services/authentication/src/main/resources/config/jboss-web-security-config.xml create mode 100644 services/authentication/src/main/resources/config/web-security-config.xml.xml create mode 100644 services/authentication/src/main/resources/db/mysql/authentication.sql create mode 100644 services/authentication/src/main/resources/db/mysql/test_authn.sql create mode 100644 services/authentication/src/test/java/org/collectionspace/authentication/AppTest.java create mode 100644 src/main/resources/scripts/db/mysql/service-dump.sql diff --git a/services/authentication/nb-configuration.xml b/services/authentication/nb-configuration.xml new file mode 100644 index 000000000..6c3fe600b --- /dev/null +++ b/services/authentication/nb-configuration.xml @@ -0,0 +1,20 @@ + + + + + + default + 8 + 80 + + diff --git a/services/authentication/pom.xml b/services/authentication/pom.xml new file mode 100644 index 000000000..6d82475f6 --- /dev/null +++ b/services/authentication/pom.xml @@ -0,0 +1,66 @@ + + + + org.collectionspace.services.main + org.collectionspace.services + 1.0 + + 4.0.0 + org.collectionspace.services + org.collectionspace.services.authentication + jar + 1.0 + services.authentication + + + 4.2.3.GA + 3.0 + UTF-8 + + + + + + + junit + junit + 4.1 + test + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + + + javax.security + jaas + 1.0.01 + provided + + + jboss + jbosssx + 4.2.3.GA + + + + + cspace-services-authn + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + + diff --git a/services/authentication/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java b/services/authentication/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java new file mode 100644 index 000000000..2b96d1e8f --- /dev/null +++ b/services/authentication/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java @@ -0,0 +1,256 @@ +/** + * Copyright 2009 University of California at Berkeley + */ +package org.collectionspace.authentication; + +import java.lang.reflect.Constructor; +import java.security.Principal; +import java.security.acl.Group; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import java.util.HashMap; +import javax.naming.InitialContext; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import org.jboss.security.SimpleGroup; +import org.jboss.security.SimplePrincipal; +import org.jboss.security.auth.spi.DatabaseServerLoginModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CSpaceDBLoginModule extends DatabaseServerLoginModule { + + //disabled due to classloading problem + //private Logger logger = LoggerFactory.getLogger(CSpaceDBLoginModule.class); + private boolean log = true; + + private void log(String str) { + System.out.println(str); + } + + protected String getUsersPassword() throws LoginException { + + String username = getUsername(); + String password = null; + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + InitialContext ctx = null; + try{ +// Properties env = new Properties(); +// env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); +// env.setProperty(Context.PROVIDER_URL, "jnp://localhost:1199/"); +// env.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); +// ctx = new InitialContext(env); +//// ctx = new InitialContext(); +// DataSource ds = (DataSource) ctx.lookup(dsJndiName); +// if(ds == null){ +// throw new IllegalArgumentException("datasource not found: " + dsJndiName); +// } +// conn = ds.getConnection(); + Class.forName("com.mysql.jdbc.Driver"); + conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cspace", "test", "test"); + + // Get the password + if(log){ + log("Excuting query: " + principalsQuery + ", with username: " + username); + } + ps = conn.prepareStatement(principalsQuery); + ps.setString(1, username); + rs = ps.executeQuery(); + if(rs.next() == false){ + if(log){ + log("Query returned no matches from db"); + } + throw new FailedLoginException("No matching username found"); + } + + password = rs.getString(1); + password = convertRawPassword(password); + if(log){ + log("Obtained user password"); + } +// }catch(NamingException ex){ +// LoginException le = new LoginException("Error looking up DataSource from: " + dsJndiName); +// le.initCause(ex); +// throw le; + }catch(SQLException ex){ + LoginException le = new LoginException("Query failed"); + le.initCause(ex); + throw le; + }catch(Exception ex){ + LoginException le = new LoginException("Unknown Exception"); + le.initCause(ex); + throw le; + }finally{ + if(rs != null){ + try{ + rs.close(); + }catch(SQLException e){ + } + } + if(ps != null){ + try{ + ps.close(); + }catch(SQLException e){ + } + } + if(conn != null){ + try{ + conn.close(); + }catch(SQLException ex){ + } + } + if(ctx != null){ + try{ + ctx.close(); + }catch(Exception e){ + } + } + } + return password; + } + + /** Execute the rolesQuery against the dsJndiName to obtain the roles for + the authenticated user. + + @return Group[] containing the sets of roles + */ + protected Group[] getRoleSets() throws LoginException { + String username = getUsername(); + if(log){ + log("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username); + } + + Connection conn = null; + HashMap setsMap = new HashMap(); + PreparedStatement ps = null; + ResultSet rs = null; + + try{ +// InitialContext ctx = new InitialContext(); +// DataSource ds = (DataSource) ctx.lookup(dsJndiName); +// conn = ds.getConnection(); + conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/cspace", "root", "admin"); + // Get the user role names + if(log){ + log("Excuting query: " + rolesQuery + ", with username: " + username); + } + + ps = + conn.prepareStatement(rolesQuery); + try{ + ps.setString(1, username); + }catch(ArrayIndexOutOfBoundsException ignore){ + // The query may not have any parameters so just try it + } + rs = ps.executeQuery(); + if(rs.next() == false){ + if(log){ + log("No roles found"); + } +// if(aslm.getUnauthenticatedIdentity() == null){ +// throw new FailedLoginException("No matching username found in Roles"); +// } + /* We are running with an unauthenticatedIdentity so create an + empty Roles set and return. + */ + + Group[] roleSets = {new SimpleGroup("Roles")}; + return roleSets; + } + + do{ + String name = rs.getString(1); + String groupName = rs.getString(2); + if(groupName == null || groupName.length() == 0){ + groupName = "Roles"; + } + + Group group = (Group) setsMap.get(groupName); + if(group == null){ + group = new SimpleGroup(groupName); + setsMap.put(groupName, group); + } + + try{ +// Principal p = aslm.createIdentity(name); + Principal p = createIdentity(name); + if(log){ + log("Assign user to role " + name); + } + + group.addMember(p); + }catch(Exception e){ + log("Failed to create principal: " + name + " " + e.toString()); + } + + }while(rs.next()); + } // catch(NamingException ex) + // { + // LoginException le = new LoginException("Error looking up DataSource from: "+dsJndiName); + // le.initCause(ex); + // throw le; + // } + catch(SQLException ex){ + LoginException le = new LoginException("Query failed"); + le.initCause(ex); + throw le; + }finally{ + if(rs != null){ + try{ + rs.close(); + }catch(SQLException e){ + } + } + if(ps != null){ + try{ + ps.close(); + }catch(SQLException e){ + } + } + if(conn != null){ + try{ + conn.close(); + }catch(Exception ex){ + } + } + + } + + Group[] roleSets = new Group[setsMap.size()]; + setsMap.values().toArray(roleSets); + return roleSets; + } + + /** Utility method to create a Principal for the given username. This + * creates an instance of the principalClassName type if this option was + * specified using the class constructor matching: ctor(String). If + * principalClassName was not specified, a SimplePrincipal is created. + * + * @param username the name of the principal + * @return the principal instance + * @throws java.lang.Exception thrown if the custom principal type cannot be created. + */ + protected Principal createIdentity(String username) + throws Exception { + Principal p = null; + if(principalClassName == null){ + p = new SimplePrincipal(username); + }else{ + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class clazz = loader.loadClass(principalClassName); + Class[] ctorSig = {String.class}; + Constructor ctor = clazz.getConstructor(ctorSig); + Object[] ctorArgs = {username}; + p = + (Principal) ctor.newInstance(ctorArgs); + } + + return p; + } +} \ No newline at end of file diff --git a/services/authentication/src/main/resources/config/jboss-login-config.xml b/services/authentication/src/main/resources/config/jboss-login-config.xml new file mode 100644 index 000000000..945f643d6 --- /dev/null +++ b/services/authentication/src/main/resources/config/jboss-login-config.xml @@ -0,0 +1,34 @@ + + + + + + + + + java:/CspaceDS + SHA-256 + false + + select passwd from users where username=? + + + select rolename, 'Roles' from users_roles where username=? + + + + diff --git a/services/authentication/src/main/resources/config/jboss-web-security-config.xml b/services/authentication/src/main/resources/config/jboss-web-security-config.xml new file mode 100644 index 000000000..69b1c7a5b --- /dev/null +++ b/services/authentication/src/main/resources/config/jboss-web-security-config.xml @@ -0,0 +1,17 @@ + + + + + + +java:/jaas/cspace diff --git a/services/authentication/src/main/resources/config/web-security-config.xml.xml b/services/authentication/src/main/resources/config/web-security-config.xml.xml new file mode 100644 index 000000000..61e1e08ca --- /dev/null +++ b/services/authentication/src/main/resources/config/web-security-config.xml.xml @@ -0,0 +1,32 @@ + + + + + + + CollectionSpace Services + /* + + + * + + + + NONE + + + + + BASIC + CollectionSpace realm + \ No newline at end of file diff --git a/services/authentication/src/main/resources/db/mysql/authentication.sql b/services/authentication/src/main/resources/db/mysql/authentication.sql new file mode 100644 index 000000000..97be14377 --- /dev/null +++ b/services/authentication/src/main/resources/db/mysql/authentication.sql @@ -0,0 +1,18 @@ +-- +-- Copyright 2009 University of California at Berkeley +-- Licensed under the Educational Community License (ECL), Version 2.0. +-- You may not use this file except in compliance with this License. +-- +use cspace; + +DROP TABLE IF EXISTS users; +CREATE TABLE users(username VARCHAR(128) PRIMARY KEY, passwd VARCHAR(128) NOT NULL ); +CREATE INDEX username_users on users(username); + +DROP TABLE IF EXISTS roles; +CREATE TABLE roles(rolename VARCHAR(128) PRIMARY KEY, rolegroup VARCHAR(128)); +CREATE INDEX rolename_roles on roles(rolename); + +DROP TABLE IF EXISTS users_roles; +CREATE TABLE users_roles(username VARCHAR(128) NOT NULL, rolename VARCHAR(128) NOT NULL); +CREATE INDEX username_users_roles on users_roles(username); \ No newline at end of file diff --git a/services/authentication/src/main/resources/db/mysql/test_authn.sql b/services/authentication/src/main/resources/db/mysql/test_authn.sql new file mode 100644 index 000000000..9ee6e3fd6 --- /dev/null +++ b/services/authentication/src/main/resources/db/mysql/test_authn.sql @@ -0,0 +1,15 @@ +-- +-- Copyright 2009 University of California at Berkeley +-- Licensed under the Educational Community License (ECL), Version 2.0. +-- You may not use this file except in compliance with this License. +-- +use cspace; + +insert into users (username, passwd) values ('test', 'test'); +insert into users (username, passwd) values ('admin', 'admin'); + +insert into roles (rolename, rolegroup) values ('collections_manager', 'collections'); +insert into roles (rolename, rolegroup) values ('collections_registrar', 'collections'); + +insert into users_roles(username, rolename) values ('test', 'collections_manager'); +insert into users_roles(username, rolename) values('admin', 'collections_registrar'); \ No newline at end of file diff --git a/services/authentication/src/test/java/org/collectionspace/authentication/AppTest.java b/services/authentication/src/test/java/org/collectionspace/authentication/AppTest.java new file mode 100644 index 000000000..737ba7aaa --- /dev/null +++ b/services/authentication/src/test/java/org/collectionspace/authentication/AppTest.java @@ -0,0 +1,38 @@ +package org.collectionspace.authentication; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Unit test for simple App. + */ +public class AppTest + extends TestCase +{ + /** + * Create the test case + * + * @param testName name of the test case + */ + public AppTest( String testName ) + { + super( testName ); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() + { + return new TestSuite( AppTest.class ); + } + + /** + * Rigourous Test :-) + */ + public void testApp() + { + assertTrue( true ); + } +} diff --git a/src/main/resources/scripts/db/mysql/init_db.sql b/src/main/resources/scripts/db/mysql/init_db.sql index a59ce60af..660246eaf 100644 --- a/src/main/resources/scripts/db/mysql/init_db.sql +++ b/src/main/resources/scripts/db/mysql/init_db.sql @@ -20,17 +20,26 @@ FLUSH PRIVILEGES; DROP database IF EXISTS jbossdb; CREATE database jbossdb; +-- +-- recreate cspace database +-- +DROP database IF EXISTS cspace; +CREATE database cspace; + -- -- recreate nuxeo database -- DROP database IF EXISTS nuxeo; CREATE database nuxeo; + -- -- grant privileges to test user on nuxeo and jbossdb databases -- GRANT ALL PRIVILEGES ON jbossdb.* TO 'test'@'localhost' IDENTIFIED BY 'test' WITH GRANT OPTION; FLUSH PRIVILEGES; +GRANT ALL PRIVILEGES ON cspace.* TO 'test'@'localhost' IDENTIFIED BY 'test' WITH GRANT OPTION; +FLUSH PRIVILEGES; GRANT ALL PRIVILEGES ON nuxeo.* TO 'test'@'localhost' IDENTIFIED BY 'test' WITH GRANT OPTION; FLUSH PRIVILEGES; diff --git a/src/main/resources/scripts/db/mysql/service-dump.sql b/src/main/resources/scripts/db/mysql/service-dump.sql new file mode 100644 index 000000000..457d39227 --- /dev/null +++ b/src/main/resources/scripts/db/mysql/service-dump.sql @@ -0,0 +1,98 @@ +-- MySQL Administrator dump 1.4 +-- +-- ------------------------------------------------------ +-- Server version 5.1.31-community + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; + +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + + +-- +-- Create schema cspace +-- + +CREATE DATABASE IF NOT EXISTS cspace; +USE cspace; + +-- +-- Definition of table `roles` +-- + +DROP TABLE IF EXISTS `roles`; +CREATE TABLE `roles` ( + `rolename` varchar(128) NOT NULL, + `rolegroup` varchar(128) DEFAULT NULL, + PRIMARY KEY (`rolename`), + KEY `rolename_roles` (`rolename`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `roles` +-- + +/*!40000 ALTER TABLE `roles` DISABLE KEYS */; +INSERT INTO `roles` (`rolename`,`rolegroup`) VALUES + ('collections_manager','collections'), + ('collections_registrar','collections'); +/*!40000 ALTER TABLE `roles` ENABLE KEYS */; + + +-- +-- Definition of table `users` +-- + +DROP TABLE IF EXISTS `users`; +CREATE TABLE `users` ( + `username` varchar(128) NOT NULL, + `passwd` varchar(128) NOT NULL, + PRIMARY KEY (`username`), + KEY `username_users` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `users` +-- + +/*!40000 ALTER TABLE `users` DISABLE KEYS */; +INSERT INTO `users` (`username`,`passwd`) VALUES + ('test','n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg='); +/*!40000 ALTER TABLE `users` ENABLE KEYS */; + + +-- +-- Definition of table `users_roles` +-- + +DROP TABLE IF EXISTS `users_roles`; +CREATE TABLE `users_roles` ( + `username` varchar(128) NOT NULL, + `rolename` varchar(128) NOT NULL, + KEY `username_users_roles` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- +-- Dumping data for table `users_roles` +-- + +/*!40000 ALTER TABLE `users_roles` DISABLE KEYS */; +INSERT INTO `users_roles` (`username`,`rolename`) VALUES + ('test','collections_manager'); +/*!40000 ALTER TABLE `users_roles` ENABLE KEYS */; + + + + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -- 2.47.3