From cda53f8c2658132d32d181f11c7ea38f6fecc38e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Kl=C3=A4ger?= Date: Wed, 6 Dec 2017 08:42:00 +0100 Subject: [PATCH] Enable JAAS-Subjects in unit tests and REST Service - Replace the publicCredentials in CurrentUserContext by Principals - Add a TaskanaPrincipal interface that extends the Principal interface by groupNames - Update the CustomAuthenticationProvider with a taskanaPrincipal for the jaasAuthenticationTokens login context - Update the tests in TaskServiceImplIntExplicitTest by a subject - Add a SamplePrincipal class for the unit test - Add workbasket-access-list into taskana-core to add sample data to the database --- .../taskana/security/CurrentUserContext.java | 25 +++- .../taskana/security/TaskanaPrincipal.java | 13 ++ .../TaskServiceImplIntExplicitTest.java | 116 ++++++++++++++++-- .../pro/taskana/security/SamplePrincipal.java | 34 +++++ .../resources/sql/workbasket-access-list.sql | 3 + .../security/CustomAutenticationProvider.java | 30 ++++- 6 files changed, 201 insertions(+), 20 deletions(-) create mode 100644 lib/taskana-core/src/main/java/pro/taskana/security/TaskanaPrincipal.java create mode 100644 lib/taskana-core/src/test/java/pro/taskana/security/SamplePrincipal.java create mode 100644 lib/taskana-core/src/test/resources/sql/workbasket-access-list.sql diff --git a/lib/taskana-core/src/main/java/pro/taskana/security/CurrentUserContext.java b/lib/taskana-core/src/main/java/pro/taskana/security/CurrentUserContext.java index 5514f7b14..0eae82956 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/security/CurrentUserContext.java +++ b/lib/taskana-core/src/main/java/pro/taskana/security/CurrentUserContext.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import javax.security.auth.Subject; import java.lang.reflect.Method; import java.security.AccessController; +import java.security.Principal; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -90,11 +91,11 @@ public final class CurrentUserContext { Subject subject = Subject.getSubject(AccessController.getContext()); LOGGER.debug("Subject of caller: {}", subject); if (subject != null) { - Set publicCredentials = subject.getPublicCredentials(); - LOGGER.debug("Public credentials of caller: {}", publicCredentials); - for (Object pC : publicCredentials) { - LOGGER.debug("Returning the first public credential: {}", pC.toString()); - return pC.toString(); + Set principals = subject.getPrincipals(); + LOGGER.debug("Public principals of caller: {}", principals); + for (Principal pC : principals) { + LOGGER.debug("Returning the first public principal: {}", pC.getName()); + return pC.getName(); } } LOGGER.debug("No userid found in subject!"); @@ -102,6 +103,20 @@ public final class CurrentUserContext { } public static List getGroupIds() { + Subject subject = Subject.getSubject(AccessController.getContext()); + LOGGER.debug("Subject of caller: {}", subject); + if (subject != null) { + Set principals = subject.getPrincipals(); + LOGGER.debug("Public principals of caller: {}", principals); + for (Principal pC : principals) { + if (pC instanceof TaskanaPrincipal) { + TaskanaPrincipal sP = (TaskanaPrincipal) pC; + LOGGER.debug("Returning the groupIds: {}", sP.getGroupNames()); + return sP.getGroupNames(); + } + } + } + LOGGER.debug("No groupids found in subject!"); return null; } diff --git a/lib/taskana-core/src/main/java/pro/taskana/security/TaskanaPrincipal.java b/lib/taskana-core/src/main/java/pro/taskana/security/TaskanaPrincipal.java new file mode 100644 index 000000000..97e9b3acf --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/security/TaskanaPrincipal.java @@ -0,0 +1,13 @@ +package pro.taskana.security; + +import java.security.Principal; +import java.util.List; + +/** + * This interface extends Principal by groupIds. + * @author KKL + */ +public interface TaskanaPrincipal extends Principal { + + List getGroupNames(); +} diff --git a/lib/taskana-core/src/test/java/pro/taskana/impl/integration/TaskServiceImplIntExplicitTest.java b/lib/taskana-core/src/test/java/pro/taskana/impl/integration/TaskServiceImplIntExplicitTest.java index 536354c16..56562cc81 100644 --- a/lib/taskana-core/src/test/java/pro/taskana/impl/integration/TaskServiceImplIntExplicitTest.java +++ b/lib/taskana-core/src/test/java/pro/taskana/impl/integration/TaskServiceImplIntExplicitTest.java @@ -1,13 +1,20 @@ package pro.taskana.impl.integration; import java.io.FileNotFoundException; +import java.io.InputStreamReader; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.sql.Connection; import java.sql.SQLException; +import java.util.ArrayList; import java.util.List; +import javax.security.auth.Subject; import javax.security.auth.login.LoginException; import javax.sql.DataSource; +import org.apache.ibatis.jdbc.ScriptRunner; import org.h2.store.fs.FileUtils; import org.junit.After; import org.junit.AfterClass; @@ -32,6 +39,8 @@ import pro.taskana.impl.configuration.TaskanaEngineConfigurationTest; import pro.taskana.impl.util.IdGenerator; import pro.taskana.model.Task; import pro.taskana.model.TaskState; +import pro.taskana.security.SamplePrincipal; + /** * Integration Test for TaskServiceImpl transactions with connection management mode EXPLICIT. @@ -44,6 +53,7 @@ public class TaskServiceImplIntExplicitTest { private TaskanaEngineConfiguration taskanaEngineConfiguration; private TaskanaEngine taskanaEngine; private TaskanaEngineImpl taskanaEngineImpl; + private Subject subject; @BeforeClass public static void resetDb() throws SQLException { @@ -55,23 +65,54 @@ public class TaskServiceImplIntExplicitTest { @Before public void setup() throws FileNotFoundException, SQLException, LoginException { dataSource = TaskanaEngineConfigurationTest.getDataSource(); - taskanaEngineConfiguration = new TaskanaEngineConfiguration(dataSource, false, false); + taskanaEngineConfiguration = new TaskanaEngineConfiguration(dataSource, false); taskanaEngine = taskanaEngineConfiguration.buildTaskanaEngine(); taskServiceImpl = (TaskServiceImpl) taskanaEngine.getTaskService(); taskanaEngineImpl = (TaskanaEngineImpl) taskanaEngine; taskanaEngineImpl.setConnectionManagementMode(ConnectionManagementMode.EXPLICIT); DBCleaner cleaner = new DBCleaner(); cleaner.clearDb(dataSource, false); + + subject = new Subject(); + SamplePrincipal samplePrincipal = new SamplePrincipal("Elena"); + List groups = new ArrayList(); + groups.add("group1"); + groups.add("group2"); + groups.add("group3"); + samplePrincipal.setGroups(groups); + subject.getPrincipals().add(samplePrincipal); + try { + Connection connection = dataSource.getConnection(); + ScriptRunner runner = new ScriptRunner(connection); + runner.runScript( + new InputStreamReader(this.getClass().getResourceAsStream("/sql/workbasket-access-list.sql"))); + + } catch (SQLException e1) { + e1.printStackTrace(); + } } @Test - public void testStart() throws FileNotFoundException, SQLException, TaskNotFoundException, NotAuthorizedException { + public void testStart() { + Subject.doAs(subject, new PrivilegedAction() { + @Override + public Object run() { + try { + do_testStart(); + } catch (TaskNotFoundException | FileNotFoundException | NotAuthorizedException | SQLException e) { + e.printStackTrace(); + } + return null; + } + }); + } + + public void do_testStart() throws FileNotFoundException, SQLException, TaskNotFoundException, NotAuthorizedException { Connection connection = dataSource.getConnection(); taskanaEngineImpl.setConnection(connection); Task task = new Task(); task.setName("Unit Test Task"); - String id1 = IdGenerator.generateWithPrefix("TWB"); - task.setWorkbasketId(id1); + task.setWorkbasketId("1"); task = taskServiceImpl.create(task); connection.commit(); // needed so that the change is visible in the other session @@ -83,7 +124,22 @@ public class TaskServiceImplIntExplicitTest { } @Test(expected = TaskNotFoundException.class) - public void testStartTransactionFail() + public void testStartTransactionFail() throws Throwable { + try { + Subject.doAs(subject, new PrivilegedExceptionAction() { + public Object run() throws TaskNotFoundException, FileNotFoundException, NotAuthorizedException, SQLException { + do_testStartTransactionFail(); + return null; + } + }); + } catch (PrivilegedActionException e) { + if (e.getCause() != null) { + throw e.getCause(); + } + } + } + + public void do_testStartTransactionFail() throws FileNotFoundException, SQLException, TaskNotFoundException, NotAuthorizedException { Connection connection = dataSource.getConnection(); taskanaEngineImpl.setConnection(connection); @@ -92,7 +148,7 @@ public class TaskServiceImplIntExplicitTest { Task task = new Task(); task.setName("Unit Test Task"); String id1 = IdGenerator.generateWithPrefix("TWB"); - task.setWorkbasketId("id1"); + task.setWorkbasketId("1"); task = taskServiceImpl.create(task); connection.commit(); taskServiceImpl.getTaskById(id1); @@ -104,7 +160,21 @@ public class TaskServiceImplIntExplicitTest { } @Test - public void testCreateTaskInTaskanaWithDefaultDb() + public void testCreateTaskInTaskanaWithDefaultDb() { + Subject.doAs(subject, new PrivilegedAction() { + @Override + public Object run() { + try { + do_testCreateTaskInTaskanaWithDefaultDb(); + } catch (TaskNotFoundException | FileNotFoundException | NotAuthorizedException | SQLException e) { + e.printStackTrace(); + } + return null; + } + }); + } + + public void do_testCreateTaskInTaskanaWithDefaultDb() throws FileNotFoundException, SQLException, TaskNotFoundException, NotAuthorizedException { DataSource ds = TaskanaEngineConfiguration.createDefaultDataSource(); TaskanaEngineConfiguration taskanaEngineConfiguration = new TaskanaEngineConfiguration(ds, false, false); @@ -115,8 +185,8 @@ public class TaskServiceImplIntExplicitTest { Task task = new Task(); task.setName("Unit Test Task"); - String id1 = IdGenerator.generateWithPrefix("TWB"); - task.setWorkbasketId(id1); + //String id1 = IdGenerator.generateWithPrefix("TWB"); + task.setWorkbasketId("1"); task = taskServiceImpl.create(task); Assert.assertNotNull(task); @@ -126,16 +196,36 @@ public class TaskServiceImplIntExplicitTest { } @Test - public void should_ReturnList_when_BuilderIsUsed() throws SQLException, NotAuthorizedException { + public void should_ReturnList_when_BuilderIsUsed() { + Subject.doAs(subject, new PrivilegedAction() { + @Override + public Object run() { + try { + do_should_ReturnList_when_BuilderIsUsed(); + } catch (NotAuthorizedException | SQLException e) { + e.printStackTrace(); + } + return null; + } + }); + } + + public void do_should_ReturnList_when_BuilderIsUsed() throws SQLException, NotAuthorizedException { Connection connection = dataSource.getConnection(); taskanaEngineImpl.setConnection(connection); + Task task = new Task(); task.setName("Unit Test Task"); - String id1 = IdGenerator.generateWithPrefix("TWB"); - task.setWorkbasketId(id1); + //String id1 = IdGenerator.generateWithPrefix("TWB"); + task.setWorkbasketId("1"); task = taskServiceImpl.create(task); + Task task2 = new Task(); + task2.setName("Unit Test Task"); + task2.setWorkbasketId("2"); + task2 = taskServiceImpl.create(task2); + TaskanaEngineImpl taskanaEngineImpl = (TaskanaEngineImpl) taskanaEngine; ClassificationQuery classificationQuery = new ClassificationQueryImpl(taskanaEngineImpl) .parentClassification("pId1", "pId2").category("cat1", "cat2").type("oneType").name("1Name", "name2") @@ -146,7 +236,7 @@ public class TaskServiceImplIntExplicitTest { .systemInstance("sysInst1", "sysInst2").value("val1", "val2", "val3"); List results = taskServiceImpl.createTaskQuery().name("bla", "test").descriptionLike("test") - .priority(1, 2, 2).state(TaskState.CLAIMED).workbasketId("asd", "asdasdasd") + .priority(1, 2, 2).state(TaskState.CLAIMED).workbasketId("1", "2") .owner("test", "test2", "bla").customFields("test").classification(classificationQuery) .objectReference(objectReferenceQuery).list(); diff --git a/lib/taskana-core/src/test/java/pro/taskana/security/SamplePrincipal.java b/lib/taskana-core/src/test/java/pro/taskana/security/SamplePrincipal.java new file mode 100644 index 000000000..1d6de485f --- /dev/null +++ b/lib/taskana-core/src/test/java/pro/taskana/security/SamplePrincipal.java @@ -0,0 +1,34 @@ +package pro.taskana.security; + +import java.util.List; + +/** + * SamplePrincipal. + * @author KKL + */ +public class SamplePrincipal implements TaskanaPrincipal { + + private String name; + private List groups; + + public SamplePrincipal(String name) { + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public List getGroupNames() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + +} diff --git a/lib/taskana-core/src/test/resources/sql/workbasket-access-list.sql b/lib/taskana-core/src/test/resources/sql/workbasket-access-list.sql new file mode 100644 index 000000000..1a46d67e8 --- /dev/null +++ b/lib/taskana-core/src/test/resources/sql/workbasket-access-list.sql @@ -0,0 +1,3 @@ +INSERT INTO WORKBASKET_ACCESS_LIST VALUES ('1', '1', 'Elena', true, true, true, true, true, false, false, false, false, false, false, false, false); +INSERT INTO WORKBASKET_ACCESS_LIST VALUES ('2', '2', 'Elena', true, true, true, true, true, true, true, true, true, false, false, false, false); +INSERT INTO WORKBASKET_ACCESS_LIST VALUES ('3', '3', 'Simone', true, true, true, true, true, true, true, true, true, true, true, true, true); diff --git a/rest/src/main/java/pro/taskana/rest/security/CustomAutenticationProvider.java b/rest/src/main/java/pro/taskana/rest/security/CustomAutenticationProvider.java index ebf17af44..fbc8e028e 100644 --- a/rest/src/main/java/pro/taskana/rest/security/CustomAutenticationProvider.java +++ b/rest/src/main/java/pro/taskana/rest/security/CustomAutenticationProvider.java @@ -1,9 +1,14 @@ package pro.taskana.rest.security; +import java.util.ArrayList; +import java.util.List; + import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.jaas.JaasAuthenticationToken; import org.springframework.security.core.Authentication; +import pro.taskana.security.TaskanaPrincipal; + public class CustomAutenticationProvider implements AuthenticationProvider { private AuthenticationProvider delegate; @@ -17,14 +22,35 @@ public class CustomAutenticationProvider implements AuthenticationProvider { .authenticate(authentication); if (jaasAuthenticationToken.isAuthenticated()) { - jaasAuthenticationToken.getLoginContext().getSubject().getPublicCredentials().add(jaasAuthenticationToken.getPrincipal()); + final String name = jaasAuthenticationToken.getPrincipal().toString(); + final List groupNames = getGroupNames(name); + TaskanaPrincipal tp = new TaskanaPrincipal() { + + @Override + public String getName() { + return name; + } + + @Override + public List getGroupNames() { + return groupNames; + } + }; + jaasAuthenticationToken.getLoginContext().getSubject().getPrincipals().add(tp); return jaasAuthenticationToken; } else { return null; } } - @Override + private List getGroupNames(String name) { + List groupNames = new ArrayList(); + groupNames.add("group1"); + groupNames.add("group2"); + return groupNames; + } + + @Override public boolean supports(Class authentication) { return delegate.supports(authentication); }