From: Sanjay Dalal Date: Thu, 17 Dec 2009 02:07:37 +0000 (+0000) Subject: CSPACE-656 1-1 user-tenant association removed, added 1-many account-tenant association. X-Git-Url: https://git.aero2k.de/?a=commitdiff_plain;h=70281a9b89307ae11bcbd22befaa3e6f0a28c524;p=tmp%2Fjakarta-migration.git CSPACE-656 1-1 user-tenant association removed, added 1-many account-tenant association. Default id provider is made less JBoss dependent. Tenant info is now put as group in security context with tenant as CSpaceTenant group-memeber Common depends on authn service (for CSpaceTenant) Account throws 400 if tenant and/or userid are missing. Provisioned test account with tenant association for testing purposes. Added account tests for the same. Account client now packages all required classes. Used in authentication service tests. Servicecontext now retrieves one or more tenants from security context before retrieving bindings. Still uses one tenant only (needs consumer to send tenant info if user is associated with n tenants. Client test now recognizes 401 for all service operations. tests: service tests. account (new) tests. authn tests by enabling service side security M services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java A services/authentication/service/src/main/java/org/collectionspace/authentication/DatabaseRealm.java A services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceTenant.java M services/authentication/service/src/main/resources/config/jboss-login-config.xml M services/authentication/service/pom.xml M services/authentication/jaxb/src/main/resources/authentication_identity_provider.xsd M services/authentication/jaxb/pom.xml M services/authentication/client/src/test/java/org/collectionspace/services/authentication/client/AuthenticationServiceTest.java M services/authentication/client/src/main/resources/db/mysql/test_authn.sql M services/authentication/client/src/main/resources/db/mysql/authentication.sql M services/authentication/client/pom.xml M services/JaxRsServiceProvider/src/main/resources/META-INF/persistence.xml _M services/dimension/service _M services/dimension/jaxb M services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java M services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java M services/common/pom.xml M services/pom.xml M services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountStorageClient.java M services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java M services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java M services/account/service/pom.xml M services/account/jaxb/src/main/resources/accounts_common.xsd A services/account/client/src/test/java/org/collectionspace/services/account A services/account/client/src/test/java/org/collectionspace/services/account/client A services/account/client/src/test/java/org/collectionspace/services/account/client/test A + services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountTest.java A + services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java D services/account/client/src/test/java/org/collectionspace/services/client/test/AccountTest.java D services/account/client/src/test/java/org/collectionspace/services/client/test/AccountServiceTest.java D services/account/client/src/test/java/org/collectionspace/services/client/AccountClient.java D services/account/client/src/test/java/org/collectionspace/services/client/AccountProxy.java M services/account/client/src/test/resources/META-INF/persistence.xml M services/account/client/src/test/resources/log4j.properties A + services/account/client/src/main/java/org/collectionspace/services/client/AccountClient.java A + services/account/client/src/main/java/org/collectionspace/services/client/AccountProxy.java A services/account/client/src/main/resources/db/mysql/test_account.sql M services/account/client/src/main/resources/db/mysql/account.sql M services/account/client/pom.xml M services/account/client/build.xml M services/build.xml M services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTest.java M services/client/src/main/java/org/collectionspace/services/client/test/ServiceRequestType.java --- diff --git a/services/JaxRsServiceProvider/src/main/resources/META-INF/persistence.xml b/services/JaxRsServiceProvider/src/main/resources/META-INF/persistence.xml index c8b051378..b200fabf5 100644 --- a/services/JaxRsServiceProvider/src/main/resources/META-INF/persistence.xml +++ b/services/JaxRsServiceProvider/src/main/resources/META-INF/persistence.xml @@ -1,15 +1,17 @@ + http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> org.hibernate.ejb.HibernatePersistence CspaceDS org.collectionspace.services.account.AccountsCommon + org.collectionspace.services.account.AccountsCommon$Tenant org.collectionspace.services.account.AccountsCommonList org.collectionspace.services.account.AccountsCommonList$AccountListItem org.collectionspace.services.authentication.User org.collectionspace.services.authentication.Role org.collectionspace.services.authentication.UserRole + diff --git a/services/account/client/build.xml b/services/account/client/build.xml index 538e37c77..b41dd80a0 100644 --- a/services/account/client/build.xml +++ b/services/account/client/build.xml @@ -3,7 +3,7 @@ collectionspace account service - + @@ -19,13 +19,13 @@ - + + description="Package CollectionSpace Services" /> @@ -50,7 +50,7 @@ + description="Install" /> @@ -73,9 +73,9 @@ - + + description="Delete target directories" > @@ -127,7 +127,7 @@ + description="geneate ddl" /> @@ -152,31 +152,41 @@ + description="create tables(s), indices for account service"> + + + + + + url="jdbc:mysql://${db.host}:${db.port}/cspace" + userid="${db.user}" + password="${db.user.password}" + src="${db.script.dir}/test_account.sql" + > + description="deploy account service in ${jboss.server.cspace}"> + description="undeploy account service from ${jboss.server.cspace}"> + description="generate distribution for account service" depends="package"> diff --git a/services/account/client/pom.xml b/services/account/client/pom.xml index 8991f43d6..3f0d57433 100644 --- a/services/account/client/pom.xml +++ b/services/account/client/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> org.collectionspace.services.account @@ -40,7 +40,7 @@ org.collectionspace.services.client 1.0 - - + org.testng testng @@ -89,7 +89,7 @@ mysql-connector-java - + cspace-services-account-client diff --git a/services/account/client/src/test/java/org/collectionspace/services/client/AccountClient.java b/services/account/client/src/main/java/org/collectionspace/services/client/AccountClient.java similarity index 100% rename from services/account/client/src/test/java/org/collectionspace/services/client/AccountClient.java rename to services/account/client/src/main/java/org/collectionspace/services/client/AccountClient.java diff --git a/services/account/client/src/test/java/org/collectionspace/services/client/AccountProxy.java b/services/account/client/src/main/java/org/collectionspace/services/client/AccountProxy.java similarity index 100% rename from services/account/client/src/test/java/org/collectionspace/services/client/AccountProxy.java rename to services/account/client/src/main/java/org/collectionspace/services/client/AccountProxy.java diff --git a/services/account/client/src/main/resources/db/mysql/account.sql b/services/account/client/src/main/resources/db/mysql/account.sql index 6a3122c6f..2f5c9a0b1 100644 --- a/services/account/client/src/main/resources/db/mysql/account.sql +++ b/services/account/client/src/main/resources/db/mysql/account.sql @@ -1,2 +1,6 @@ +alter table tenants drop foreign key FKAAE82D09C4F08FD6; drop table if exists accounts_common; -create table accounts_common (csid varchar(255) not null, email longtext not null, first_name longtext not null, last_name longtext not null, mi varchar(1), mobile varchar(255), phone varchar(255), screen_name varchar(128) not null, status varchar(15) not null, tenantid varchar(255) not null, userid longtext not null, primary key (csid)); +drop table if exists tenants; +create table accounts_common (csid varchar(255) not null, email longtext not null, mobile varchar(255), phone varchar(255), screen_name varchar(128) not null, status varchar(15) not null, userid longtext not null, primary key (csid)); +create table tenants (HJID bigint not null auto_increment, id varchar(255) not null, name varchar(255) not null, TENANT_ACCOUNTSCOMMON_CSID varchar(255), primary key (HJID)); +alter table tenants add index FKAAE82D09C4F08FD6 (TENANT_ACCOUNTSCOMMON_CSID), add constraint FKAAE82D09C4F08FD6 foreign key (TENANT_ACCOUNTSCOMMON_CSID) references accounts_common (csid); diff --git a/services/account/client/src/main/resources/db/mysql/test_account.sql b/services/account/client/src/main/resources/db/mysql/test_account.sql new file mode 100644 index 000000000..ddc6e7ba6 --- /dev/null +++ b/services/account/client/src/main/resources/db/mysql/test_account.sql @@ -0,0 +1,9 @@ +-- +-- 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 `cspace`.`accounts_common` VALUES ('eeca40d7-dc77-4cc5-b489-16a53c75525a','test.test@berkeley.edu',NULL,NULL,'test','ACTIVE','test'); + +INSERT INTO `cspace`.`tenants` VALUES (1,'1','movingimages.us','eeca40d7-dc77-4cc5-b489-16a53c75525a'); \ No newline at end of file diff --git a/services/account/client/src/test/java/org/collectionspace/services/client/test/AccountServiceTest.java b/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java similarity index 78% rename from services/account/client/src/test/java/org/collectionspace/services/client/test/AccountServiceTest.java rename to services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java index bf5ac4ab8..3939470ae 100644 --- a/services/account/client/src/test/java/org/collectionspace/services/client/test/AccountServiceTest.java +++ b/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountServiceTest.java @@ -20,9 +20,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.collectionspace.services.client.test; +package org.collectionspace.services.account.client.test; +import java.util.ArrayList; import java.util.List; +import java.util.UUID; import javax.ws.rs.core.Response; import org.apache.commons.codec.binary.Base64; @@ -30,6 +32,8 @@ import org.collectionspace.services.client.AccountClient; import org.collectionspace.services.account.AccountsCommon; import org.collectionspace.services.account.AccountsCommonList; import org.collectionspace.services.account.Status; +import org.collectionspace.services.client.test.AbstractServiceTest; +import org.collectionspace.services.client.test.ServiceRequestType; import org.jboss.resteasy.client.ClientResponse; import org.testng.Assert; @@ -76,7 +80,7 @@ public class AccountServiceTest extends AbstractServiceTest { // Submit the request to the service and store the response. AccountsCommon account = - createAccountInstance("barney", "dino", "barney", "hithere08", "barney@dinoland.com"); + createAccountInstance("barney", "hithere08", "barney@dinoland.com", true, true, true); ClientResponse res = client.create(account); int statusCode = res.getStatus(); @@ -101,6 +105,47 @@ public class AccountServiceTest extends AbstractServiceTest { } } + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"create"}) + public void createWithoutTenant(String testName) throws Exception { + + setupCreate(testName); + + // Submit the request to the service and store the response. + AccountsCommon account = + createAccountInstance("babybop", "hithere08", "babybop@dinoland.com", false, true, true); + ClientResponse res = client.create(account); + int statusCode = res.getStatus(); + // Does it exactly match the expected status code? + if (logger.isDebugEnabled()) { + logger.debug(testName + ": status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + Assert.assertEquals(statusCode, Response.Status.BAD_REQUEST.getStatusCode()); + + } + + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"create"}) + public void createWithoutUser(String testName) throws Exception { + + setupCreate(testName); + + // Submit the request to the service and store the response. + AccountsCommon account = + createAccountInstance("babybop", "hithere08", "babybop@dinoland.com", true, false, true); + ClientResponse res = client.create(account); + int statusCode = res.getStatus(); + // Does it exactly match the expected status code? + if (logger.isDebugEnabled()) { + logger.debug(testName + ": status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + Assert.assertEquals(statusCode, Response.Status.BAD_REQUEST.getStatusCode()); + } + //to not cause uniqueness violation for account, createList is removed @Override @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, @@ -209,12 +254,12 @@ public class AccountServiceTest extends AbstractServiceTest { int i = 0; for (AccountsCommonList.AccountListItem item : items) { - logger.debug(testName + ": list-item[" + i + "] csid=" + - item.getCsid()); - logger.debug(testName + ": list-item[" + i + "] screenName=" + - item.getScreenName()); - logger.debug(testName + ": list-item[" + i + "] URI=" + - item.getUri()); + logger.debug(testName + ": list-item[" + i + "] csid=" + + item.getCsid()); + logger.debug(testName + ": list-item[" + i + "] screenName=" + + item.getScreenName()); + logger.debug(testName + ": list-item[" + i + "] URI=" + + item.getUri()); i++; } @@ -293,7 +338,7 @@ public class AccountServiceTest extends AbstractServiceTest { Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE); if (logger.isDebugEnabled()) { - logger.debug("got object to update with ID: " + knownResourceId); + logger.debug(testName + ": got object to update password with ID: " + knownResourceId); } AccountsCommon toUpdateAccount = (AccountsCommon) res.getEntity(); @@ -302,7 +347,7 @@ public class AccountServiceTest extends AbstractServiceTest { //change password toUpdateAccount.setPassword(Base64.encodeBase64("imagination".getBytes())); if (logger.isDebugEnabled()) { - logger.debug("updated object"); + logger.debug(testName + ": updated object"); logger.debug(objectAsXmlString(toUpdateAccount, AccountsCommon.class)); } @@ -328,7 +373,50 @@ public class AccountServiceTest extends AbstractServiceTest { } @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, - dependsOnMethods = {"updatePassword"}) + dependsOnMethods = {"update"}) + public void updatePasswordWithoutUser(String testName) throws Exception { + + // Perform setup. + setupUpdate(testName); + + ClientResponse res = + client.read(knownResourceId); + if (logger.isDebugEnabled()) { + logger.debug(testName + ": read status = " + res.getStatus()); + } + Assert.assertEquals(res.getStatus(), EXPECTED_STATUS_CODE); + + if (logger.isDebugEnabled()) { + logger.debug(testName + " : got object to update with ID: " + knownResourceId); + } + AccountsCommon toUpdateAccount = + (AccountsCommon) res.getEntity(); + Assert.assertNotNull(toUpdateAccount); + + toUpdateAccount.setUserId(null); + //change password + toUpdateAccount.setPassword(Base64.encodeBase64("imagination".getBytes())); + if (logger.isDebugEnabled()) { + logger.debug(testName + " : updated object"); + logger.debug(objectAsXmlString(toUpdateAccount, + AccountsCommon.class)); + } + + // Submit the request to the service and store the response. + res = client.update(knownResourceId, toUpdateAccount); + int statusCode = res.getStatus(); + // Check the status code of the response: does it match the expected response(s)? + if (logger.isDebugEnabled()) { + logger.debug(testName + ": status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + Assert.assertEquals(statusCode, Response.Status.BAD_REQUEST.getStatusCode()); + + } + + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"updatePasswordWithoutUser"}) public void deactivate(String testName) throws Exception { // Perform setup. @@ -405,7 +493,7 @@ public class AccountServiceTest extends AbstractServiceTest { // Note: The ID used in this 'create' call may be arbitrary. // The only relevant ID may be the one used in updateAccount(), below. AccountsCommon account = - createAccountInstance("simba", "mufasa", "simba", "tiger", "simba@lionking.com"); + createAccountInstance("simba", "tiger", "simba@lionking.com", true, true, true); ClientResponse res = client.update(NON_EXISTENT_ID, account); int statusCode = res.getStatus(); @@ -429,7 +517,7 @@ public class AccountServiceTest extends AbstractServiceTest { // // Note: The ID used in this 'create' call may be arbitrary. // The only relevant ID may be the one used in updateAccount(), below. - ClientResponse res = + ClientResponse res = client.read(knownResourceId); if (logger.isDebugEnabled()) { logger.debug(testName + ": read status = " + res.getStatus()); @@ -449,7 +537,7 @@ public class AccountServiceTest extends AbstractServiceTest { logger.debug(objectAsXmlString(toUpdateAccount, AccountsCommon.class)); } - EXPECTED_STATUS_CODE = Response.Status.BAD_REQUEST.getStatusCode(); + res = client.update(knownResourceId, toUpdateAccount); int statusCode = res.getStatus(); @@ -460,7 +548,7 @@ public class AccountServiceTest extends AbstractServiceTest { } Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); - Assert.assertEquals(statusCode, EXPECTED_STATUS_CODE); + Assert.assertEquals(statusCode, Response.Status.BAD_REQUEST.getStatusCode()); } // --------------------------------------------------------------- @@ -469,7 +557,7 @@ public class AccountServiceTest extends AbstractServiceTest { // Success outcomes @Override @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, - dependsOnMethods = {"testSubmitRequest", "updateNonExistent"}) + dependsOnMethods = {"testSubmitRequest", "updateWrongUser"}) public void delete(String testName) throws Exception { // Perform setup. @@ -533,8 +621,8 @@ public class AccountServiceTest extends AbstractServiceTest { // Check the status code of the response: does it match // the expected response(s)? if (logger.isDebugEnabled()) { - logger.debug("testSubmitRequest: url=" + url + - " status=" + statusCode); + logger.debug("testSubmitRequest: url=" + url + + " status=" + statusCode); } Assert.assertEquals(statusCode, EXPECTED_STATUS); @@ -543,17 +631,38 @@ public class AccountServiceTest extends AbstractServiceTest { // --------------------------------------------------------------- // Utility methods used by tests above // --------------------------------------------------------------- - private AccountsCommon createAccountInstance(String firstName, String lastName, String screenName, - String passwd, String email) { + /* + * createAccountInstance + * @param tenant fillup tenant + * @param user to fill up user + * @param password to fill up password + */ + private AccountsCommon createAccountInstance(String screenName, + String passwd, String email, boolean tenant, boolean user, boolean password) { AccountsCommon account = new AccountsCommon(); - account.setFirstName(firstName); - account.setLastName(lastName); account.setScreenName(screenName); - account.setUserId(screenName); - account.setPassword(Base64.encodeBase64(passwd.getBytes())); + if (user) { + account.setUserId(screenName); + } + if (password) { + account.setPassword(Base64.encodeBase64(passwd.getBytes())); + } account.setEmail(email); account.setPhone("1234567890"); + if (tenant) { + List atl = new ArrayList(); + AccountsCommon.Tenant at = new AccountsCommon.Tenant(); + at.setId(UUID.randomUUID().toString()); + at.setName("movingimages.us"); + atl.add(at); + + AccountsCommon.Tenant at2 = new AccountsCommon.Tenant(); + at2.setId(UUID.randomUUID().toString()); + at2.setName("collectionspace.org"); + atl.add(at2); + account.setTenant(atl); + } if (logger.isDebugEnabled()) { logger.debug("to be created, account common"); logger.debug(objectAsXmlString(account, diff --git a/services/account/client/src/test/java/org/collectionspace/services/client/test/AccountTest.java b/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountTest.java similarity index 63% rename from services/account/client/src/test/java/org/collectionspace/services/client/test/AccountTest.java rename to services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountTest.java index a2c6ed970..de083f7a1 100644 --- a/services/account/client/src/test/java/org/collectionspace/services/client/test/AccountTest.java +++ b/services/account/client/src/test/java/org/collectionspace/services/account/client/test/AccountTest.java @@ -2,16 +2,20 @@ * To change this template, choose Tools | Templates * and open the template in the editor. */ -package org.collectionspace.services.client.test; +package org.collectionspace.services.account.client.test; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; +import javax.persistence.NoResultException; import javax.persistence.Persistence; import javax.persistence.Query; import org.collectionspace.services.account.AccountsCommon; +import org.collectionspace.services.account.AccountsCommon.Tenant; import org.collectionspace.services.account.Status; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -52,27 +56,69 @@ public class AccountTest { } } + @SuppressWarnings("unchecked") + @Test(dataProvider = "testName", dataProviderClass = AccountTest.class) + public void createTest(String testName) throws Exception { + AccountsCommon account = null; + try { + account = findAccount("test"); + if (account != null) { + return; + } + } catch (NoResultException nre) { + //ignore + } + if (account == null) { + account = new AccountsCommon(); + } + account.setScreenName("test"); + account.setEmail("test.test@berkeley.edu"); + account.setUserId("test"); + account.setStatus(Status.ACTIVE); + id = UUID.randomUUID().toString(); + account.setCsid(id); + + Tenant tenant = new Tenant(); + tenant.setId("1"); + tenant.setName("movingimages.us"); + List lt = new ArrayList(); + lt.add(tenant); + account.setTenant(lt); + em.getTransaction().begin(); + em.persist(account); + // Commit the transaction + em.getTransaction().commit(); + if (logger.isDebugEnabled()) { + logger.debug("created/updated account " + + " screen name=" + account.getScreenName() + + " email=" + account.getEmail()); + } + } + @SuppressWarnings("unchecked") @Test(dataProvider = "testName", dataProviderClass = AccountTest.class) public void create(String testName) throws Exception { AccountsCommon account = new AccountsCommon(); account.setScreenName("john"); - account.setFirstName("John"); - account.setLastName("Doe"); account.setEmail("john.doe@berkeley.edu"); account.setUserId("johndoe"); account.setStatus(Status.ACTIVE); id = UUID.randomUUID().toString(); account.setCsid(id); - account.setTenantid("123"); //set by service runtime + Tenant tenant = new Tenant(); + tenant.setId("123"); + tenant.setName("movingimages.us.standalone"); + List lt = new ArrayList(); + lt.add(tenant); + account.setTenant(lt); em.getTransaction().begin(); em.persist(account); // Commit the transaction em.getTransaction().commit(); if (logger.isDebugEnabled()) { - logger.debug("created account " + - " first name=" + account.getFirstName() + - " email=" + account.getEmail()); + logger.debug("created account " + + " screen name=" + account.getScreenName() + + " email=" + account.getEmail()); } } @@ -83,8 +129,8 @@ public class AccountTest { AccountsCommon account = findAccount("john"); Assert.assertNotNull(account); if (logger.isDebugEnabled()) { - logger.debug("read account " + - " first name=" + account.getFirstName()); + logger.debug("read account " + + " screen name=" + account.getScreenName()); } } @@ -109,9 +155,9 @@ public class AccountTest { Assert.assertEquals(no, 1); AccountsCommon account = findAccount("john"); if (logger.isDebugEnabled()) { - logger.debug("updated account " + - " first name=" + account.getFirstName() + - " email=" + account.getEmail()); + logger.debug("updated account " + + " screen name=" + account.getScreenName() + + " email=" + account.getEmail()); } } @@ -119,22 +165,19 @@ public class AccountTest { @Test(dataProvider = "testName", dataProviderClass = AccountTest.class, dependsOnMethods = {"update"}) public void delete(String testName) throws Exception { - Query q = em.createQuery("delete from org.collectionspace.services.account.AccountsCommon where csid=:csid"); - q.setParameter("csid", id); // Begin transaction em.getTransaction().begin(); - int no = q.executeUpdate(); - ; + AccountsCommon account = findAccount("john"); + em.remove(account); if (logger.isDebugEnabled()) { - logger.debug("deleting account " + - " csid=" + id); + logger.debug("deleting account " + + " csid=" + id); } // Commit the transaction em.getTransaction().commit(); - Assert.assertEquals(no, 1); if (logger.isDebugEnabled()) { - logger.debug("deleted account " + - " csid=" + id); + logger.debug("deleted account " + + " csid=" + id); } } diff --git a/services/account/client/src/test/resources/META-INF/persistence.xml b/services/account/client/src/test/resources/META-INF/persistence.xml index 123eef2d6..f4c1e8b49 100644 --- a/services/account/client/src/test/resources/META-INF/persistence.xml +++ b/services/account/client/src/test/resources/META-INF/persistence.xml @@ -3,6 +3,7 @@ http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> org.collectionspace.services.account.AccountsCommon + org.collectionspace.services.account.AccountsCommon$Tenant org.collectionspace.services.account.AccountsCommonList org.collectionspace.services.account.AccountsCommonList$AccountListItem diff --git a/services/account/client/src/test/resources/log4j.properties b/services/account/client/src/test/resources/log4j.properties index fa374e924..f9c47870a 100644 --- a/services/account/client/src/test/resources/log4j.properties +++ b/services/account/client/src/test/resources/log4j.properties @@ -22,3 +22,4 @@ log4j.logger.org.apache=INFO log4j.logger.httpclient=INFO log4j.logger.org.jboss.resteasy=INFO log4j.logger.org.hibernate=INFO +log4j.logger.org.hibernate.cfg=WARN diff --git a/services/account/jaxb/src/main/resources/accounts_common.xsd b/services/account/jaxb/src/main/resources/accounts_common.xsd index 169858c3b..efc5f0336 100644 --- a/services/account/jaxb/src/main/resources/accounts_common.xsd +++ b/services/account/jaxb/src/main/resources/accounts_common.xsd @@ -2,28 +2,28 @@ + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" + xmlns:hj="http://hyperjaxb3.jvnet.org/ejb/schemas/customizations" + xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" + xmlns:ns="http://collectionspace.org/servics/account" + xmlns="http://collectionspace.org/services/account" + targetNamespace="http://collectionspace.org/services/account" + version="0.1" + jaxb:extensionBindingPrefixes="hj orm" + > - @@ -49,33 +49,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -104,7 +77,7 @@ - + @@ -121,18 +94,37 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/services/account/service/pom.xml b/services/account/service/pom.xml index 85138a9ad..327d1bd1a 100644 --- a/services/account/service/pom.xml +++ b/services/account/service/pom.xml @@ -6,7 +6,7 @@ org.collectionspace.services 1.0 - + 4.0.0 org.collectionspace.services org.collectionspace.services.account.service @@ -38,7 +38,7 @@ org.slf4j slf4j-log4j12 - + junit junit @@ -50,7 +50,7 @@ testng 5.6 - + commons-beanutils @@ -62,7 +62,7 @@ commons-logging 1.1 - + @@ -72,7 +72,7 @@ provided - + @@ -96,10 +96,10 @@ resteasy-multipart-provider 1.1.GA - + - + collectionspace-services-account diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java b/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java index dca0561d1..9261e4d95 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/AccountResource.java @@ -100,6 +100,10 @@ public class AccountResource path.path("" + csid); Response response = Response.created(path.build()).build(); return response; + } catch (BadRequestException bre) { + Response response = Response.status( + Response.Status.BAD_REQUEST).entity("Create failed reason " + bre.getErrorReason()).type("text/plain").build(); + throw new WebApplicationException(response); } catch (UnauthorizedException ue) { Response response = Response.status( Response.Status.UNAUTHORIZED).entity("Create failed reason " + ue.getErrorReason()).type("text/plain").build(); diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java index bdc735a85..afe561ad0 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountDocumentHandler.java @@ -30,8 +30,10 @@ import org.collectionspace.services.account.AccountsCommonList; import org.collectionspace.services.account.AccountsCommonList.AccountListItem; import org.collectionspace.services.account.Status; import org.collectionspace.services.common.document.AbstractDocumentHandler; +import org.collectionspace.services.common.document.BadRequestException; import org.collectionspace.services.common.document.DocumentWrapper; -import org.collectionspace.services.nuxeo.util.NuxeoUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -40,6 +42,7 @@ import org.collectionspace.services.nuxeo.util.NuxeoUtils; public class AccountDocumentHandler extends AbstractDocumentHandler { + private final Logger logger = LoggerFactory.getLogger(AccountDocumentHandler.class); private AccountsCommon account; private AccountsCommonList accountList; @@ -47,12 +50,29 @@ public class AccountDocumentHandler public void handleCreate(DocumentWrapper wrapDoc) throws Exception { String id = UUID.randomUUID().toString(); AccountsCommon account = wrapDoc.getWrappedObject(); + if (account.getUserId() == null || "".equals(account.getUserId())) { + String msg = "userId is missing"; + logger.error(msg); + throw new BadRequestException(msg); + } + List tl = account.getTenant(); + if (tl == null || tl.size() == 0) { + String msg = "missing tenant information!"; + logger.error(msg); + throw new BadRequestException(msg); + } account.setCsid(id); account.setStatus(Status.ACTIVE); } @Override public void handleUpdate(DocumentWrapper wrapDoc) throws Exception { + if (account.getPassword() != null + && (account.getUserId() == null || "".equals(account.getUserId()))) { + String msg = "userId is missing"; + logger.error(msg); + throw new BadRequestException(msg); + } } @Override @@ -102,8 +122,6 @@ public class AccountDocumentHandler AccountListItem accListItem = new AccountListItem(); accListItem.setScreenName(account.getScreenName()); accListItem.setEmail(account.getEmail()); - accListItem.setFirstName(account.getFirstName()); - accListItem.setLastName(account.getLastName()); accListItem.setStatus(account.getStatus()); String id = account.getCsid(); accListItem.setUri(getServiceContextPath() + id); @@ -144,7 +162,6 @@ public class AccountDocumentHandler * @param account */ private void sanitize(AccountsCommon account) { - account.setTenantid(""); account.setPassword(null); } } diff --git a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountStorageClient.java b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountStorageClient.java index f069b0f9e..0e1dcd8f0 100644 --- a/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountStorageClient.java +++ b/services/account/service/src/main/java/org/collectionspace/services/account/storage/AccountStorageClient.java @@ -23,9 +23,10 @@ */ package org.collectionspace.services.account.storage; +import java.util.ArrayList; +import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; -import javax.persistence.NoResultException; import javax.persistence.Query; import org.apache.commons.codec.binary.Base64; import org.collectionspace.services.account.AccountsCommon; @@ -33,7 +34,6 @@ import org.collectionspace.services.authentication.User; import org.collectionspace.services.common.context.ServiceContext; import org.collectionspace.services.common.document.BadRequestException; import org.collectionspace.services.common.document.DocumentException; -import org.collectionspace.services.common.document.DocumentFilter; import org.collectionspace.services.common.document.DocumentHandler; import org.collectionspace.services.common.document.DocumentHandler.Action; import org.collectionspace.services.common.document.DocumentNotFoundException; @@ -41,6 +41,7 @@ import org.collectionspace.services.common.document.DocumentWrapper; import org.collectionspace.services.common.document.DocumentWrapperImpl; import org.collectionspace.services.common.security.SecurityUtils; import org.collectionspace.services.common.storage.jpa.JpaStorageClient; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,14 +84,22 @@ public class AccountStorageClient extends JpaStorageClient { em.getTransaction().begin(); //if userid and password are given, add to default id provider if (account.getUserId() != null && account.getPassword() != null) { - User user = createUser(account, ctx.getTenantId()); + User user = createUser(account); em.persist(user); } - account.setTenantid(ctx.getTenantId()); +// if (account.getTenant() != null) { +// UserTenant ut = createTenantAssoc(account); +// em.persist(ut); +// } em.persist(account); em.getTransaction().commit(); handler.complete(Action.CREATE, wrapDoc); return (String) getValue(account, "getCsid"); + } catch (BadRequestException bre) { + if (em != null && em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + throw bre; } catch (Exception e) { if (em != null && em.getTransaction().isActive()) { em.getTransaction().rollback(); @@ -138,15 +147,21 @@ public class AccountStorageClient extends JpaStorageClient { if (account.getUserId() != null && account.getPassword() != null) { User userFound = getUser(em, account); - User user = createUser(account, ctx.getTenantId()); + User user = createUser(account); em.merge(user); } em.merge(account); em.getTransaction().commit(); handler.complete(Action.UPDATE, wrapDoc); } catch (BadRequestException bre) { + if (em != null && em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } throw bre; } catch (DocumentException de) { + if (em != null && em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } throw de; } catch (Exception e) { if (logger.isDebugEnabled()) { @@ -192,8 +207,8 @@ public class AccountStorageClient extends JpaStorageClient { //if userid gives any indication about the id provider, it should //be used to avoid the following approach - User userLocal = em.find(User.class, accountFound.getUserId()); Query usrDel = null; + User userLocal = getUser(em, accountFound); if (userLocal != null) { StringBuilder usrDelStr = new StringBuilder("DELETE FROM "); usrDelStr.append(User.class.getCanonicalName()); @@ -203,12 +218,12 @@ public class AccountStorageClient extends JpaStorageClient { usrDel.setParameter("username", accountFound.getUserId()); } em.getTransaction().begin(); - int accDelCount = accDel.executeUpdate(); - if (accDelCount != 1) { - if (em != null && em.getTransaction().isActive()) { - em.getTransaction().rollback(); - } - } +// int accDelCount = accDel.executeUpdate(); +// if (accDelCount != 1) { +// if (em != null && em.getTransaction().isActive()) { +// em.getTransaction().rollback(); +// } +// } if (userLocal != null) { int usrDelCount = usrDel.executeUpdate(); if (usrDelCount != 1) { @@ -222,9 +237,13 @@ public class AccountStorageClient extends JpaStorageClient { throw new DocumentNotFoundException(msg); } } + em.remove(accountFound); em.getTransaction().commit(); } catch (DocumentException de) { + if (em != null && em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } throw de; } catch (Exception e) { if (logger.isDebugEnabled()) { @@ -264,9 +283,8 @@ public class AccountStorageClient extends JpaStorageClient { return true; } - private User createUser(AccountsCommon account, String tenantId) { + private User createUser(AccountsCommon account) { User user = new User(); - user.setTenantid(tenantId); user.setUsername(account.getUserId()); byte[] bpass = Base64.decodeBase64(account.getPassword()); SecurityUtils.validatePassword(new String(bpass)); @@ -288,4 +306,20 @@ public class AccountStorageClient extends JpaStorageClient { } return userFound; } + +// private UserTenant createTenantAssoc(AccountsCommon account) { +// UserTenant userTenant = new UserTenant(); +// userTenant.setUserId(account.getUserId()); +// List atl = account.getTenant(); +// List utl = +// new ArrayList(); +// for (AccountsCommon.Tenant at : atl) { +// UserTenant.Tenant ut = new UserTenant.Tenant(); +// ut.setId(at.getId()); +// ut.setName(at.getName()); +// utl.add(ut); +// } +// userTenant.setTenant(utl); +// return userTenant; +// } } diff --git a/services/authentication/client/pom.xml b/services/authentication/client/pom.xml index 0c3c01822..44c8cf0fb 100644 --- a/services/authentication/client/pom.xml +++ b/services/authentication/client/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> org.collectionspace.services.authentication @@ -40,7 +40,12 @@ org.collectionspace.services.client 1.0 - + + + org.collectionspace.services + org.collectionspace.services.account.client + 1.0 + org.collectionspace.services org.collectionspace.services.collectionobject.client @@ -83,7 +88,7 @@ mysql-connector-java - + cspace-services-collectionobject-client diff --git a/services/authentication/client/src/main/resources/db/mysql/authentication.sql b/services/authentication/client/src/main/resources/db/mysql/authentication.sql index 863a09caa..a985e957e 100644 --- a/services/authentication/client/src/main/resources/db/mysql/authentication.sql +++ b/services/authentication/client/src/main/resources/db/mysql/authentication.sql @@ -1,6 +1,6 @@ drop table if exists roles; drop table if exists users; drop table if exists users_roles; -create table roles (rolename varchar(255) not null, rolegroup varchar(255) not null, tenantid varchar(255) not null, primary key (rolename)); -create table users (username varchar(255) not null, passwd varchar(128) not null, tenantid varchar(255) not null, primary key (username)); -create table users_roles (HJID bigint not null auto_increment, rolename varchar(255) not null, tenantid varchar(255) not null, username varchar(255) not null, primary key (HJID)); +create table roles (rolename varchar(255) not null, rolegroup varchar(255) not null, primary key (rolename)); +create table users (username varchar(255) not null, passwd varchar(128) not null, primary key (username)); +create table users_roles (HJID bigint not null auto_increment, rolename varchar(255) not null, username varchar(255) not null, primary key (HJID)); diff --git a/services/authentication/client/src/main/resources/db/mysql/test_authn.sql b/services/authentication/client/src/main/resources/db/mysql/test_authn.sql index dde95566a..8daa17f59 100644 --- a/services/authentication/client/src/main/resources/db/mysql/test_authn.sql +++ b/services/authentication/client/src/main/resources/db/mysql/test_authn.sql @@ -5,10 +5,11 @@ -- use cspace; -INSERT INTO `users` (`username`,`passwd`, `tenantid`) VALUES ('test','n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg=', '1'); +insert into `users` (`username`,`passwd`) VALUES ('test','n4bQgYhMfWWaL+qgxVrQFaO/TxsrC4Is0V1sFbDwCgg='); -insert into roles (rolename, rolegroup, `tenantid`) values ('collections_manager', 'collections', '1'); -insert into roles (rolename, rolegroup, `tenantid`) values ('collections_registrar', 'collections', '1'); +insert into `roles` (`rolename`, `rolegroup`) values ('kernel', 'kernel'); +insert into `roles` (`rolename`, `rolegroup`) values ('collections_manager', 'collections'); +insert into `roles` (`rolename`, `rolegroup`) values ('collections_registrar', 'collections'); -insert into users_roles(username, rolename, `tenantid`) values ('test', 'collections_manager', '1'); -insert into users_roles(username, rolename, `tenantid`) values('admin', 'collections_registrar', '1'); \ No newline at end of file +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/client/src/test/java/org/collectionspace/services/authentication/client/AuthenticationServiceTest.java b/services/authentication/client/src/test/java/org/collectionspace/services/authentication/client/AuthenticationServiceTest.java index 2ca1edb11..3ead7909a 100644 --- a/services/authentication/client/src/test/java/org/collectionspace/services/authentication/client/AuthenticationServiceTest.java +++ b/services/authentication/client/src/test/java/org/collectionspace/services/authentication/client/AuthenticationServiceTest.java @@ -22,8 +22,14 @@ */ package org.collectionspace.services.authentication.client; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.apache.commons.codec.binary.Base64; +import org.collectionspace.services.client.AccountClient; +import org.collectionspace.services.account.AccountsCommon; import org.jboss.resteasy.client.ClientResponse; import org.testng.Assert; import org.testng.annotations.Test; @@ -48,6 +54,8 @@ public class AuthenticationServiceTest extends AbstractServiceTest { /** The known resource id. */ private String knownResourceId = null; + private String barneyAccountId = null; + private String babybopAccountId = null; /** The logger. */ final Logger logger = LoggerFactory.getLogger(AuthenticationServiceTest.class); @@ -61,12 +69,69 @@ public class AuthenticationServiceTest extends AbstractServiceTest { return null; } + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class) + public void createAccounts(String testName) throws Exception { + // Perform setup, such as initializing the type of service request + // (e.g. CREATE, DELETE), its valid and expected status codes, and + // its associated HTTP method name (e.g. POST, DELETE). + setupCreate(testName); + AccountClient accountClient = new AccountClient(); + if (!accountClient.isServerSecure()) { + logger.warn("set -Dcspace.server.secure=true to run security tests"); + return; + } + accountClient.setProperty(CollectionSpaceClient.AUTH_PROPERTY, + "true"); + accountClient.setProperty(CollectionSpaceClient.USER_PROPERTY, + "test"); + accountClient.setProperty( + CollectionSpaceClient.PASSWORD_PROPERTY, "test"); + // Submit the request to the service and store the response. + AccountsCommon account = + createAccountInstance("barney", "barney08", "barney@dinoland.com", "1"); + ClientResponse res = accountClient.create(account); + int statusCode = res.getStatus(); + + if (logger.isDebugEnabled()) { + logger.debug(testName + ": barney status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + // Store the ID returned from this create operation + // for additional tests below. + barneyAccountId = extractId(res); + if (logger.isDebugEnabled()) { + logger.debug(testName + ": barneyAccountId=" + barneyAccountId); + } + + account = createAccountInstance("babybop", "babybop09", "babybop@dinoland.com", "non-existent"); + res = accountClient.create(account); + statusCode = res.getStatus(); + + if (logger.isDebugEnabled()) { + logger.debug(testName + ": babybop status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + + // Store the ID returned from this create operation + // for additional tests below. + babybopAccountId = extractId(res); + if (logger.isDebugEnabled()) { + logger.debug(testName + ": babybopAccountId=" + babybopAccountId); + } + + } + + /* (non-Javadoc) * @see org.collectionspace.services.client.test.AbstractServiceTest#create() */ - @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class) + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"createAccounts"}) @Override public void create(String testName) { + setupCreate(testName); CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); String identifier = this.createIdentifier(); MultipartOutput multipart = createCollectionObjectInstance( @@ -79,9 +144,9 @@ public class AuthenticationServiceTest extends AbstractServiceTest { collectionObjectClient.setProperty(CollectionSpaceClient.AUTH_PROPERTY, "true"); collectionObjectClient.setProperty(CollectionSpaceClient.USER_PROPERTY, - "test"); + "barney"); collectionObjectClient.setProperty( - CollectionSpaceClient.PASSWORD_PROPERTY, "test"); + CollectionSpaceClient.PASSWORD_PROPERTY, "barney08"); try { collectionObjectClient.setupHttpClient(); collectionObjectClient.setProxy(); @@ -101,10 +166,11 @@ public class AuthenticationServiceTest extends AbstractServiceTest { } /** - * Creates the collection object instance without user. + * Creates the collection object instance without password. */ - @Test(dependsOnMethods = {"create"}) - public void createWithoutUser() { + @Test(dependsOnMethods = {"createAccounts"}) + public void createWithoutPassword() { + banner("createWithoutPassword"); CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); String identifier = this.createIdentifier(); MultipartOutput multipart = createCollectionObjectInstance( @@ -115,28 +181,29 @@ public class AuthenticationServiceTest extends AbstractServiceTest { } collectionObjectClient.setProperty(CollectionSpaceClient.AUTH_PROPERTY, "true"); - collectionObjectClient.removeProperty(CollectionSpaceClient.USER_PROPERTY); - collectionObjectClient.setProperty( - CollectionSpaceClient.PASSWORD_PROPERTY, "test"); + collectionObjectClient.setProperty(CollectionSpaceClient.USER_PROPERTY, + "test"); + collectionObjectClient.removeProperty(CollectionSpaceClient.PASSWORD_PROPERTY); try { collectionObjectClient.setupHttpClient(); collectionObjectClient.setProxy(); } catch (Exception e) { - logger.error("createWithoutUser: caught " + e.getMessage()); + logger.error("createWithoutPassword: caught " + e.getMessage()); return; } ClientResponse res = collectionObjectClient.create(multipart); if (logger.isDebugEnabled()) { - logger.debug("createWithoutUser: status = " + res.getStatus()); + logger.debug("createWithoutPassword: status = " + res.getStatus()); } Assert.assertEquals(res.getStatus(), Response.Status.UNAUTHORIZED.getStatusCode(), "expected " + Response.Status.UNAUTHORIZED.getStatusCode()); } /** - * Creates the collection object instance without password. + * Creates the collection object with unknown user */ - @Test(dependsOnMethods = {"createWithoutUser"}) - public void createWithoutPassword() { + @Test(dependsOnMethods = {"createAccounts"}) + public void createWithUnknownUser() { + banner("createWithUnknownUser"); CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); String identifier = this.createIdentifier(); MultipartOutput multipart = createCollectionObjectInstance( @@ -148,18 +215,19 @@ public class AuthenticationServiceTest extends AbstractServiceTest { collectionObjectClient.setProperty(CollectionSpaceClient.AUTH_PROPERTY, "true"); collectionObjectClient.setProperty(CollectionSpaceClient.USER_PROPERTY, - "test"); - collectionObjectClient.removeProperty(CollectionSpaceClient.PASSWORD_PROPERTY); + "foo"); + collectionObjectClient.setProperty(CollectionSpaceClient.PASSWORD_PROPERTY, + "bar"); try { collectionObjectClient.setupHttpClient(); collectionObjectClient.setProxy(); } catch (Exception e) { - logger.error("createWithoutPassword: caught " + e.getMessage()); + logger.error("createWithUnknownUser: caught " + e.getMessage()); return; } ClientResponse res = collectionObjectClient.create(multipart); if (logger.isDebugEnabled()) { - logger.debug("createWithoutPassword: status = " + res.getStatus()); + logger.debug("createWithUnknownUser: status = " + res.getStatus()); } Assert.assertEquals(res.getStatus(), Response.Status.UNAUTHORIZED.getStatusCode(), "expected " + Response.Status.UNAUTHORIZED.getStatusCode()); } @@ -167,8 +235,9 @@ public class AuthenticationServiceTest extends AbstractServiceTest { /** * Creates the collection object instance with incorrect password. */ - @Test(dependsOnMethods = {"createWithoutPassword"}) + @Test(dependsOnMethods = {"createAccounts"}) public void createWithIncorrectPassword() { + banner("createWithIncorrectPassword"); CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); String identifier = this.createIdentifier(); MultipartOutput multipart = createCollectionObjectInstance( @@ -198,10 +267,11 @@ public class AuthenticationServiceTest extends AbstractServiceTest { } /** - * Creates the collection object instance without user password. + * Creates the collection object instance with incorrect user password. */ - @Test(dependsOnMethods = {"createWithoutPassword"}) - public void createWithoutUserPassword() { + @Test(dependsOnMethods = {"createAccounts"}) + public void createWithIncorrectUserPassword() { + banner("createWithIncorrectUserPassword"); CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); String identifier = this.createIdentifier(); MultipartOutput multipart = createCollectionObjectInstance( @@ -212,27 +282,31 @@ public class AuthenticationServiceTest extends AbstractServiceTest { } collectionObjectClient.setProperty(CollectionSpaceClient.AUTH_PROPERTY, "true"); - collectionObjectClient.removeProperty(CollectionSpaceClient.USER_PROPERTY); - collectionObjectClient.removeProperty(CollectionSpaceClient.PASSWORD_PROPERTY); + collectionObjectClient.setProperty(CollectionSpaceClient.USER_PROPERTY, + "foo"); + collectionObjectClient.setProperty( + CollectionSpaceClient.PASSWORD_PROPERTY, "bar"); try { collectionObjectClient.setupHttpClient(); collectionObjectClient.setProxy(); } catch (Exception e) { - logger.error("createWithoutUserPassword: caught " + e.getMessage()); + logger.error("createWithIncorrectUserPassword: caught " + e.getMessage()); return; } ClientResponse res = collectionObjectClient.create(multipart); if (logger.isDebugEnabled()) { - logger.debug("createWithoutUserPassword: status = " + res.getStatus()); + logger.debug("createWithIncorrectUserPassword: status = " + + res.getStatus()); } - Assert.assertEquals(res.getStatus(), Response.Status.FORBIDDEN.getStatusCode(), "expected " + Response.Status.FORBIDDEN.getStatusCode()); + Assert.assertEquals(res.getStatus(), Response.Status.UNAUTHORIZED.getStatusCode(), "expected " + Response.Status.UNAUTHORIZED.getStatusCode()); } /** * Creates the collection object instance with incorrect user password. */ - @Test(dependsOnMethods = {"createWithoutPassword"}) - public void createWithIncorrectUserPassword() { + @Test(dependsOnMethods = {"createAccounts"}) + public void createWithoutTenant() { + banner("createWithoutTenant"); CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); String identifier = this.createIdentifier(); MultipartOutput multipart = createCollectionObjectInstance( @@ -244,20 +318,20 @@ public class AuthenticationServiceTest extends AbstractServiceTest { collectionObjectClient.setProperty(CollectionSpaceClient.AUTH_PROPERTY, "true"); collectionObjectClient.setProperty(CollectionSpaceClient.USER_PROPERTY, - "foo"); + "babybop"); collectionObjectClient.setProperty( - CollectionSpaceClient.PASSWORD_PROPERTY, "bar"); + CollectionSpaceClient.PASSWORD_PROPERTY, "babybop09"); try { collectionObjectClient.setupHttpClient(); collectionObjectClient.setProxy(); } catch (Exception e) { - logger.error("createWithIncorrectUserPassword: caught " + e.getMessage()); + logger.error("createWithoutTenant: caught " + e.getMessage()); return; } ClientResponse res = collectionObjectClient.create(multipart); if (logger.isDebugEnabled()) { - logger.debug("createWithIncorrectUserPassword: status = " + - res.getStatus()); + logger.debug("createWithoutTenant: status = " + + res.getStatus()); } Assert.assertEquals(res.getStatus(), Response.Status.UNAUTHORIZED.getStatusCode(), "expected " + Response.Status.UNAUTHORIZED.getStatusCode()); } @@ -267,8 +341,9 @@ public class AuthenticationServiceTest extends AbstractServiceTest { */ @Override @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, - dependsOnMethods = {"createWithIncorrectUserPassword"}) + dependsOnMethods = {"create"}) public void delete(String testName) { + setupDelete(testName); CollectionObjectClient collectionObjectClient = new CollectionObjectClient(); collectionObjectClient = new CollectionObjectClient(); if (!collectionObjectClient.isServerSecure()) { @@ -299,6 +374,41 @@ public class AuthenticationServiceTest extends AbstractServiceTest { Response.Status.OK.getStatusCode(), "expected " + Response.Status.OK.getStatusCode()); } + @Test(dataProvider = "testName", dataProviderClass = AbstractServiceTest.class, + dependsOnMethods = {"delete"}) + public void deleteAccounts(String testName) throws Exception { + + // Perform setup. + setupDelete(testName); + AccountClient accountClient = new AccountClient(); + if (!accountClient.isServerSecure()) { + logger.warn("set -Dcspace.server.secure=true to run security tests"); + return; + } + accountClient.setProperty(CollectionSpaceClient.AUTH_PROPERTY, + "true"); + accountClient.setProperty(CollectionSpaceClient.USER_PROPERTY, + "test"); + accountClient.setProperty( + CollectionSpaceClient.PASSWORD_PROPERTY, "test"); + // Submit the request to the service and store the response. + ClientResponse res = accountClient.delete(barneyAccountId); + int statusCode = res.getStatus(); + if (logger.isDebugEnabled()) { + logger.debug(testName + ": barney status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + + res = accountClient.delete(babybopAccountId); + statusCode = res.getStatus(); + if (logger.isDebugEnabled()) { + logger.debug(testName + ": babybop status = " + statusCode); + } + Assert.assertTrue(REQUEST_TYPE.isValidStatusCode(statusCode), + invalidStatusCodeMessage(REQUEST_TYPE, statusCode)); + } + // --------------------------------------------------------------- // Utility methods used by tests above // --------------------------------------------------------------- @@ -342,6 +452,38 @@ public class AuthenticationServiceTest extends AbstractServiceTest { return multipart; } + private AccountsCommon createAccountInstance(String screenName, + String passwd, String email, String tenantId) { + + AccountsCommon account = new AccountsCommon(); + account.setScreenName(screenName); + account.setUserId(screenName); + account.setPassword(Base64.encodeBase64(passwd.getBytes())); + account.setEmail(email); + account.setPhone("1234567890"); + List atl = new ArrayList(); + + AccountsCommon.Tenant at = new AccountsCommon.Tenant(); + at.setId(tenantId);//for testing purposes + at.setName("movingimages.us"); + atl.add(at); + //disable 2nd tenant till tenant identification is in effect + //on the service side for 1-n user-tenants +// AccountsCommon.Tenant at2 = new AccountsCommon.Tenant(); +// at2.setId(UUID.randomUUID().toString()); +// at2.setName("collectionspace.org"); +// atl.add(at2); + account.setTenant(atl); + + if (logger.isDebugEnabled()) { + logger.debug("to be created, account common"); + logger.debug(objectAsXmlString(account, + AccountsCommon.class)); + } + return account; + + } + /* (non-Javadoc) * @see org.collectionspace.services.client.test.AbstractServiceTest#createList() */ diff --git a/services/authentication/jaxb/pom.xml b/services/authentication/jaxb/pom.xml index 5510cb666..583abbe5f 100644 --- a/services/authentication/jaxb/pom.xml +++ b/services/authentication/jaxb/pom.xml @@ -61,11 +61,6 @@ testng 5.6 - - org.collectionspace.services - org.collectionspace.services.client - 1.0 - diff --git a/services/authentication/jaxb/src/main/resources/authentication_identity_provider.xsd b/services/authentication/jaxb/src/main/resources/authentication_identity_provider.xsd index e50ed0fa7..76eca985b 100644 --- a/services/authentication/jaxb/src/main/resources/authentication_identity_provider.xsd +++ b/services/authentication/jaxb/src/main/resources/authentication_identity_provider.xsd @@ -37,19 +37,6 @@ - - - - - - - - - - - - - @@ -82,19 +69,6 @@ - - - - - - - - - - - - - @@ -127,19 +101,6 @@ - - - - - - - - - - - - - diff --git a/services/authentication/service/pom.xml b/services/authentication/service/pom.xml index d0574ba17..4ee883511 100644 --- a/services/authentication/service/pom.xml +++ b/services/authentication/service/pom.xml @@ -19,14 +19,13 @@ + - org.slf4j - slf4j-api - - - org.slf4j - slf4j-log4j12 + commons-logging + commons-logging + 1.1.1 + junit @@ -35,9 +34,7 @@ test - - - + javax.security jaas diff --git a/services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java b/services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java index 6ef9ba827..d79b54ff8 100644 --- a/services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java +++ b/services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceDBLoginModule.java @@ -23,99 +23,62 @@ */ 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.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; -import java.util.HashMap; import java.util.Map; -import javax.naming.InitialContext; -import javax.naming.NamingException; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.login.FailedLoginException; import javax.security.auth.login.LoginException; -import javax.sql.DataSource; -import org.jboss.security.SimpleGroup; -import org.jboss.security.SimplePrincipal; -import org.jboss.security.auth.spi.DatabaseServerLoginModule; +import org.jboss.security.auth.spi.UsernamePasswordLoginModule; /** * CollectionSpace default identity provider supporting multi-tenancy * @author */ -public class CSpaceDBLoginModule extends DatabaseServerLoginModule { - - protected String tenantQuery = "select tenantid from users where username=?"; +public class CSpaceDBLoginModule extends UsernamePasswordLoginModule { + + private DatabaseRealm realm; + + /** + * Initialize CSpaceDBLoginModule + * + * @param options - + * dsJndiName: The name of the DataSource of the database containing the + * Principals, Roles tables + * principalsQuery: The prepared statement query, equivalent to: + * "select Password from Principals where PrincipalID=?" + * rolesQuery: The prepared statement query, equivalent to: + * "select Role, RoleGroup from Roles where PrincipalID=?" + * tenantsQuery: + * "select TenantId, TenantName, TenantGroup from Tenants where PrincipalID=?" + */ + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map sharedState, Map options) { + super.initialize(subject, callbackHandler, sharedState, options); + realm = new DatabaseRealm(options); + } //disabled due to classloading problem // private Logger logger = LoggerFactory.getLogger(CSpaceDBLoginModule.class); - private String tenantId; protected String getUsersPassword() throws LoginException { String username = getUsername(); String password = null; - Connection conn = null; - PreparedStatement ps = null; - ResultSet rs = null; try { - conn = getConnection(); - // Get the password - if (log.isDebugEnabled()) { - log.debug("Executing query: " + principalsQuery + ", with username: " + username); - } - ps = conn.prepareStatement(principalsQuery); - ps.setString(1, username); - rs = ps.executeQuery(); - if (rs.next() == false) { - if (log.isDebugEnabled()) { - log.debug(principalsQuery + " returned no matches from db"); - } - throw new FailedLoginException("No matching username found"); - } - - password = rs.getString(1); + password = realm.getUsersPassword(username); password = convertRawPassword(password); if (log.isDebugEnabled()) { log.debug("Obtained user password"); } - tenantId = rs.getString(2); - if (log.isDebugEnabled()) { - log.debug("Obtained tenantId"); - } - CSpacePrincipal principal = (CSpacePrincipal)getIdentity(); - principal.setTenantId(tenantId); - } catch (SQLException ex) { - LoginException le = new LoginException("Query failed"); - le.initCause(ex); - throw le; + } catch (LoginException lex) { + throw lex; } 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) { - } - } } return password; } @@ -127,126 +90,30 @@ public class CSpaceDBLoginModule extends DatabaseServerLoginModule { */ protected Group[] getRoleSets() throws LoginException { String username = getUsername(); - if (log.isDebugEnabled()) { - log.debug("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username); - } - - Connection conn = null; - HashMap setsMap = new HashMap(); - PreparedStatement ps = null; - ResultSet rs = null; - - try { - conn = getConnection(); - // Get the user role names - if (log.isDebugEnabled()) { - log.debug("Executing query: " + rolesQuery + ", with username: " + username); - } - ps = conn.prepareStatement(rolesQuery); - try { - ps.setString(1, username); - ps.setString(2, tenantId); - } catch (ArrayIndexOutOfBoundsException ignore) { - // The query may not have any parameters so just try it - } - rs = ps.executeQuery(); - if (rs.next() == false) { - if (log.isDebugEnabled()) { - log.debug("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.isDebugEnabled()) { - log.debug("Assign user to role " + name); - } - - group.addMember(p); - } catch (Exception e) { - log.error("Failed to create principal: " + name + " " + e.toString()); - } - - } while (rs.next()); - } 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) { - } - } + Collection roles = realm.getRoles(username, + "org.collectionspace.authentication.CSpacePrincipal", + "org.jboss.security.SimpleGroup"); - } + Collection tenants = realm.getTenants(username, + "org.collectionspace.authentication.CSpacePrincipal", + "org.jboss.security.SimpleGroup"); - Group[] roleSets = new Group[setsMap.size()]; - setsMap.values().toArray(roleSets); + List all = new ArrayList(); + all.addAll(roles); + all.addAll(tenants); + Group[] roleSets = new Group[all.size()]; + all.toArray(roleSets); return roleSets; } - - private Connection getConnection() throws LoginException, SQLException { - InitialContext ctx = null; - Connection conn = null; - try { - ctx = new InitialContext(); - DataSource ds = (DataSource) ctx.lookup(dsJndiName); - if (ds == null) { - throw new IllegalArgumentException("datasource not found: " + dsJndiName); - } - conn = ds.getConnection(); - return conn; - } catch (NamingException ex) { - LoginException le = new LoginException("Error looking up DataSource from: " + dsJndiName); - le.initCause(ex); - throw le; - } finally { - if (ctx != null) { - try { - ctx.close(); - } catch (Exception e) { - } - } - } - + /** A hook to allow subclasses to convert a password from the database + into a plain text string or whatever form is used for matching against + the user input. It is called from within the getUsersPassword() method. + @param rawPassword - the password as obtained from the database + @return the argument rawPassword + */ + protected String convertRawPassword(String rawPassword) { + return rawPassword; } } diff --git a/services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceTenant.java b/services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceTenant.java new file mode 100644 index 000000000..5b4565e1f --- /dev/null +++ b/services/authentication/service/src/main/java/org/collectionspace/authentication/CSpaceTenant.java @@ -0,0 +1,169 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + + * http://www.collectionspace.org + * http://wiki.collectionspace.org + + * 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. + + * You may obtain a copy of the ECL 2.0 License at + + * https://source.collectionspace.org/collection-space/LICENSE.txt + + * 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.collectionspace.authentication; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; + +/** + * A CSpace Principal representing a tenant + * A class derived (in principle) from JBoss SimpleGroup and SimplePrincipal + * @author + */ +public class CSpaceTenant implements Group, Cloneable { + + /** The serialVersionUID */ + private static final long serialVersionUID = 1L; + private String name; + private String id; + private HashMap members = new HashMap(3); + + public CSpaceTenant(String name, String id) { + if(name == null || id == null) { + String msg = "CSpaceTenant: invalid argument(s), can't be null" + + "name=" + name + " id=" + id; + throw new IllegalArgumentException(msg); + } + this.name = name; + this.id = id; + } + + @Override + public String getName() { + return name; + } + + public String getId() { + return id; + } + + /** + * Adds the specified member to the tenant. + * @param user the principal to add to this tenant. + * @return true if the member was successfully added, + * false if the principal was already a member. + */ + @Override + public boolean addMember(Principal user) { + boolean isMember = members.containsKey(user); + if (isMember == false) { + members.put(user, user); + } + return isMember == false; + } + + /** + * isMember returns true if the passed principal is a member of the tenant. + * This method does a recursive search, so if a principal belongs to a + * tenant which is a member of this tenant, true is returned. + * @param member the principal whose membership is to be checked. + * @return true if the principal is a member of this tenant, false otherwise. + */ + @Override + public boolean isMember(Principal member) { + // First see if there is a key with the member name + boolean isMember = members.containsKey(member); + if (isMember == false) { // Check any tenants for membership + Collection values = members.values(); + Iterator iter = values.iterator(); + while (isMember == false && iter.hasNext()) { + Object next = iter.next(); + if (next instanceof Group) { + Group tenant = (Group) next; + isMember = tenant.isMember(member); + } + } + } + return isMember; + } + + /** + * members returns an enumeration of the members in the tenant. + * The returned objects can be instances of either Principal + * or Group (which is a subinterface of Principal). + * @return an enumeration of the tenant members. + */ + @Override + public Enumeration members() { + return Collections.enumeration(members.values()); + } + + /** + * removeMember removes the specified member from the tenant. + * @param user the principal to remove from this tenant. + * @return true if the principal was removed, or + * false if the principal was not a member. + */ + @Override + public boolean removeMember(Principal user) { + Object prev = members.remove(user); + return prev != null; + } + + /** + * Compare this tenant against another tenant + * @return true if name equals another.getName(); + */ + @Override + public boolean equals(Object another) { + if (!(another instanceof CSpaceTenant)) { + return false; + } + String anotherName = ((CSpaceTenant) another).getName(); + String anotherId = ((CSpaceTenant) another).getId(); + return name.equals(anotherName) && id.equals(anotherId); + } + + @Override + public int hashCode() { + return (name + id).hashCode(); + } + + @Override + public String toString() { + StringBuffer tmp = new StringBuffer(getName()); + tmp.append("(members:"); + Iterator iter = members.keySet().iterator(); + while (iter.hasNext()) { + tmp.append(iter.next()); + tmp.append(','); + } + tmp.setCharAt(tmp.length() - 1, ')'); + return tmp.toString(); + } + + @Override + public synchronized Object clone() throws CloneNotSupportedException { + CSpaceTenant clone = (CSpaceTenant) super.clone(); + if (clone != null) { + clone.members = (HashMap) this.members.clone(); + } + return clone; + } +} diff --git a/services/authentication/service/src/main/java/org/collectionspace/authentication/DatabaseRealm.java b/services/authentication/service/src/main/java/org/collectionspace/authentication/DatabaseRealm.java new file mode 100644 index 000000000..39147afd2 --- /dev/null +++ b/services/authentication/service/src/main/java/org/collectionspace/authentication/DatabaseRealm.java @@ -0,0 +1,469 @@ +/** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + + * http://www.collectionspace.org + * http://wiki.collectionspace.org + + * 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. + + * You may obtain a copy of the ECL 2.0 License at + + * https://source.collectionspace.org/collection-space/LICENSE.txt + + * 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. + *//** + * This document is a part of the source code and related artifacts + * for CollectionSpace, an open source collections management system + * for museums and related institutions: + + * http://www.collectionspace.org + * http://wiki.collectionspace.org + + * 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. + + * You may obtain a copy of the ECL 2.0 License at + + * https://source.collectionspace.org/collection-space/LICENSE.txt + + * 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. + */ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +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.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import javax.sql.DataSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * DatabaseRealm provides access to user, password, role, tenant database + * @author + */ +public class DatabaseRealm { + + private static Log log = LogFactory.getLog(DatabaseRealm.class); + private String datasourceName; + private String principalsQuery; + private String rolesQuery; + private String tenantsQuery; + private boolean suspendResume; + + /** + * create DatabaseRelam + * @param datasourceName datasource name + */ + public DatabaseRealm(Map options) { + datasourceName = (String) options.get("dsJndiName"); + if (datasourceName == null) { + datasourceName = "java:/DefaultDS"; + } + Object tmp = options.get("principalsQuery"); + if (tmp != null) { + principalsQuery = tmp.toString(); + } + tmp = options.get("rolesQuery"); + if (tmp != null) { + rolesQuery = tmp.toString(); + } + tmp = options.get("tenantsQuery"); + if (tmp != null) { + tenantsQuery = tmp.toString(); + } + tmp = options.get("suspendResume"); + if (tmp != null) { + suspendResume = Boolean.valueOf(tmp.toString()).booleanValue(); + } + if (log.isTraceEnabled()) { + log.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName); + log.trace("principalsQuery=" + principalsQuery); + log.trace("rolesQuery=" + rolesQuery); + log.trace("suspendResume=" + suspendResume); + } + + } + + String getUsersPassword(String username) throws LoginException { + + String password = null; + Connection conn = null; + PreparedStatement ps = null; + ResultSet rs = null; + try { + conn = getConnection(); + // Get the password + if (log.isDebugEnabled()) { + log.debug("Executing query: " + principalsQuery + ", with username: " + username); + } + ps = conn.prepareStatement(principalsQuery); + ps.setString(1, username); + rs = ps.executeQuery(); + if (rs.next() == false) { + if (log.isDebugEnabled()) { + log.debug(principalsQuery + " returned no matches from db"); + } + throw new FailedLoginException("No matching username found"); + } + + password = rs.getString(1); + } 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) { + } + } + } + return password; + } + + /** + * Execute the rolesQuery against the datasourceName to obtain the roles for + * the authenticated user. + * @return collection containing the roles + */ + Collection getRoles(String username, String principalClassName, String groupClassName) throws LoginException { + + if (log.isDebugEnabled()) { + log.debug("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username); + } + + Connection conn = null; + HashMap groupsMap = new HashMap(); + PreparedStatement ps = null; + ResultSet rs = null; + + try { + conn = getConnection(); + // Get the user role names + if (log.isDebugEnabled()) { + log.debug("Executing 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.isDebugEnabled()) { + log.debug("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 g = createGroup(groupClassName, "Roles"); + groupsMap.put(g.getName(), g); + return groupsMap.values(); + } + + do { + String roleName = rs.getString(1); + String groupName = rs.getString(2); + if (groupName == null || groupName.length() == 0) { + groupName = "Roles"; + } + + Group group = (Group) groupsMap.get(groupName); + if (group == null) { + group = createGroup(groupClassName, groupName); + groupsMap.put(groupName, group); + } + + try { + Principal p = createPrincipal(principalClassName, roleName); + if (log.isDebugEnabled()) { + log.debug("Assign user to role " + roleName); + } + + group.addMember(p); + } catch (Exception e) { + log.error("Failed to create principal: " + roleName + " " + e.toString()); + } + + } while (rs.next()); + } catch (SQLException ex) { + LoginException le = new LoginException("Query failed"); + le.initCause(ex); + throw le; + } catch (Exception e) { + LoginException le = new LoginException("unknown exception"); + le.initCause(e); + 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) { + } + } + + } + + return groupsMap.values(); + + } + + /** + * Execute the tenantsQuery against the datasourceName to obtain the tenants for + * the authenticated user. + * @return collection containing the roles + */ + Collection getTenants(String username, String principalClassName, String groupClassName) throws LoginException { + + if (log.isDebugEnabled()) { + log.debug("getTenants using tenantsQuery: " + tenantsQuery + ", username: " + username); + } + + Connection conn = null; + HashMap groupsMap = new HashMap(); + PreparedStatement ps = null; + ResultSet rs = null; + + try { + conn = getConnection(); + // Get the user role names + if (log.isDebugEnabled()) { + log.debug("Executing query: " + tenantsQuery + ", with username: " + username); + } + + ps = conn.prepareStatement(tenantsQuery); + 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.isDebugEnabled()) { + log.debug("No tenants found"); + } + // We are running with an unauthenticatedIdentity so create an + // empty Tenants set and return. + // FIXME should this be allowed? + Group g = createGroup(groupClassName, "Tenants"); + groupsMap.put(g.getName(), g); + return groupsMap.values(); + } + + do { + String tenantId = rs.getString(1); + String tenantName = rs.getString(2); + String groupName = rs.getString(3); + if (groupName == null || groupName.length() == 0) { + groupName = "Tenants"; + } + + Group group = (Group) groupsMap.get(groupName); + if (group == null) { + group = createGroup(groupClassName, groupName); + groupsMap.put(groupName, group); + } + + try { + Principal p = createTenant(tenantName, tenantId); + if (log.isDebugEnabled()) { + log.debug("Assign user to tenant " + tenantName); + } + + group.addMember(p); + } catch (Exception e) { + log.error("Failed to create tenant: " + tenantName + " " + e.toString()); + } + } while (rs.next()); + } catch (SQLException ex) { + LoginException le = new LoginException("Query failed"); + le.initCause(ex); + throw le; + } catch (Exception e) { + LoginException le = new LoginException("unknown exception"); + le.initCause(e); + 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) { + } + } + + } + + return groupsMap.values(); + } + + private CSpaceTenant createTenant(String name, String id) throws Exception { + return new CSpaceTenant(name, id); + } + + private Group createGroup(String groupClassName, String name) throws Exception { + return (Group) createPrincipal(groupClassName, name); + } + + private Principal createPrincipal(String principalClassName, String name) throws Exception { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class clazz = loader.loadClass(principalClassName); + Class[] ctorSig = {String.class}; + Constructor ctor = clazz.getConstructor(ctorSig); + Object[] ctorArgs = {name}; + Principal p = (Principal) ctor.newInstance(ctorArgs); + return p; + } + + private Connection getConnection() throws LoginException, SQLException { + InitialContext ctx = null; + Connection conn = null; + try { + ctx = new InitialContext(); + DataSource ds = (DataSource) ctx.lookup(getDataSourceName()); + if (ds == null) { + throw new IllegalArgumentException("datasource not found: " + getDataSourceName()); + } + conn = ds.getConnection(); + return conn; + } catch (NamingException ex) { + LoginException le = new LoginException("Error looking up DataSource from: " + getDataSourceName()); + le.initCause(ex); + throw le; + } finally { + if (ctx != null) { + try { + ctx.close(); + } catch (Exception e) { + } + } + } + + } + + /** + * @return the datasourceName + */ + public String getDataSourceName() { + return datasourceName; + } + + /** + * @return the principalQuery + */ + public String getPrincipalQuery() { + return principalsQuery; + } + + /** + * @param principalQuery the principalQuery to set + */ + public void setPrincipalQuery(String principalQuery) { + this.principalsQuery = principalQuery; + } + + /** + * @return the roleQuery + */ + public String getRoleQuery() { + return rolesQuery; + } + + /** + * @param roleQuery the roleQuery to set + */ + public void setRoleQuery(String roleQuery) { + this.rolesQuery = roleQuery; + } + + /** + * @return the tenantQuery + */ + public String getTenantQuery() { + return tenantsQuery; + } + + /** + * @param tenantQuery the tenantQuery to set + */ + public void setTenantQuery(String tenantQuery) { + this.tenantsQuery = tenantQuery; + } +} diff --git a/services/authentication/service/src/main/resources/config/jboss-login-config.xml b/services/authentication/service/src/main/resources/config/jboss-login-config.xml index 9d4221a70..53fd571c3 100644 --- a/services/authentication/service/src/main/resources/config/jboss-login-config.xml +++ b/services/authentication/service/src/main/resources/config/jboss-login-config.xml @@ -4,10 +4,10 @@ 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. - + Document : jboss-login-config.xml Created on : June 19, 2009, 9:58 AM - Author : + Author : Description: jboss config for JAAS DatabaseServerLoginModule --> @@ -19,16 +19,19 @@ copy before the "other" application-policy + flag="required"> CspaceDS SHA-256 false org.collectionspace.authentication.CSpacePrincipal - select passwd, tenantid from users where username=? + select passwd from users where username=? - select rolename, 'Roles' from users_roles where username=? and tenantid=? + select rolename, 'Roles' from users_roles where username=? + + + select id, name, 'Tenants' from accounts_common a, tenants as t where a.userid=? and a.csid = t.TENANT_ACCOUNTSCOMMON_CSID diff --git a/services/build.xml b/services/build.xml index e36b150ba..02d4002d4 100644 --- a/services/build.xml +++ b/services/build.xml @@ -3,7 +3,7 @@ collectionspace services main - + @@ -17,8 +17,8 @@ - + description="Package CollectionSpace Services" /> + @@ -29,7 +29,7 @@ - + @@ -45,7 +45,7 @@ + description="Install" /> @@ -70,7 +70,7 @@ + description="Delete target directories" > @@ -105,7 +105,7 @@ + description="create service-specific tables(s), indices, etc."> @@ -113,9 +113,9 @@ - + description="deploy services in ${jboss.server.cspace}"> + @@ -129,7 +129,7 @@ + description="undeploy services from ${jboss.server.cspace}"> @@ -139,20 +139,21 @@ - + + description="deploy services in running ${jboss.server.cspace}"> + description="create distribution for services"> + @@ -161,11 +162,11 @@ - + - + description="create distribution for CollectionSpace installer"> + @@ -173,7 +174,7 @@ - - + diff --git a/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTest.java b/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTest.java index d5fc0bbf2..235d6b525 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTest.java +++ b/services/client/src/main/java/org/collectionspace/services/client/test/AbstractServiceTest.java @@ -66,7 +66,7 @@ public abstract class AbstractServiceTest implements ServiceTest { protected final String NON_EXISTENT_ID = createNonExistentIdentifier(); // The HTTP status code expected to be returned in the response, // from a request made to a service (where relevant). - int EXPECTED_STATUS_CODE = 0; + protected int EXPECTED_STATUS_CODE = 0; // The generic type of service request being tested // (e.g. CREATE, UPDATE, DELETE). // @@ -654,7 +654,7 @@ public abstract class AbstractServiceTest implements ServiceTest { return sb.toString(); } - private void banner(String label) { + protected void banner(String label) { if(logger.isDebugEnabled()){ logger.debug("==================================================="); logger.debug(" Test = " + label); diff --git a/services/client/src/main/java/org/collectionspace/services/client/test/ServiceRequestType.java b/services/client/src/main/java/org/collectionspace/services/client/test/ServiceRequestType.java index 87363d4bb..92de8f039 100644 --- a/services/client/src/main/java/org/collectionspace/services/client/test/ServiceRequestType.java +++ b/services/client/src/main/java/org/collectionspace/services/client/test/ServiceRequestType.java @@ -20,7 +20,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.collectionspace.services.client.test; import java.util.Arrays; @@ -40,194 +39,212 @@ import org.slf4j.LoggerFactory; * $LastChangedDate$ */ public enum ServiceRequestType { - + // Define each of the service request types and their valid HTTP status codes. - CREATE { + @Override - public int[] validStatusCodes() { - final int[] STATUS_CODES = { - Response.Status.CREATED.getStatusCode(), - Response.Status.BAD_REQUEST.getStatusCode(), - Response.Status.FORBIDDEN.getStatusCode(), - Response.Status.CONFLICT.getStatusCode(), - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() - }; - Arrays.sort(STATUS_CODES); - return STATUS_CODES; + public int[] validStatusCodes() { + final int[] STATUS_CODES = { + Response.Status.CREATED.getStatusCode(), + Response.Status.BAD_REQUEST.getStatusCode(), + Response.Status.UNAUTHORIZED.getStatusCode(), + Response.Status.FORBIDDEN.getStatusCode(), + Response.Status.CONFLICT.getStatusCode(), + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() + }; + Arrays.sort(STATUS_CODES); + return STATUS_CODES; } + @Override public boolean isValidStatusCode(int statusCode) { - if (Arrays.binarySearch(CREATE.validStatusCodes(), statusCode) >= 0) { - return true; - } else { - return false; - } + if (Arrays.binarySearch(CREATE.validStatusCodes(), statusCode) >= 0) { + return true; + } else { + return false; + } } + @Override public String validStatusCodesAsString() { - return Arrays.toString(CREATE.validStatusCodes()); + return Arrays.toString(CREATE.validStatusCodes()); } + @Override public String httpMethodName() { return javax.ws.rs.HttpMethod.POST; } - }, // Note that commas are required at the end of each enum block, except the last. - - + }, // Note that commas are required at the end of each enum block, except the last. + READ { + @Override - public int[] validStatusCodes() { - final int[] STATUS_CODES = { - Response.Status.OK.getStatusCode(), - Response.Status.FORBIDDEN.getStatusCode(), - Response.Status.NOT_FOUND.getStatusCode(), - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() - }; - Arrays.sort(STATUS_CODES); - return STATUS_CODES; + public int[] validStatusCodes() { + final int[] STATUS_CODES = { + Response.Status.OK.getStatusCode(), + Response.Status.UNAUTHORIZED.getStatusCode(), + Response.Status.FORBIDDEN.getStatusCode(), + Response.Status.NOT_FOUND.getStatusCode(), + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() + }; + Arrays.sort(STATUS_CODES); + return STATUS_CODES; } + @Override public boolean isValidStatusCode(int statusCode) { - if (Arrays.binarySearch(READ.validStatusCodes(), statusCode) >= 0) { - return true; - } else { - return false; - } + if (Arrays.binarySearch(READ.validStatusCodes(), statusCode) >= 0) { + return true; + } else { + return false; + } } + @Override public String validStatusCodesAsString() { - return Arrays.toString(READ.validStatusCodes()); + return Arrays.toString(READ.validStatusCodes()); } + @Override public String httpMethodName() { return javax.ws.rs.HttpMethod.GET; } }, - - READ_LIST { + @Override - public int[] validStatusCodes() { - final int[] STATUS_CODES = { - Response.Status.OK.getStatusCode(), - Response.Status.BAD_REQUEST.getStatusCode(), - Response.Status.FORBIDDEN.getStatusCode(), - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() - }; - Arrays.sort(STATUS_CODES); - return STATUS_CODES; + public int[] validStatusCodes() { + final int[] STATUS_CODES = { + Response.Status.OK.getStatusCode(), + Response.Status.BAD_REQUEST.getStatusCode(), + Response.Status.UNAUTHORIZED.getStatusCode(), + Response.Status.FORBIDDEN.getStatusCode(), + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() + }; + Arrays.sort(STATUS_CODES); + return STATUS_CODES; } + @Override public boolean isValidStatusCode(int statusCode) { - if (Arrays.binarySearch(READ_LIST.validStatusCodes(), statusCode) >= 0) { - return true; - } else { - return false; - } + if (Arrays.binarySearch(READ_LIST.validStatusCodes(), statusCode) >= 0) { + return true; + } else { + return false; + } } + @Override public String validStatusCodesAsString() { - return Arrays.toString(READ_LIST.validStatusCodes()); + return Arrays.toString(READ_LIST.validStatusCodes()); } + @Override public String httpMethodName() { return javax.ws.rs.HttpMethod.GET; } }, - - UPDATE { + @Override - public int[] validStatusCodes() { - final int[] STATUS_CODES = { - Response.Status.OK.getStatusCode(), - Response.Status.BAD_REQUEST.getStatusCode(), - Response.Status.FORBIDDEN.getStatusCode(), - Response.Status.NOT_FOUND.getStatusCode(), - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() - }; - Arrays.sort(STATUS_CODES); - return STATUS_CODES; + public int[] validStatusCodes() { + final int[] STATUS_CODES = { + Response.Status.OK.getStatusCode(), + Response.Status.BAD_REQUEST.getStatusCode(), + Response.Status.UNAUTHORIZED.getStatusCode(), + Response.Status.FORBIDDEN.getStatusCode(), + Response.Status.NOT_FOUND.getStatusCode(), + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() + }; + Arrays.sort(STATUS_CODES); + return STATUS_CODES; } + @Override public boolean isValidStatusCode(int statusCode) { - if (Arrays.binarySearch(UPDATE.validStatusCodes(), statusCode) >= 0) { - return true; - } else { - return false; - } + if (Arrays.binarySearch(UPDATE.validStatusCodes(), statusCode) >= 0) { + return true; + } else { + return false; + } } + @Override public String validStatusCodesAsString() { - return Arrays.toString(UPDATE.validStatusCodes()); + return Arrays.toString(UPDATE.validStatusCodes()); } + @Override public String httpMethodName() { return javax.ws.rs.HttpMethod.PUT; } }, - - DELETE { + @Override - public int[] validStatusCodes() { - final int[] STATUS_CODES = { - Response.Status.OK.getStatusCode(), - Response.Status.FORBIDDEN.getStatusCode(), - Response.Status.NOT_FOUND.getStatusCode(), - Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() - }; - Arrays.sort(STATUS_CODES); - return STATUS_CODES; + public int[] validStatusCodes() { + final int[] STATUS_CODES = { + Response.Status.OK.getStatusCode(), + Response.Status.UNAUTHORIZED.getStatusCode(), + Response.Status.FORBIDDEN.getStatusCode(), + Response.Status.NOT_FOUND.getStatusCode(), + Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() + }; + Arrays.sort(STATUS_CODES); + return STATUS_CODES; } + @Override public boolean isValidStatusCode(int statusCode) { - if (Arrays.binarySearch(DELETE.validStatusCodes(), statusCode) >= 0) { - return true; - } else { - return false; - } + if (Arrays.binarySearch(DELETE.validStatusCodes(), statusCode) >= 0) { + return true; + } else { + return false; + } } + @Override public String validStatusCodesAsString() { - return Arrays.toString(DELETE.validStatusCodes()); + return Arrays.toString(DELETE.validStatusCodes()); } + @Override public String httpMethodName() { return javax.ws.rs.HttpMethod.DELETE; } }, - - // Used by guard code. NON_EXISTENT { + @Override - public int[] validStatusCodes() { - final int[] STATUS_CODES = { 0 }; - Arrays.sort(STATUS_CODES); - return STATUS_CODES; + public int[] validStatusCodes() { + final int[] STATUS_CODES = {0}; + Arrays.sort(STATUS_CODES); + return STATUS_CODES; } + @Override public boolean isValidStatusCode(int statusCode) { - return false; + return false; } + @Override public String validStatusCodesAsString() { - return Arrays.toString(NON_EXISTENT.validStatusCodes()); + return Arrays.toString(NON_EXISTENT.validStatusCodes()); } + @Override public String httpMethodName() { - return ""; + return ""; } }; - + // Template methods to be implemented by each ServiceRequestType. - public abstract int[] validStatusCodes(); - + public abstract boolean isValidStatusCode(int statusCode); - + public abstract String validStatusCodesAsString(); public abstract String httpMethodName(); diff --git a/services/common/pom.xml b/services/common/pom.xml index e9b0f3eee..44214b6ee 100644 --- a/services/common/pom.xml +++ b/services/common/pom.xml @@ -11,40 +11,8 @@ jar 1.0 services.common - + - - com.sun.xml.bind - jaxb-impl - - - org.jvnet.jaxb2-commons - property-listener-injector - - - org.jvnet.jaxb2_commons - runtime - - - org.jboss.resteasy - resteasy-jaxrs - - - tjws - webserver - - - - - org.jboss.resteasy - resteasy-jaxb-provider - 1.1.GA - - - org.jboss.resteasy - resteasy-multipart-provider - 1.1.GA - @@ -61,7 +29,7 @@ org.slf4j slf4j-log4j12 - + javax.servlet @@ -69,7 +37,7 @@ 2.5 provided - + javax.security jaas @@ -80,7 +48,7 @@ javax.security jacc 1.0 - provided + provided + + mysql + mysql-connector-java + javax.persistence persistence-api + + + com.sun.xml.bind + jaxb-impl + + + org.jvnet.jaxb2-commons + property-listener-injector + + + org.jvnet.jaxb2_commons + runtime + + + + + org.jboss.resteasy + resteasy-jaxrs + + + tjws + webserver + + + + + org.jboss.resteasy + resteasy-jaxb-provider + 1.1.GA + + + org.jboss.resteasy + resteasy-multipart-provider + 1.1.GA + + org.hibernate hibernate-entitymanager @@ -189,15 +197,30 @@ 1.1.1 - + + org.collectionspace.services + org.collectionspace.services.authentication.jaxb + 1.0 + provided + + + org.collectionspace.services + org.collectionspace.services.authentication.service + 1.0 + provided + - + collectionspace-services-common org.jvnet.jaxb2.maven2 maven-jaxb2-plugin + + 0.7.0 @@ -211,23 +234,23 @@ -XtoString -Xinject-listener-code - - org.jvnet.jaxb2_commons + org.jvnet.jaxb2_commons basic 0.4.1 - org.jvnet.jaxb2-commons + org.jvnet.jaxb2-commons - property-listener-injector + property-listener-injector 1.0 diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java index d67d68460..c0a4fc1c9 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/AbstractServiceContext.java @@ -23,9 +23,8 @@ */ package org.collectionspace.services.common.context; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.security.Principal; +import java.security.acl.Group; +import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,6 +32,7 @@ import java.util.Set; import javax.security.auth.Subject; import javax.security.jacc.PolicyContext; import javax.security.jacc.PolicyContextException; +import org.collectionspace.authentication.CSpaceTenant; import org.collectionspace.services.common.ClientType; import org.collectionspace.services.common.ServiceMain; @@ -66,29 +66,29 @@ public abstract class AbstractServiceContext ServiceMain.getInstance().getTenantBindingConfigReader(); //FIXME retrieveTenantId is not working consistently in non-auth mode //TODO: get tenant binding from security context - String tenantId = null; //retrieveTenantId(); + String tenantId = retrieveTenantId(); if (tenantId == null) { //for testing purposes tenantId = "1"; //hardcoded for movingimages.us } tenantBinding = tReader.getTenantBinding(tenantId); if (tenantBinding == null) { - String msg = "No tenant binding found while processing request for " + - serviceName; + String msg = "No tenant binding found for tenantId=" + tenantId + + " while processing request for service= " + serviceName; logger.error(msg); throw new IllegalStateException(msg); } serviceBinding = tReader.getServiceBinding(tenantId, serviceName); if (serviceBinding == null) { - String msg = "No service binding found while processing request for " + - serviceName + " for tenant id=" + getTenantId() + - " name=" + getTenantName(); + String msg = "No service binding found while processing request for " + + serviceName + " for tenant id=" + getTenantId() + + " name=" + getTenantName(); logger.error(msg); throw new IllegalStateException(msg); } if (logger.isDebugEnabled()) { - logger.debug("tenantId=" + tenantId + - " service binding=" + serviceBinding.getName()); + logger.debug("tenantId=" + tenantId + + " service binding=" + serviceBinding.getName()); } } @@ -225,18 +225,18 @@ public abstract class AbstractServiceContext String tenantId = null; Subject caller = null; - Set principals = null; + Set groups = null; try { caller = (Subject) PolicyContext.getContext(SUBJECT_CONTEXT_KEY); if (caller == null) { //logger.warn("security not enabled..."); return tenantId; } - principals = caller.getPrincipals(Principal.class); - if (principals != null && principals.size() == 0) { + groups = caller.getPrincipals(Group.class); + if (groups != null && groups.size() == 0) { //TODO: find out why subject is not null if (logger.isDebugEnabled()) { - logger.debug("weird case where subject is not null and there are no principals"); + logger.debug("no tenant(s) found!"); } return tenantId; } @@ -245,20 +245,21 @@ public abstract class AbstractServiceContext logger.error(msg, pce); throw new UnauthorizedException(msg); } - for (Principal p : principals) { - try { - Method m = p.getClass().getMethod("getTenantId"); - Object r = m.invoke(p); - if (logger.isDebugEnabled()) { - logger.debug("retrieved tenantid=" + r + - " for principal=" + p.getName()); + for (Group g : groups) { + if ("Tenants".equals(g.getName())) { + Enumeration members = g.members(); + while (members.hasMoreElements()) { + CSpaceTenant tenant = (CSpaceTenant) members.nextElement(); + tenantId = tenant.getId(); + if (logger.isDebugEnabled()) { + logger.debug("found tenant id=" + tenant.getId() + + " name=" + tenant.getName()); + } } - tenantId = (String) r; - break; - } catch (Exception e) { - //continue with next principal } } + //TODO: if a user is associated with more than one tenants, the tenant + //id should be matched with sent over the wire if (tenantId == null) { String msg = "Could not find tenant context"; logger.error(msg); diff --git a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java index b3e8e3adc..cbe2dd3ab 100644 --- a/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java +++ b/services/common/src/main/java/org/collectionspace/services/common/context/ServiceContext.java @@ -45,7 +45,7 @@ public interface ServiceContext { public static final String PART_COMMON_LABEL = "common"; /** - * getTenantId get tenant id + * getTenantId get id of tenant for which service is accessed * @return tenant id */ public String getTenantId(); diff --git a/services/pom.xml b/services/pom.xml index 98a2e96c7..d8e530a07 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -13,7 +13,6 @@ services.main - authentication common