TSK-339 Make Taskana configurable
This commit is contained in:
parent
633bc2372f
commit
f327aee7b5
|
@ -23,8 +23,13 @@ public class TaskanaEngineConfiguration {
|
|||
private static final String USER_PASSWORD = "sa";
|
||||
private static final String JDBC_H2_MEM_TASKANA = "jdbc:h2:mem:taskana;IGNORECASE=TRUE";
|
||||
private static final String H2_DRIVER = "org.h2.Driver";
|
||||
private static final String TASKANA_ROLES_PROPERTIES = "/taskanaroles.properties";
|
||||
private static final String TASKANA_PROPERTIES_SEPARATOR = "|";
|
||||
|
||||
protected DataSource dataSource;
|
||||
protected DbSchemaCreator dbScriptRunner;
|
||||
protected String propertiesFileName = TASKANA_ROLES_PROPERTIES;
|
||||
protected String propertiesSeparator = TASKANA_PROPERTIES_SEPARATOR;
|
||||
|
||||
// global switch to enable JAAS based authentication and Taskana
|
||||
// authorizations
|
||||
|
@ -42,7 +47,21 @@ public class TaskanaEngineConfiguration {
|
|||
|
||||
public TaskanaEngineConfiguration(DataSource dataSource, boolean useManagedTransactions,
|
||||
boolean securityEnabled) throws SQLException {
|
||||
this(dataSource, useManagedTransactions, securityEnabled, null, null);
|
||||
}
|
||||
|
||||
public TaskanaEngineConfiguration(DataSource dataSource, boolean useManagedTransactions,
|
||||
boolean securityEnabled, String propertiesFileName, String propertiesSeparator) throws SQLException {
|
||||
this.useManagedTransactions = useManagedTransactions;
|
||||
this.securityEnabled = securityEnabled;
|
||||
|
||||
if (propertiesFileName != null) {
|
||||
this.propertiesFileName = propertiesFileName;
|
||||
}
|
||||
|
||||
if (propertiesSeparator != null) {
|
||||
this.propertiesSeparator = propertiesSeparator;
|
||||
}
|
||||
|
||||
if (dataSource != null) {
|
||||
this.dataSource = dataSource;
|
||||
|
@ -53,7 +72,6 @@ public class TaskanaEngineConfiguration {
|
|||
dbScriptRunner = new DbSchemaCreator(this.dataSource);
|
||||
dbScriptRunner.run();
|
||||
|
||||
this.securityEnabled = securityEnabled;
|
||||
}
|
||||
|
||||
public static DataSource createDefaultDataSource() {
|
||||
|
@ -100,6 +118,22 @@ public class TaskanaEngineConfiguration {
|
|||
return this.useManagedTransactions;
|
||||
}
|
||||
|
||||
public String getPropertiesFileName() {
|
||||
return this.propertiesFileName;
|
||||
}
|
||||
|
||||
public void setPropertiesFileName(String propertiesFileName) {
|
||||
this.propertiesFileName = propertiesFileName;
|
||||
}
|
||||
|
||||
public String getPropertiesSeparator() {
|
||||
return this.propertiesSeparator;
|
||||
}
|
||||
|
||||
public void setPropertiesSeparator(String propertiesSeparator) {
|
||||
this.propertiesSeparator = propertiesSeparator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine whether all access ids (user Id and group ids) should be used in lower case.
|
||||
*
|
||||
|
|
|
@ -1,9 +1,22 @@
|
|||
package pro.taskana.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.ibatis.mapping.Environment;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
@ -28,6 +41,7 @@ import pro.taskana.exceptions.ConnectionNotSetException;
|
|||
import pro.taskana.exceptions.SystemException;
|
||||
import pro.taskana.exceptions.UnsupportedDatabaseException;
|
||||
import pro.taskana.impl.persistence.MapTypeHandler;
|
||||
import pro.taskana.impl.util.LoggerUtils;
|
||||
import pro.taskana.mappings.AttachmentMapper;
|
||||
import pro.taskana.mappings.ClassificationMapper;
|
||||
import pro.taskana.mappings.DistributionTargetMapper;
|
||||
|
@ -52,6 +66,7 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
|||
protected SqlSessionFactory sessionFactory;
|
||||
protected ConnectionManagementMode mode = ConnectionManagementMode.PARTICIPATE;
|
||||
protected java.sql.Connection connection = null;
|
||||
protected Map<TaskanaRole, Set<String>> roleMap = new HashMap<>();
|
||||
|
||||
public static TaskanaEngine createTaskanaEngine(TaskanaEngineConfiguration taskanaEngineConfiguration) {
|
||||
return new TaskanaEngineImpl(taskanaEngineConfiguration);
|
||||
|
@ -61,6 +76,8 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
|||
this.taskanaEngineConfiguration = taskanaEngineConfiguration;
|
||||
createTransactionFactory(taskanaEngineConfiguration.getUseManagedTransactions());
|
||||
this.sessionManager = createSqlSessionManager();
|
||||
initRoles(taskanaEngineConfiguration.getPropertiesFileName(),
|
||||
taskanaEngineConfiguration.getPropertiesSeparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -275,6 +292,82 @@ public class TaskanaEngineImpl implements TaskanaEngine {
|
|||
}
|
||||
}
|
||||
|
||||
protected void initRoles(String propertiesFilename, String propertiesSeparator) {
|
||||
String propertiesFile = propertiesFilename == null ? taskanaEngineConfiguration.getPropertiesFileName()
|
||||
: propertiesFilename;
|
||||
String separator = propertiesSeparator == null ? taskanaEngineConfiguration.getPropertiesSeparator()
|
||||
: propertiesSeparator;
|
||||
if (taskanaEngineConfiguration.isSecurityEnabled()) {
|
||||
// is the filename fully qualified or relative?
|
||||
boolean loadFromClasspath = true;
|
||||
File f = new File(propertiesFile);
|
||||
if (f.exists() && !f.isDirectory()) {
|
||||
loadFromClasspath = false;
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
List<String> validPropertyNames = Arrays.asList(TaskanaRole.USER.getPropertyName(),
|
||||
TaskanaRole.BUSINESS_ADMIN.getPropertyName(), TaskanaRole.ADMIN.getPropertyName());
|
||||
try {
|
||||
if (loadFromClasspath) {
|
||||
|
||||
InputStream inputStream = this.getClass().getResourceAsStream(propertiesFile);
|
||||
if (inputStream == null) {
|
||||
LOGGER.error("properties file {} was not found on classpath.", propertiesFile);
|
||||
ensureRoleMapIsFullyInitialized();
|
||||
return;
|
||||
} else {
|
||||
props.load(new InputStreamReader(inputStream));
|
||||
}
|
||||
} else {
|
||||
props.load(new FileInputStream(propertiesFile));
|
||||
}
|
||||
for (Object obj : props.keySet()) {
|
||||
String propertyName = ((String) obj);
|
||||
if (validPropertyNames.contains(propertyName.toLowerCase().trim())) {
|
||||
String propertyValue = props.getProperty(propertyName);
|
||||
Set<String> roleMemberSet = new HashSet<>();
|
||||
StringTokenizer st = new StringTokenizer(propertyValue, separator);
|
||||
while (st.hasMoreTokens()) {
|
||||
String token = st.nextToken().toLowerCase().trim();
|
||||
roleMemberSet.add(token);
|
||||
}
|
||||
TaskanaRole key = TaskanaRole.fromProperyName(propertyName);
|
||||
if (key != null) {
|
||||
roleMap.put(key, roleMemberSet);
|
||||
} else {
|
||||
LOGGER.error("internal System error when processing properties file {}.", propertiesFile);
|
||||
throw new SystemException(
|
||||
"internal System error when processing properties file " + propertiesFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
ensureRoleMapIsFullyInitialized();
|
||||
|
||||
roleMap.forEach(
|
||||
(k, v) -> LOGGER.debug("Found Taskana RoleConfig {} : {} ", k, LoggerUtils.setToString(v)));
|
||||
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("caught IOException when processing properties file {}.", propertiesFile);
|
||||
throw new SystemException("internal System error when processing properties file " + propertiesFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureRoleMapIsFullyInitialized() {
|
||||
// make sure that roleMap does not return null for any role
|
||||
if (!roleMap.containsKey(TaskanaRole.ADMIN)) {
|
||||
roleMap.put(TaskanaRole.ADMIN, new HashSet<>());
|
||||
}
|
||||
if (!roleMap.containsKey(TaskanaRole.BUSINESS_ADMIN)) {
|
||||
roleMap.put(TaskanaRole.BUSINESS_ADMIN, new HashSet<>());
|
||||
}
|
||||
|
||||
if (!roleMap.containsKey(TaskanaRole.USER)) {
|
||||
roleMap.put(TaskanaRole.USER, new HashSet<>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* With sessionStack, we maintain a Stack of SqlSessionManager objects on a per thread basis. SqlSessionManager is
|
||||
* the MyBatis object that wraps database connections. The purpose of this stack is to keep track of nested calls.
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package pro.taskana.impl;
|
||||
|
||||
/**
|
||||
* This enum contains all roles that are known to taskana.
|
||||
*/
|
||||
public enum TaskanaRole {
|
||||
USER("taskana.roles.user"),
|
||||
BUSINESS_ADMIN("taskana.roles.businessadmin"),
|
||||
ADMIN("taskana.roles.admin");
|
||||
|
||||
private final String propertyName;
|
||||
|
||||
TaskanaRole(String propertyName) {
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public static TaskanaRole fromProperyName(String name) {
|
||||
if (USER.propertyName.equalsIgnoreCase(name)) {
|
||||
return TaskanaRole.USER;
|
||||
} else if (BUSINESS_ADMIN.propertyName.equalsIgnoreCase(name)) {
|
||||
return TaskanaRole.BUSINESS_ADMIN;
|
||||
} else if (ADMIN.propertyName.equalsIgnoreCase(name)) {
|
||||
return TaskanaRole.ADMIN;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
}
|
|
@ -71,4 +71,15 @@ public final class LoggerUtils {
|
|||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> String setToString(Set<T> set) {
|
||||
if (set == null || set.isEmpty()) {
|
||||
return "[]";
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder("[");
|
||||
set.forEach(e -> result.append("(").append(e).append(") ,"));
|
||||
result.append("]");
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
package acceptance.config;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.h2.store.fs.FileUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import pro.taskana.configuration.TaskanaEngineConfiguration;
|
||||
import pro.taskana.impl.TaskanaEngineImpl;
|
||||
import pro.taskana.impl.TaskanaRole;
|
||||
import pro.taskana.impl.configuration.TaskanaEngineConfigurationTest;
|
||||
|
||||
/**
|
||||
* Test taskana's role configuration.
|
||||
*
|
||||
* @author bbr
|
||||
*/
|
||||
public class TaskanaRoleConfigAccTest extends TaskanaEngineImpl {
|
||||
|
||||
public TaskanaRoleConfigAccTest() throws SQLException {
|
||||
super(new TaskanaEngineConfiguration(TaskanaEngineConfigurationTest.getDataSource(), true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardConfig() {
|
||||
Set<TaskanaRole> rolesConfigured = super.roleMap.keySet();
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.ADMIN));
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.BUSINESS_ADMIN));
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.USER));
|
||||
|
||||
Set<String> users = roleMap.get(TaskanaRole.USER);
|
||||
assertTrue(users.contains("user_1_1"));
|
||||
assertTrue(users.contains("user_1_2"));
|
||||
|
||||
Set<String> admins = roleMap.get(TaskanaRole.ADMIN);
|
||||
assertTrue(admins.contains("teamlead_1"));
|
||||
assertTrue(admins.contains("teamlead_2"));
|
||||
|
||||
Set<String> businessAdmins = roleMap.get(TaskanaRole.BUSINESS_ADMIN);
|
||||
assertTrue(businessAdmins.contains("max"));
|
||||
assertTrue(businessAdmins.contains("moritz"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherConfigFileSameDelimiter() throws IOException, SQLException {
|
||||
String propertiesFileName = createNewConfigFileWithSameDelimiter("/dummyTestConfig.properties");
|
||||
try {
|
||||
initRoles(propertiesFileName, null);
|
||||
|
||||
Set<TaskanaRole> rolesConfigured = super.roleMap.keySet();
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.ADMIN));
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.BUSINESS_ADMIN));
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.USER));
|
||||
|
||||
Set<String> users = roleMap.get(TaskanaRole.USER);
|
||||
assertTrue(users.contains("nobody"));
|
||||
|
||||
Set<String> admins = roleMap.get(TaskanaRole.ADMIN);
|
||||
assertTrue(admins.contains("holger"));
|
||||
assertTrue(admins.contains("stefan"));
|
||||
|
||||
Set<String> businessAdmins = roleMap.get(TaskanaRole.BUSINESS_ADMIN);
|
||||
assertTrue(businessAdmins.contains("ebe"));
|
||||
assertTrue(businessAdmins.contains("konstantin"));
|
||||
} finally {
|
||||
deleteFile(propertiesFileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherConfigFileDifferentDelimiter() throws IOException, SQLException {
|
||||
String delimiter = ";";
|
||||
String propertiesFileName = createNewConfigFileWithDifferentDelimiter("/dummyTestConfig.properties", delimiter);
|
||||
try {
|
||||
initRoles(propertiesFileName, delimiter);
|
||||
|
||||
Set<TaskanaRole> rolesConfigured = super.roleMap.keySet();
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.ADMIN));
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.BUSINESS_ADMIN));
|
||||
assertTrue(rolesConfigured.contains(TaskanaRole.USER));
|
||||
|
||||
Set<String> users = roleMap.get(TaskanaRole.USER);
|
||||
assertTrue(users.isEmpty());
|
||||
|
||||
Set<String> admins = roleMap.get(TaskanaRole.ADMIN);
|
||||
assertTrue(admins.contains("holger"));
|
||||
assertTrue(admins.contains("name=stefan,organisation=novatec"));
|
||||
|
||||
Set<String> businessAdmins = roleMap.get(TaskanaRole.BUSINESS_ADMIN);
|
||||
assertTrue(businessAdmins.contains("name=ebe, ou = bpm"));
|
||||
assertTrue(businessAdmins.contains("konstantin"));
|
||||
} finally {
|
||||
deleteFile(propertiesFileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String createNewConfigFileWithDifferentDelimiter(String filename, String delimiter) throws IOException {
|
||||
String userHomeDirectroy = System.getProperty("user.home");
|
||||
String propertiesFileName = userHomeDirectroy + filename;
|
||||
File f = new File(propertiesFileName);
|
||||
if (!f.exists()) {
|
||||
try (PrintWriter writer = new PrintWriter(propertiesFileName, "UTF-8")) {
|
||||
writer.println("taskana.roles.Admin =hOlGeR " + delimiter + "name=Stefan,Organisation=novatec");
|
||||
writer.println(" taskana.roles.businessadmin = name=ebe, ou = bpm " + delimiter + " konstantin ");
|
||||
writer.println(" taskana.roles.user = ");
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return propertiesFileName;
|
||||
}
|
||||
|
||||
private void deleteFile(String propertiesFileName) {
|
||||
System.out.println("about to delete " + propertiesFileName);
|
||||
File f = new File(propertiesFileName);
|
||||
if (f.exists() && !f.isDirectory()) {
|
||||
FileUtils.delete(propertiesFileName);
|
||||
}
|
||||
}
|
||||
|
||||
private String createNewConfigFileWithSameDelimiter(String filename) throws IOException {
|
||||
String userHomeDirectroy = System.getProperty("user.home");
|
||||
String propertiesFileName = userHomeDirectroy + filename;
|
||||
File f = new File(propertiesFileName);
|
||||
if (!f.exists()) {
|
||||
try (PrintWriter writer = new PrintWriter(propertiesFileName, "UTF-8")) {
|
||||
writer.println("taskana.roles.Admin =hOlGeR|Stefan");
|
||||
writer.println(" taskana.roles.businessadmin = ebe | konstantin ");
|
||||
writer.println(" taskana.roles.user = nobody");
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return propertiesFileName;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
taskana.roles.user = group1 | group2|teamlead_1 |teamlead_2 |user_1_1| user_1_1| user_1_2| user_2_1| user_2_2| max|elena|simone
|
||||
taskana.roles.Admin=teamlead_1|teamlead_2
|
||||
taskana.roles.businessadmin=max|Moritz
|
Loading…
Reference in New Issue