refactoring and test run without errors
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/AuditingIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java
similarity index 97%
rename from src/main/java/de/ids_mannheim/korap/interfaces/AuditingIface.java
rename to src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java
index 18a616f..ed21e60 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/AuditingIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.interfaces;
+package de.ids_mannheim.korap.interfaces.db;
 
 import de.ids_mannheim.korap.auditing.AuditRecord;
 import de.ids_mannheim.korap.user.User;
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/db/EntityHandlerIface.java
similarity index 96%
rename from src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
rename to src/main/java/de/ids_mannheim/korap/interfaces/db/EntityHandlerIface.java
index aed2871..f349f23 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/db/EntityHandlerIface.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.interfaces;
+package de.ids_mannheim.korap.interfaces.db;
 
 import de.ids_mannheim.korap.exceptions.EmptyResultException;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/PersistenceClient.java b/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java
similarity index 95%
rename from src/main/java/de/ids_mannheim/korap/interfaces/PersistenceClient.java
rename to src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java
index 07d53c7..c21d941 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/PersistenceClient.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.interfaces;
+package de.ids_mannheim.korap.interfaces.db;
 
 import lombok.Getter;
 
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/PolicyHandlerIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/db/PolicyHandlerIface.java
similarity index 98%
rename from src/main/java/de/ids_mannheim/korap/interfaces/PolicyHandlerIface.java
rename to src/main/java/de/ids_mannheim/korap/interfaces/db/PolicyHandlerIface.java
index 122970f..15e4296 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/PolicyHandlerIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/db/PolicyHandlerIface.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.interfaces;
+package de.ids_mannheim.korap.interfaces.db;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.resources.KustvaktResource;
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/ResourceOperationIface.java b/src/main/java/de/ids_mannheim/korap/interfaces/db/ResourceOperationIface.java
similarity index 96%
rename from src/main/java/de/ids_mannheim/korap/interfaces/ResourceOperationIface.java
rename to src/main/java/de/ids_mannheim/korap/interfaces/db/ResourceOperationIface.java
index fa0bf35..260e928 100644
--- a/src/main/java/de/ids_mannheim/korap/interfaces/ResourceOperationIface.java
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/db/ResourceOperationIface.java
@@ -1,4 +1,4 @@
-package de.ids_mannheim.korap.interfaces;
+package de.ids_mannheim.korap.interfaces.db;
 
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.resources.KustvaktResource;
diff --git a/src/main/java/de/ids_mannheim/korap/interfaces/defaults/KustvaktEncryption.java b/src/main/java/de/ids_mannheim/korap/interfaces/defaults/KustvaktEncryption.java
new file mode 100644
index 0000000..a21d767
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/interfaces/defaults/KustvaktEncryption.java
@@ -0,0 +1,483 @@
+package de.ids_mannheim.korap.interfaces.defaults;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.interfaces.EncryptionIface;
+import de.ids_mannheim.korap.user.User;
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.mindrot.jbcrypt.BCrypt;
+import org.owasp.esapi.ESAPI;
+import org.owasp.esapi.Randomizer;
+import org.owasp.esapi.Validator;
+import org.owasp.esapi.errors.ValidationException;
+import org.owasp.esapi.reference.DefaultValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class KustvaktEncryption implements EncryptionIface {
+
+    private static final String ALGORITHM = "SHA-256";
+    private static Logger jlog = LoggerFactory
+            .getLogger(KustvaktEncryption.class);
+    // todo: disable this
+    private static final String PASSWORD_SALT_FIELD = "accountCreation";
+
+    private final boolean nullable;
+    private final Validator validator;
+    private final Randomizer randomizer;
+    private KustvaktConfiguration config;
+
+    public KustvaktEncryption(KustvaktConfiguration config) {
+        jlog.info("initializing KorAPEncryption implementation");
+        this.nullable = false;
+        this.validator = DefaultValidator.getInstance();
+        this.randomizer = ESAPI.randomizer();
+        this.config = config;
+    }
+
+    public static boolean matchTokenByteCode(Object param) {
+        if (!(param instanceof String))
+            return false;
+        String token = (String) param;
+        byte[] bytes = token.getBytes();
+        return 64 == bytes.length;
+    }
+
+    private String encodeBase(byte[] bytes) throws EncoderException {
+        return Base64.encodeBase64String(bytes);
+    }
+
+    @Override
+    public String encodeBase() {
+        try {
+            return encodeBase(this.createSecureRandom(24));
+        }catch (EncoderException e) {
+            return "";
+        }
+    }
+
+    public String produceSecureHash(String input) {
+        return produceSecureHash(input, "");
+    }
+
+    @Override
+    public String produceSecureHash(String input, String salt) {
+        String hashString = "";
+        switch (config.getEncryption()) {
+            case ESAPICYPHER:
+                try {
+                    hashString = hash(input, salt);
+                }catch (NoSuchAlgorithmException e) {
+                    jlog.error("there was an encryption error!", e);
+                    return null;
+                }catch (Exception e) {
+                    jlog.error("there was an error!", e);
+                    return null;
+                }
+                break;
+            case SIMPLE:
+                try {
+                    MessageDigest md = MessageDigest.getInstance("SHA-256");
+                    md.update(input.getBytes("UTF-8"));
+                    byte[] digest = md.digest();
+
+                    for (byte b : digest)
+                        hashString += String.format("%02x", b);
+                }catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
+                    e.printStackTrace();
+                }
+                break;
+            case BCRYPT:
+                hashString = bcryptHash(input, salt);
+                break;
+            default:
+                jlog.warn("Invalid value: {}", config.getEncryption());
+                break;
+        }
+        return hashString;
+    }
+
+    public String hash(String text, String salt) throws Exception {
+        byte[] bytes;
+
+        MessageDigest md = MessageDigest.getInstance(ALGORITHM);
+        md.reset();
+        md.update(ESAPI.securityConfiguration().getMasterSalt());
+        md.update(salt.getBytes());
+        md.update(text.getBytes());
+
+        bytes = md.digest();
+        for (int i = 0; i < 234; ++i) {
+            md.reset();
+            bytes = md.digest(bytes);
+        }
+        String coding = ESAPI.encoder().encodeForBase64(bytes, false);
+        return coding;
+    }
+
+    @Override
+    public String hash(String input) {
+        String hashString = "";
+        MessageDigest md;
+        try {
+            md = MessageDigest.getInstance("SHA-256");
+            md.update(input.getBytes("UTF-8"));
+        }catch (NoSuchAlgorithmException e) {
+            return "";
+        }catch (UnsupportedEncodingException e) {
+            return "";
+        }
+
+        byte[] digest = md.digest();
+
+        for (byte b : digest) {
+            hashString += String.format("%02x", b);
+        }
+        return hashString;
+    }
+
+    /**
+     * // some sort of algorithm to create token and isSystem regularly the integrity
+     * // of the token
+     * public String createAuthToken() {
+     * final byte[] rNumber = SecureRGenerator
+     * .getNextSecureRandom(SecureRGenerator.TOKEN_RANDOM_SIZE);
+     * String hash;
+     * try {
+     * hash = produceSimpleHash(SecureRGenerator.toHex(rNumber));
+     * } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
+     * return "";
+     * }
+     * return hash;
+     * }
+     */
+
+    private byte[] createSecureRandom(int size) {
+        return SecureRGenerator.getNextSecureRandom(size);
+    }
+
+    @Override
+    public String createToken(boolean hash, Object... obj) {
+        StringBuffer b = new StringBuffer();
+        try {
+            for (Object o : obj) {
+                b.append(" | ");
+                b.append(o);
+            }
+            if (hash)
+                return encodeBase(hash(b.toString().trim()).getBytes());
+            else
+                return encodeBase(b.toString().trim().getBytes());
+        }catch (EncoderException e) {
+            return "";
+        }
+
+    }
+
+    @Override
+    public String createToken() {
+        String encoded;
+        String v = randomizer
+                .getRandomString(SecureRGenerator.TOKEN_RANDOM_SIZE,
+                        SecureRGenerator.toHex(createSecureRandom(64))
+                                .toCharArray());
+        encoded = hash(v);
+        jlog.trace("creating new token {}", encoded);
+        return encoded;
+    }
+
+    @Override
+    public String createID(Object... obj) {
+        final byte[] rNumber = SecureRGenerator
+                .getNextSecureRandom(SecureRGenerator.CORPUS_RANDOM_SIZE);
+        if (obj.length != 0) {
+            ArrayList s = new ArrayList();
+            Collections.addAll(s, obj);
+            obj = s.toArray();
+        }else {
+            obj = new Object[1];
+            obj[0] = rNumber;
+        }
+        return createToken(false, obj);
+    }
+
+    @Override
+    public boolean checkHash(String plain, String hash, String salt) {
+        String pw = "";
+        switch (config.getEncryption()) {
+            case ESAPICYPHER:
+                pw = produceSecureHash(plain, salt);
+                break;
+            case BCRYPT:
+                try {
+                    return BCrypt.checkpw(plain, hash);
+                }catch (IllegalArgumentException e) {
+                    return false;
+                }
+            case SIMPLE:
+                pw = hash(plain);
+                break;
+        }
+        return pw.equals(hash);
+    }
+
+    @Override
+    public boolean checkHash(String plain, String hash) {
+        switch (config.getEncryption()) {
+            case ESAPICYPHER:
+                return produceSecureHash(plain).equals(hash);
+            case BCRYPT:
+                try {
+                    return BCrypt.checkpw(plain, hash);
+                }catch (IllegalArgumentException e) {
+                    return false;
+                }
+            case SIMPLE:
+                return hash(plain).equals(hash);
+        }
+        return false;
+    }
+
+    @Override
+    public String getSalt(User user) {
+        Class u = user.getClass();
+        Field field;
+        try {
+            field = u.getSuperclass().getDeclaredField(PASSWORD_SALT_FIELD);
+        }catch (NoSuchFieldException e) {
+            try {
+                field = u.getDeclaredField(PASSWORD_SALT_FIELD);
+            }catch (NoSuchFieldException e1) {
+                // do nothing
+                e.printStackTrace();
+                return null;
+            }
+        }
+        try {
+            field.setAccessible(true);
+            String value = String.valueOf(field.get(user));
+            field.setAccessible(false);
+            return value;
+        }catch (IllegalAccessException e) {
+            // do nothing
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    @Override
+    public Map<String, Object> validateMap(Map<String, Object> map)
+            throws KustvaktException {
+        Map<String, Object> safeMap = new HashMap<>();
+        if (map != null) {
+            for (Map.Entry<String, Object> entry : map.entrySet()) {
+                Object value = null;
+                if (entry.getValue() instanceof String) {
+                    value = validateString((String) entry.getValue());
+
+                }else if (entry.getValue() instanceof List) {
+                    List list = (List) entry.getValue();
+                    for (Object v : list) {
+                        if (v instanceof String)
+                            validateString((String) v);
+                    }
+
+                    if (((List) entry.getValue()).size() == 1)
+                        value = list.get(0);
+                    else
+                        value = list;
+                }
+                safeMap.put(entry.getKey(), value);
+            }
+        }
+        return safeMap;
+    }
+
+
+    private String validateString(String descr, String input, String type,
+            int length, boolean nullable) throws KustvaktException {
+        if (jlog.isDebugEnabled())
+            jlog.debug("validating string entry '{}'", input);
+        String s;
+        try {
+            s = validator.getValidInput(descr, input, type, length, nullable);
+        }catch (ValidationException e) {
+            jlog.error(
+                    "String value did not validate ('{}') with validation type {}",
+                    new Object[] { input, type, e.getMessage() });
+            throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+                    "invalid string of type " + type, input);
+        }
+        return s;
+    }
+
+    @Override
+    public String validateString(String input) throws KustvaktException {
+        if (input.contains("@")) {
+            return validateEmail(input);
+        }else
+            return validateString("Safe String", input, "SafeString",
+                    config.getValidationStringLength(), nullable);
+    }
+
+    @Override
+    public String validateEmail(String email) throws KustvaktException {
+        jlog.debug("validating email entry '{}'", email);
+        return validateString("Email", email, "Email",
+                config.getValidationEmaillength(), nullable);
+    }
+
+    @Override
+    public String validateIPAddress(String ipaddress) throws KustvaktException {
+        jlog.debug("validating IP address entry '{}'", ipaddress);
+        return validateString("IP Address", ipaddress, "IPAddress",
+                config.getValidationStringLength(), nullable);
+    }
+
+    @Override
+    public void validate(Object instance) throws KustvaktException {
+        if (instance == null)
+            return;
+        try {
+            validateStringField(instance.getClass().getDeclaredFields(),
+                    instance);
+            validateStringField(
+                    instance.getClass().getSuperclass().getDeclaredFields(),
+                    instance);
+        }catch (IllegalAccessException e) {
+            jlog.error("object value did not validate", e.getMessage());
+            throw new KustvaktException(StatusCodes.PARAMETER_VALIDATION_ERROR,
+                    "object could not be validated", instance.toString());
+        }
+    }
+
+    //fixme: fix validation algorithm
+    @Override
+    public String validatePassphrase(String pw) throws KustvaktException {
+        String safe_string = validateString(pw);
+        String pw_conf;
+        try {
+            pw_conf = validator
+                    .getValidInput("User Password", safe_string, "Password", 20,
+                            false);
+        }catch (ValidationException e) {
+            jlog.error("password value did not validate", e.getMessage());
+            throw new KustvaktException(StatusCodes.PARAMETER_VALIDATION_ERROR,
+                    "password did not validate", "password");
+        }
+        return pw_conf;
+    }
+
+    //FIXME: currently all sets are skipped during validation (since users should not be allowed to edit those sets anyway,
+    //I think we will be safe here
+    private void validateStringField(Field[] fields, Object instance)
+            throws KustvaktException, IllegalAccessException {
+        for (Field field : fields) {
+            boolean priv = false;
+            if (field.getType().isAssignableFrom(String.class)) {
+                if (Modifier.isPrivate(field.getModifiers())) {
+                    priv = true;
+                    field.setAccessible(true);
+                }
+                if (field.getName().equals("password") | Modifier
+                        .isFinal(field.getModifiers()))
+                    continue;
+                String val = (String) field.get(instance);
+                if (val != null) {
+                    String[] set = val.split(";");
+                    if (set.length > 1)
+                        continue;
+                }
+                String safe;
+                if (!field.getName().equals("email"))
+                    safe = validateString("Safe String", val, "SafeString",
+                            config.getValidationStringLength(), true);
+                else
+                    safe = validateString("User Email", val, "Email",
+                            config.getValidationEmaillength(), true);
+                field.set(instance, safe == null ? "" : safe);
+
+                if (priv) {
+                    field.setAccessible(false);
+                }
+            }
+        }
+    }
+
+    private String bcryptHash(String text, String salt) {
+        if (salt == null || salt.isEmpty())
+            salt = BCrypt.gensalt(config.getLoadFactor());
+        return BCrypt.hashpw(text, salt);
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getCanonicalName();
+    }
+
+    public static class SecureRGenerator {
+        private static final String SHA1_PRNG = "SHA1PRNG";
+        protected static final int DEFAULT_RANDOM_SIZE = 128;
+        protected static final int TOKEN_RANDOM_SIZE = 128;
+        protected static final int USERID_RANDOM_SIZE = 64;
+        protected static final int CORPUS_RANDOM_SIZE = 48;
+        private static final char[] HEX_DIGIT = { '0', '1', '2', '3', '4', '5',
+                '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'z', 'x', 'h',
+                'q', 'w' };
+        private static final SecureRandom sRandom__;
+
+        static {
+            try {
+                sRandom__ = SecureRandom.getInstance("SHA1PRNG");
+            }catch (NoSuchAlgorithmException e) {
+                throw new Error(e);
+            }
+        }
+
+        public static byte[] getNextSecureRandom(int bits) {
+            if (bits % 8 != 0) {
+                throw new IllegalArgumentException(
+                        "Size is not divisible by 8!");
+            }
+
+            byte[] bytes = new byte[bits / 8];
+
+            sRandom__.nextBytes(bytes);
+
+            return bytes;
+        }
+
+        public static String toHex(byte[] bytes) {
+            if (bytes == null) {
+                return null;
+            }
+
+            StringBuilder buffer = new StringBuilder(bytes.length * 2);
+            for (byte thisByte : bytes) {
+                buffer.append(byteToHex(thisByte));
+            }
+
+            return buffer.toString();
+        }
+
+        private static String byteToHex(byte b) {
+            char[] array = { HEX_DIGIT[(b >> 4 & 0xF)], HEX_DIGIT[(b & 0xF)] };
+            return new String(array);
+        }
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/resource/rewrite/IdWriter.java b/src/main/java/de/ids_mannheim/korap/resource/rewrite/IdWriter.java
new file mode 100644
index 0000000..0c91ac0
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/resource/rewrite/IdWriter.java
@@ -0,0 +1,8 @@
+package de.ids_mannheim.korap.resource.rewrite;
+
+/**
+ * @author hanl
+ * @date 25/09/2015
+ */
+public class IdWriter {
+}
diff --git a/src/main/java/de/ids_mannheim/korap/security/ac/PolicyDao.java b/src/main/java/de/ids_mannheim/korap/security/ac/PolicyDao.java
new file mode 100644
index 0000000..d260b65
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/security/ac/PolicyDao.java
@@ -0,0 +1,808 @@
+package de.ids_mannheim.korap.security.ac;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.exceptions.dbException;
+import de.ids_mannheim.korap.ext.interfaces.PolicyHandlerIface;
+import de.ids_mannheim.korap.ext.resource.KorAPResource;
+import de.ids_mannheim.korap.ext.resource.ResourceFactory;
+import de.ids_mannheim.korap.ext.security.types.Parameter;
+import de.ids_mannheim.korap.ext.security.types.PolicyCondition;
+import de.ids_mannheim.korap.ext.security.types.SecurityPolicy;
+import de.ids_mannheim.korap.interfaces.PersistenceClient;
+import de.ids_mannheim.korap.user.Attributes;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.BooleanUtils;
+import de.ids_mannheim.korap.utils.KustvaktLogger;
+import de.ids_mannheim.korap.utils.StringUtils;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.ResultSetExtractor;
+import org.springframework.jdbc.core.RowCallbackHandler;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 14/01/2014
+ */
+// todo: transactions and exception management
+public class PolicyDao implements PolicyHandlerIface {
+
+    private NamedParameterJdbcTemplate jdbcTemplate;
+
+    public PolicyDao(PersistenceClient client) {
+        this.jdbcTemplate = (NamedParameterJdbcTemplate) client.getSource();
+    }
+
+    /**
+     * @param policy
+     * @param user
+     * @return int to indicate the rows updated/inserted
+     * @throws KustvaktException
+     */
+    // fixme: better way of dealing with this?
+    // fixme: enable needs to be set specifically for mysql db
+    @Override
+    public int createPolicy(SecurityPolicy policy, User user)
+            throws KustvaktException {
+        String sql =
+                "INSERT INTO policy_store (target_id, creator, created, posix, enable, expire, iprange)"
+                        + " SELECT id, :creator, :cr, :posix, :en, :exp, :ip FROM resource_store WHERE persistent_id=:target;";
+
+        if (policy.getTarget() == null)
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.MISSING_POLICY_TARGET, policy.toString());
+
+        if (policy.getConditions().isEmpty())
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.MISSING_POLICY_CONDITIONS);
+
+        if (policy.getPermissionByte() == 0)
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.MISSING_POLICY_PERMISSION);
+
+        KeyHolder keyHolder = new GeneratedKeyHolder();
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        np.addValue("target", policy.getTarget());
+        if (policy.getContext().getEnd() != 0L)
+            np.addValue("exp", new Timestamp(policy.getContext().getEnd()));
+        else
+            np.addValue("exp", null);
+        np.addValue("en", new Timestamp(policy.getContext().getStart()));
+        np.addValue("posix", policy.getPermissionByte());
+        np.addValue("cr", new Timestamp(TimeUtils.getNow().getMillis()));
+        np.addValue("creator", user.getId());
+        np.addValue("ip", policy.getContext().getIpmask());
+
+        try {
+            mapConditionsToUsers(policy, user);
+            this.jdbcTemplate.update(sql, np, keyHolder, new String[] { "id" });
+            policy.setID(keyHolder.getKey().intValue());
+            this.mapConstraints(policy);
+            return policy.getID();
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (INSERT) not possible for '{}' for user '{}'",
+                            policy.toString(), user.getId());
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.DB_INSERT_FAILED, policy.toString());
+        }
+    }
+
+    /**
+     * should also include a remove operation, so removed policy constraints
+     *
+     * @param policy
+     * @return
+     * @throws KustvaktException
+     */
+    // benchmark this!
+    @Override
+    public void mapConstraints(SecurityPolicy policy) throws KustvaktException {
+        final String cond = "INSERT INTO group_ref (group_id, policy_id) VALUES (:group, :policyID);";
+        final String remove = "DELETE FROM group_ref WHERE group_id=:group and policy_id=:policyID;";
+        try {
+            List<PolicyCondition> conditions = policy.getConditions();
+            int idx = 0;
+            if (!policy.getRemoved().isEmpty()) {
+                MapSqlParameterSource[] sources_removed = new MapSqlParameterSource[policy
+                        .getRemoved().size()];
+                for (Integer toremove : policy.getRemoved()) {
+                    MapSqlParameterSource source = new MapSqlParameterSource();
+                    source.addValue("group",
+                            conditions.get(toremove).getSpecifier());
+                    source.addValue("policyID", policy.getID());
+                    sources_removed[idx++] = source;
+                }
+                this.jdbcTemplate.batchUpdate(remove, sources_removed);
+            }
+
+            if (!policy.getAdded().isEmpty()) {
+                idx = 0;
+                MapSqlParameterSource[] sources = new MapSqlParameterSource[policy
+                        .getAdded().size()];
+                for (Integer add : policy.getAdded()) {
+                    MapSqlParameterSource source = new MapSqlParameterSource();
+                    source.addValue("group",
+                            conditions.get(add).getSpecifier());
+                    source.addValue("policyID", policy.getID());
+                    sources[idx++] = source;
+                }
+                this.jdbcTemplate.batchUpdate(cond, sources);
+            }
+            policy.clear();
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (MAPPING POLICY CONDITIONS) not possible for '{}' for user '{}'",
+                            policy.toString(), policy.getCreator());
+            // throwing an error here is not recommended
+            //            throw new dbException(policy.getCreator(), "policy_store",
+            //                    StatusCodes.DB_INSERT_FAILED, policy.toString());
+        }
+    }
+
+    // todo: check transactional behaviour! --> rollback
+    private void mapConditionsToUsers(SecurityPolicy policy, User user)
+            throws KustvaktException {
+        for (PolicyCondition cond : policy.getConditions()) {
+            MapSqlParameterSource param = new MapSqlParameterSource();
+            param.addValue("name", cond.getSpecifier());
+            param.addValue("userid", user.getId());
+
+            try {
+                final Integer[] results = new Integer[2];
+                jdbcTemplate
+                        .query("SELECT COUNT(*) as total, (select count(*) from group_users where user_id=:userid and "
+                                        + "group_id=:name) as users FROM group_store WHERE name=:name",
+                                param, new RowCallbackHandler() {
+                                    @Override
+                                    public void processRow(ResultSet rs)
+                                            throws SQLException {
+                                        results[0] = rs.getInt("total");
+                                        results[1] = rs.getInt("users");
+                                    }
+                                });
+
+                boolean admin = false;
+                if (results[0] == 0) {
+                    admin = true;
+                    this.createCondition(cond, user);
+                }
+                if (results[1] == 0)
+                    this.addToCondition(Arrays.asList(user.getUsername()), cond,
+                            admin);
+            }catch (DataAccessException e) {
+                KustvaktLogger.SECURITY_LOGGER
+                        .error("Operation (SELECT) not possible for '{}' for user '{}'",
+                                policy.getTarget(), user.getId());
+                throw new dbException(user.getId(), "policy_store",
+                        StatusCodes.DB_GET_FAILED, policy.toString());
+            }
+        }
+    }
+
+    // fixme: does not compare permissions. parent can still disregard policy because of missing permisssions
+    @Override
+    public List<SecurityPolicy>[] getPolicies(Integer target, final User user,
+            Byte perm) {
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("target", target);
+        param.addValue("userid", user.getId());
+        param.addValue("perm", perm);
+        param.addValue("en", new Timestamp(TimeUtils.getNow().getMillis()));
+
+        String sql_new =
+                "select pv.*, pv.perm & :perm as allowed, rh.depth, (select max(depth) from resource_tree \n"
+                        +
+                        "where child_id=rh.child_id) as max_depth from policy_view as pv "
+                        +
+                        "inner join resource_tree as rh on rh.parent_id=pv.id "
+                        +
+                        "where rh.child_id=:target and pv.enable <= :en and (pv.expire > :en or pv.expire is NULL) and "
+                        +
+                        "(pv.group_id='self' or pv.group_id in (select g.group_id from group_users as g where g.user_id=:userid)) and "
+                        +
+                        "(select sum(distinct depth) from resource_tree where child_id=rh.child_id) = "
+                        +
+                        "(select sum(distinct res.depth) from p_view as pos inner join resource_tree as res on res.parent_id=pos.id where (pos.group_id in (select g.group_id from group_users as g "
+                        +
+                        "where g.user_id=:userid) or pos.group_id='self') and res.child_id=rh.child_id group by child_id);";
+
+        try {
+            return this.jdbcTemplate.query(sql_new, param,
+                    new ResultSetExtractor<List<SecurityPolicy>[]>() {
+
+                        @Override
+                        public List<SecurityPolicy>[] extractData(ResultSet rs)
+                                throws SQLException, DataAccessException {
+                            return SecurityRowMappers.mapping(rs, user);
+                        }
+                    });
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Permission Denied for retrieval for '{}' for user '{}'",
+                            target, user.getId());
+            return new List[2];
+        }
+    }
+
+    @Override
+    public List<SecurityPolicy>[] getPolicies(String target, final User user,
+            Byte perm) {
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("target", target);
+        param.addValue("userid", user.getId());
+        param.addValue("perm", perm);
+        param.addValue("en", new Timestamp(TimeUtils.getNow().getMillis()));
+
+        // fixme: missing constraint of user group membership!
+        String sql_new = "select pv.*, pv.perm & :perm as allowed, " +
+                "rh.depth, (select max(depth) from resource_tree " +
+                "where child_id=rh.child_id) as max_depth from p_view as pv " +
+                "inner join resource_tree as rh on rh.parent_id=pv.id " +
+                "where rh.child_id=(select id from resource_store where persistentID=:target) and "
+                +
+                "pv.enable <= :en and (pv.expire > :en or pv.expire is NULL) and "
+                +
+                "(pv.group_id='self' or pv.group_id in (select g.group_id from group_users as g where g.userid=:userid)) and "
+                +
+                "(select sum(distinct depth) from resource_tree where child_id=rh.child_id) = "
+                +
+                "(select sum(distinct res.depth) from p_view as pos inner join resource_tree as res on res.parent_id=pos.id where (pos.group_id in (select g.group_id from group_users "
+                +
+                "as g where g.userid=:userid) or pos.group_id='self') and res.child_id=rh.child_id group by child_id)";
+
+        try {
+            return this.jdbcTemplate.query(sql_new, param,
+                    new ResultSetExtractor<List<SecurityPolicy>[]>() {
+
+                        @Override
+                        public List<SecurityPolicy>[] extractData(ResultSet rs)
+                                throws SQLException, DataAccessException {
+                            return SecurityRowMappers.mapping(rs, user);
+                        }
+                    });
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Permission Denied for retrieval for '{}' for user '{}'",
+                            target, user.getId());
+            return new List[2];
+        }
+    }
+
+    @Override
+    public List<SecurityPolicy>[] findPolicies(String path, final User user,
+            Byte perm) {
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("path", StringUtils.buildSQLRegex(path));
+        param.addValue("userid", user.getId());
+        param.addValue("perm", perm);
+        param.addValue("en", new Timestamp(TimeUtils.getNow().getMillis()));
+
+        String sql_new = "select pv.*, pv.perm & :perm as allowed, " +
+                "rh.depth, (select max(depth) from resource_tree " +
+                "where child_id=rh.child_id) as max_depth from p_view as pv " +
+                "inner join resource_tree as rh on rh.parent_id=pv.id " +
+                "where rt.name_path regexp :path and " +
+                "pv.enable <= :en and (pv.expire > :en or pv.expire is NULL) and "
+                +
+                "(pv.group_id='self' or pv.group_id in (select g.group_id from group_users as g where g.userid=:userid)) and "
+                +
+                "(select sum(distinct depth) from resource_tree where child_id=rh.child_id) = "
+                +
+                "(select sum(distinct res.depth) from p_view as pos inner join resource_tree as res on res.parent_id=pos.id where (pos.group_id in (select g.group_id from group_users "
+                +
+                "as g where g.userid=:userid) or pos.group_id='self') and res.child_id=rh.child_id group by child_id)";
+
+        try {
+            return this.jdbcTemplate.query(sql_new, param,
+                    new ResultSetExtractor<List<SecurityPolicy>[]>() {
+
+                        @Override
+                        public List<SecurityPolicy>[] extractData(ResultSet rs)
+                                throws SQLException, DataAccessException {
+                            return SecurityRowMappers.mapping(rs, user);
+                        }
+                    });
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Permission Denied for retrieval for '{}' for user '{}'",
+                            path, user.getId());
+            return new List[2];
+        }
+    }
+
+    /**
+     * @param path  if set searches in path where the child element equals name. Also applicable for root resources!
+     * @param user
+     * @param clazz
+     * @return
+     */
+    //todo: not working yet!
+    // todo: does not concern itsself with location matching, ever!
+    @Override
+    public List<KorAPResource.Container> getDescending(String path,
+            final User user, Byte b, final Class<? extends KorAPResource> clazz)
+            throws KustvaktException {
+        final MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("userid", user.getId());
+        param.addValue("type", ResourceFactory.getResourceMapping(clazz));
+        param.addValue("part", "%" + path);
+        param.addValue("perm", b);
+
+        String sql;
+        if (path != null && !path.isEmpty()) {
+            sql = "select pv.*, pv.perm & :perm as allowed, rh.depth, rh.name_path, (select max(depth) from resource_tree \n"
+                    +
+                    "where child_id=rh.child_id) as max_depth from p_view as pv "
+                    +
+                    "inner join resource_tree as rh on rh.child_id=pv.id " +
+                    "where pv.type=:type and (rh.name_path like :part) and ((pv.creator=:userid and pv.group_id='self') or "
+                    +
+                    "(pv.group_id in (select g.group_id from group_users as g where g.userid=:userid) and "
+                    +
+                    "(select sum(distinct depth) from resource_tree where child_id=rh.child_id) = "
+                    +
+                    "(select sum(distinct depth) from p_view as pos inner join resource_tree as res on res.parent_id=pos.id "
+                    +
+                    "where pos.group_id in (select g.group_id from group_users as g where g.userid=:userid) "
+                    +
+                    "and res.child_id=rh.child_id group by child_id))) " +
+                    "group by pv.pid, pv.id having count(distinct pv.group_id) = "
+                    +
+                    "((select count(co.group_id) from group_ref as co where co.policyid=pv.pid) or "
+                    +
+                    "(select 1 from p_view as cp2 where cp2.group_id='self' and cp2.id=pv.id)) "
+                    +
+                    "order by rh.depth asc, pv.id desc;";
+        }else {
+            sql = "select pv.*, pv.perm & :perm as allowed, rh.depth, rh.name_path, (select max(depth) from resource_tree \n"
+                    +
+                    "where child_id=rh.child_id) as max_depth from p_view as pv "
+                    +
+                    "inner join resource_tree as rh on rh.child_id=pv.id " +
+                    "where pv.type=:type and ((pv.creator=:userid and pv.group_id='self') or "
+                    +
+                    "(pv.group_id in (select g.group_id from group_users as g where g.userid=:userid) and "
+                    +
+                    "(select sum(distinct depth) from resource_tree where child_id=rh.child_id) = "
+                    +
+                    "(select sum(distinct depth) from p_view as pos inner join resource_tree as res on res.parent_id=pos.id "
+                    +
+                    "where pos.group_id in (select g.group_id from group_users as g where g.userid=:userid) "
+                    +
+                    "and res.child_id=rh.child_id group by child_id))) " +
+                    "group by pv.pid, pv.id having count(distinct pv.group_id) = "
+                    +
+                    "((select count(co.group_id) from group_ref as co where co.policyid=pv.pid) or "
+                    +
+                    "(select 1 from p_view as cp2 where cp2.group_id='self' and cp2.id=pv.id)) "
+                    +
+                    "order by rh.depth asc, pv.id desc;";
+        }
+        try {
+            return this.jdbcTemplate.query(sql, param,
+                    new SecurityRowMappers.HierarchicalResultExtractor());
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Permission Denied for retrieval for path '{}' for user '{}'",
+                            path, user.getId());
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.DB_GET_FAILED, path, clazz.toString());
+        }
+    }
+
+    @Override
+    public List<KorAPResource.Container> getAscending(String name, User user,
+            Byte b, Class<? extends KorAPResource> clazz)
+            throws KustvaktException {
+        final MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("userid", user.getId());
+        param.addValue("type", ResourceFactory.getResourceMapping(clazz));
+        param.addValue("part", "%" + name);
+        param.addValue("perm", b);
+
+        String sql;
+        if (name != null && !name.isEmpty()) {
+            sql = "select pv.*, pv.perm & :perm as allowed, rh.depth, rh.name_path,\n"
+                    +
+                    "(select max(depth) from resource_tree \n" +
+                    "where child_id=rh.child_id) as max_depth from p_view as pv\n"
+                    +
+                    "inner join resource_tree as rh on rh.child_id=pv.id\n" +
+                    "where pv.id in (select rt.parent_id from resource_tree as rt inner join resource_store rs on rs.id=rt.child_id\n"
+                    +
+                    "where rs.type=:type and rt.name_path like :part) and ((pv.creator=:userid and pv.group_id='self') or\n"
+                    +
+                    "(pv.group_id in (select g.group_id from group_users as g where g.userid=:userid) and\n"
+                    +
+                    "(select sum(distinct depth) from resource_tree where child_id=rh.child_id) =\n"
+                    +
+                    "(select sum(distinct depth) from p_view as pos inner join resource_tree as res on res.parent_id=pos.id\n"
+                    +
+                    "where pos.group_id in (select g.group_id from group_users as g where g.userid=:userid)\n"
+                    +
+                    "and res.child_id=rh.child_id group by child_id)))\n" +
+                    "group by pv.pid, pv.id having count(distinct pv.group_id) = \n"
+                    +
+                    "case when pv.creator=:userid then 1 else (select count(distinct co.group_id) "
+                    +
+                    "from group_ref as co where co.policyid=pv.pid) end order by rh.depth desc, pv.id desc;";
+        }else {
+            sql = "select pv.*, pv.perm & :perm as allowed, rh.depth, rh.name_path,\n"
+                    +
+                    "(select max(depth) from resource_tree \n" +
+                    "where child_id=rh.child_id) as max_depth from p_view as pv\n"
+                    +
+                    "inner join resource_tree as rh on rh.child_id=pv.id\n" +
+                    "where pv.id in (select rt.parent_id from resource_tree as rt inner join resource_store rs on rs.id=rt.child_id\n"
+                    +
+                    "where rs.type=:type) and ((pv.creator=:userid and pv.group_id='self') or\n"
+                    +
+                    "(pv.group_id in (select g.group_id from group_users as g where g.userid=:userid) and\n"
+                    +
+                    "(select sum(distinct depth) from resource_tree where child_id=rh.child_id) =\n"
+                    +
+                    "(select sum(distinct depth) from p_view as pos inner join resource_tree as res on res.parent_id=pos.target_id\n"
+                    +
+                    "where pos.group_id in (select g.group_id from group_users as g where g.userid=:userid)\n"
+                    +
+                    "and res.child_id=rh.child_id group by child_id)))\n" +
+                    "group by pv.pid, pv.id having count(distinct pv.group_id) = \n"
+                    +
+                    "case when pv.creator=:userid then 1 else (select count(distinct co.group_id) "
+                    +
+                    "from group_ref as co where co.policyid=pv.pid) end order by rh.depth desc, pv.id desc;";
+        }
+        try {
+            return this.jdbcTemplate.query(sql, param,
+                    new SecurityRowMappers.HierarchicalResultExtractor());
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Permission Denied for retrieval for path '{}' for user '{}'",
+                            name, user.getId());
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.DB_GET_FAILED, name, clazz.toString());
+        }
+    }
+
+    // todo: return all resources or only leave nodes? --> currently only leaves are returned
+    // todo: access to leave node also means that the path to the root for that permission is allowed,
+    // todo: thus all upper resource access is as well allowed
+
+    //todo: remove not used context?! --> who is allowed to do so?
+    @Override
+    public int deletePolicy(SecurityPolicy policy, User user)
+            throws KustvaktException {
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("id", policy.getID());
+
+        try {
+            this.jdbcTemplate
+                    .update("DELETE FROM group_ref WHERE policyid=:id", param);
+            return this.jdbcTemplate
+                    .update("DELETE FROM policy_store WHERE id=:id", param);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (DELETE) not possible for '{}' for user '{}'",
+                            policy.toString(), user.getId());
+            throw new dbException(user.getId(), "policy_store, group_ref",
+                    StatusCodes.DB_DELETE_FAILED, policy.toString());
+        }
+    }
+
+    @Override
+    public void deleteResourcePolicies(String id, User user)
+            throws KustvaktException {
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("id", id);
+        String sql = "DELETE FROM policy_store WHERE target_id=:id;";
+        try {
+            this.jdbcTemplate.update(sql, param);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (DELETE) not possible for '{}' for user '{}'",
+                            id, user.getId());
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.DB_DELETE_FAILED, id);
+        }
+    }
+
+    @Override
+    public int updatePolicy(SecurityPolicy policy, User user)
+            throws KustvaktException {
+        MapSqlParameterSource np = new MapSqlParameterSource();
+        np.addValue("posix", policy.getPermissionByte());
+        np.addValue("en", policy.getContext().getStart());
+        np.addValue("ex", policy.getContext().getEnd());
+        np.addValue("id", policy.getID());
+
+        try {
+            int result = this.jdbcTemplate
+                    .update("UPDATE policy_store SET posix=:posix WHERE id=:id",
+                            np);
+            this.mapConstraints(policy);
+            return result;
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (UPDATE) not possible for '{}' for user '{}'",
+                            policy.toString(), user.getId());
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.DB_UPDATE_FAILED, policy.toString());
+        }
+    }
+
+    @Override
+    public int checkPolicy(SecurityPolicy policy, User user)
+            throws KustvaktException {
+        if (policy.getID() == -1)
+            return 0;
+
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("id", policy.getID());
+        String sql1 = "SELECT COUNT(*) FROM policy_store AS p WHERE p.id=:id;";
+
+        try {
+            return this.jdbcTemplate.queryForObject(sql1, param, Integer.class);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (SELECT) not possible for '{}' for user '{}'",
+                            policy.getTarget(), user.getId());
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.DB_GET_FAILED, policy.toString());
+        }
+    }
+
+    /**
+     * checks if the user is a member of the specified group. Additional ownership can be tested via boolean flag
+     *
+     * @param user
+     * @param group
+     * @param owner
+     * @return
+     * @throws KustvaktException
+     */
+    @Override
+    public int matchCondition(User user, String group, boolean owner)
+            throws KustvaktException {
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        param.addValue("userid", user.getId());
+        param.addValue("group", group);
+        param.addValue("isadmin", BooleanUtils.getBoolean(owner));
+        String sql;
+        if (owner) {
+            sql = "SELECT COUNT(*) FROM group_users AS gu INNER JOIN groupolicy_store AS gs "
+                    +
+                    "ON gs.name=gu.group_id WHERE gu.userID=:userid " +
+                    "AND gs.name=:group AND gu.admin=:isadmin;";
+        }else {
+            sql = "SELECT COUNT(*) FROM group_users AS gu INNER JOIN groupolicy_store AS gs "
+                    +
+                    "ON gs.name=gu.group_id WHERE gu.userID=:userid " +
+                    "AND gs.name=:group;";
+        }
+
+        try {
+            return this.jdbcTemplate.queryForObject(sql, param, Integer.class);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (SELECT) not possible for '{}' for user '{}'",
+                            group, user.getId());
+            throw new dbException(user.getId(), "policy_store",
+                    StatusCodes.DB_GET_FAILED, group);
+        }
+    }
+
+    private Integer createCondition(PolicyCondition condition, User user)
+            throws KustvaktException {
+        MapSqlParameterSource param = new MapSqlParameterSource();
+        KeyHolder key = new GeneratedKeyHolder();
+        param.addValue("name", condition.getSpecifier());
+        param.addValue("ex", condition.getFlags().get(Attributes.EXPORT));
+        param.addValue("qo", condition.getFlags().get(Attributes.QUERY_ONLY));
+        param.addValue("com", condition.getFlags().get(Attributes.COMMERCIAL));
+        param.addValue("sy", condition.getFlags().get(Attributes.SYM_USE));
+        param.addValue("ex", condition.getFlags().get(Attributes.LICENCE));
+        try {
+            this.jdbcTemplate
+                    .update("INSERT INTO group_store (name, sym_use, export, commercial) "
+                            + "VALUES (:name, :sy, :ex, :com);", param, key);
+            return key.getKey().intValue();
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (INSERT) not possible for '{}'",
+                            condition.toString());
+            throw new dbException(user.getId(), "group_store",
+                    StatusCodes.DB_INSERT_FAILED, condition.toString());
+        }
+    }
+
+    @Override
+    public int addToCondition(String username, PolicyCondition condition,
+            boolean admin) throws KustvaktException {
+        final String insert =
+                "INSERT INTO group_users (userID, group_id, admin) " +
+                        "VALUES ((SELECT id FROM korap_users " +
+                        "WHERE username=:username), :group, :status);";
+        try {
+            MapSqlParameterSource param = new MapSqlParameterSource();
+            param.addValue("group", condition.getSpecifier());
+            param.addValue("username", username);
+            param.addValue("status", BooleanUtils.getBoolean(admin));
+            return this.jdbcTemplate.update(insert, param);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (INSERT) not possible for '{}' for user '{}'",
+                            condition.toString(), username);
+            throw new dbException(null, "group_store",
+                    StatusCodes.DB_INSERT_FAILED, condition.toString());
+        }
+    }
+
+    /**
+     * @param usernames
+     * @param condition
+     * @param admin
+     * @return
+     * @throws KustvaktException userID and group_id have a unique constraint,
+     *                        thus: if any of the supplied users is already a member of the group, the entire chain will be broken!
+     */
+    //todo definitely needs rework
+    //todo: test the unique index constraints!
+    @Override
+    public int[] addToCondition(List<String> usernames,
+            PolicyCondition condition, boolean admin) throws KustvaktException {
+        MapSqlParameterSource[] sources = new MapSqlParameterSource[usernames
+                .size()];
+
+        //        todo: use unique index for that! problematic though --> why?
+        //        final String select = "select count(id) from group_users where userID=" +
+        //                "(select id from korap_users where username=:username) " +
+        //                "AND group_id=:group;";
+
+        //todo: use index to create uniqueness. how to batch?
+        final String insert =
+                "INSERT INTO group_users (user_id, group_id, admin) " +
+                        "VALUES ((SELECT id FROM korap_users " +
+                        "WHERE username=:username), :group, :status);";
+        try {
+            for (int idx = 0; idx < usernames.size(); idx++) {
+                if (usernames.get(idx) == null || usernames.get(idx).isEmpty())
+                    throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT);
+
+                MapSqlParameterSource param = new MapSqlParameterSource();
+                param.addValue("group", condition.getSpecifier());
+                param.addValue("username", usernames.get(idx));
+                param.addValue("status", BooleanUtils.getBoolean(admin));
+                // if primary keys uniqueness is determined by both keys, then use
+                // that as checkup (may also be manageable via triggers)
+                //                if (this.jdbcTemplate
+                //                        .queryForObject(select, param, Integer.class) == 0)
+                sources[idx] = param;
+            }
+
+            // todo: only insert if user is not already a member of this group
+            //fixme: problem - unique constraints throws exception. skip that user entry?!
+            return this.jdbcTemplate.batchUpdate(insert, sources);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (INSERT) not possible for '{}' for user '{}'",
+                            condition.toString(), usernames);
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    @Override
+    public void removeFromCondition(List<String> usernames,
+            PolicyCondition condition) throws KustvaktException {
+        MapSqlParameterSource[] sources = new MapSqlParameterSource[usernames
+                .size()];
+        int idx = 0;
+        for (String s : usernames) {
+            MapSqlParameterSource param = new MapSqlParameterSource();
+            param.addValue("group", condition.getSpecifier());
+            param.addValue("username", s);
+            sources[idx++] = param;
+        }
+
+        final String del =
+                "DELETE FROM group_users WHERE group_id=:group AND userID=(SELECT id FROM "
+                        + "korap_users WHERE username=:username);";
+
+        try {
+            this.jdbcTemplate.batchUpdate(del, sources);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (DELETE) not possible for '{}' for user '{}'",
+                            condition.toString(), usernames);
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    @Override
+    public void createParamBinding(Parameter param) throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("key", param.getName());
+        source.addValue("policy", param.getPolicy().getID());
+        source.addValue("value", param.getValue());
+        source.addValue("flag", param.isEquality());
+
+        if (!parameterExists(param.getName()))
+            createParameter(param.getName(), "", param.getOwner());
+        final String insert =
+                "INSERT INTO param_map (paramID, policy_id, value, flag) VALUES ((SELECT id FROM param_store "
+                        + "WHERE p_key=:key), (SELECT id FROM policy_store WHERE id=:policy), :value, :flag);";
+        try {
+            this.jdbcTemplate.update(insert, source);
+        }catch (DataAccessException e) {
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (INSERT) not possible for '{}",
+                            param.toString());
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    @Override
+    public List<String> getUsersFromCondition(PolicyCondition condition)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("specifier", condition.getSpecifier());
+        final String sql1 =
+                "SELECT username FROM korap_users WHERE id IN (SELECT user_id FROM "
+                        + "group_users WHERE group_id=:specifier);";
+        try {
+            return this.jdbcTemplate.queryForList(sql1, source, String.class);
+        }catch (DataAccessException e) {
+            e.printStackTrace();
+            KustvaktLogger.SECURITY_LOGGER
+                    .error("Operation (SELECT) not possible for '{}'",
+                            condition.toString());
+            throw new KustvaktException(StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    private boolean parameterExists(String key) {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("key", key);
+        final String select = "SELECT COUNT(*) FROM param_store WHERE p_key=:key;";
+        return this.jdbcTemplate.queryForObject(select, source, Integer.class)
+                == 1;
+    }
+
+    private void createParameter(String parameter, String value, Integer owner)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("name", parameter);
+        source.addValue("value", value);
+        source.addValue("owner", owner);
+        final String sql = "INSERT INTO param_store (p_key, p_value) VALUES (:name, :value);";
+        try {
+            this.jdbcTemplate.update(sql, source);
+        }catch (DataAccessException e) {
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+    @Override
+    public void removeParamBinding(SecurityPolicy policy)
+            throws KustvaktException {
+        MapSqlParameterSource source = new MapSqlParameterSource();
+        source.addValue("id", policy.getID());
+        final String sql = "DELETE FROM param_map WHERE policy_id=:id";
+        try {
+            this.jdbcTemplate.update(sql, source);
+        }catch (DataAccessException e) {
+            throw new KustvaktException(e, StatusCodes.CONNECTION_ERROR);
+        }
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/security/ac/SecurityRowMappers.java b/src/main/java/de/ids_mannheim/korap/security/ac/SecurityRowMappers.java
new file mode 100644
index 0000000..bf2de64
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/security/ac/SecurityRowMappers.java
@@ -0,0 +1,239 @@
+package de.ids_mannheim.korap.security.ac;
+
+import de.ids_mannheim.korap.ext.resource.KorAPResource;
+import de.ids_mannheim.korap.ext.resource.ResourceFactory;
+import de.ids_mannheim.korap.ext.security.types.PolicyCondition;
+import de.ids_mannheim.korap.ext.security.types.PolicyContext;
+import de.ids_mannheim.korap.ext.security.types.SecurityPolicy;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.PrefixTreeMap;
+import lombok.Data;
+import org.springframework.dao.DataAccessException;
+import org.springframework.jdbc.core.ResultSetExtractor;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.*;
+
+/**
+ * @author hanl
+ * @date 03/03/2014
+ */
+public class SecurityRowMappers {
+
+    public static class PolicyRowMapper implements RowMapper<SecurityPolicy> {
+
+        @Override
+        public SecurityPolicy mapRow(ResultSet rs, int rowNum)
+                throws SQLException {
+            SecurityPolicy p = new SecurityPolicy();
+            p.setID(rs.getInt("pid")).setTarget(rs.getString("persistent_id"))
+                    .setPOSIX(rs.getString("perm"));
+
+            PolicyContext context = new PolicyContext();
+            context.setIPMask(rs.getString("iprange"));
+            Timestamp date = rs.getTimestamp("enable");
+            Timestamp date2 = rs.getTimestamp("expire");
+            if (date != null)
+                context.setEnableTime(date.getTime());
+            if (date2 != null)
+                context.setExpirationTime(date2.getTime());
+            //            context.addFlag("export", rs.getBoolean("export"));
+            //            context.addFlag("sym_use", rs.getInt("sym_use"));
+            p.setContext(context);
+            return p;
+        }
+    }
+
+    @Data
+    public static class FlagContext extends PolicyContext {
+
+        private Map<String, Object> flags;
+
+        public FlagContext() {
+            this.flags = new HashMap<>();
+        }
+
+        public FlagContext addFlag(String key, Object value) {
+            this.flags.put(key, value);
+            return this;
+        }
+
+        public FlagContext removeFlag(String key) {
+            this.flags.remove(key);
+            return this;
+        }
+
+        public FlagContext clearFlags() {
+            this.flags.clear();
+            return this;
+        }
+    }
+
+    public static List<SecurityPolicy>[] mapping(ResultSet rs, User user)
+            throws SQLException {
+        List<SecurityPolicy>[] policyArray = null;
+        List<Integer>[] idx = null;
+        while (rs.next()) {
+            // user has no permission here --> thus skip
+            if (rs.getInt("allowed") == 0)
+                continue;
+
+            if (policyArray == null) {
+                int v = rs.getInt("max_depth") + 1;
+                policyArray = new List[v];
+                idx = new List[v];
+            }
+
+            int depth = rs.getInt("depth");
+
+            if (policyArray[depth] == null) {
+                policyArray[depth] = new ArrayList<>();
+                idx[depth] = new ArrayList<>();
+            }
+
+            Integer pid = rs.getInt("pid");
+            String grouping = rs.getString("group_ref");
+            Integer index = idx[depth].indexOf(pid);
+
+            SecurityPolicy policy;
+            if (index == -1) {
+                if (pid == -1 && grouping.equalsIgnoreCase("self")) {
+                    policy = new SecurityPolicy.OwnerPolicy(
+                            rs.getString("persistent_id"), rs.getInt("creator"));
+                    policyArray[depth].add(0, policy);
+                    idx[depth].add(0, pid);
+                }else {
+                    policy = new SecurityRowMappers.PolicyRowMapper()
+                            .mapRow(rs, 0);
+                    policyArray[depth].add(policy);
+                    idx[depth].add(pid);
+
+                    //todo:
+                    //                    if (policy.isActive(user)) {
+                    //                        policyArray[depth].add(policy);
+                    //                        idx[depth].add(pid);
+                    //                    }
+                }
+            }else
+                policy = policyArray[depth].get(index);
+
+            PolicyCondition c = new PolicyCondition(rs.getString("group_ref"));
+            if (!policy.contains(c))
+                policy.addCondition(c);
+        }
+        return policyArray;
+    }
+
+    @Deprecated
+    public static List<SecurityPolicy>[] map(ResultSet rs) throws SQLException {
+        Map<Integer, SecurityPolicy>[] policyArray = null;
+        while (rs.next()) {
+            // user has no permission here!
+            if (rs.getInt("allowed") == 0)
+                continue;
+
+            if (policyArray == null)
+                policyArray = new Map[rs.getInt("max_depth") + 1];
+
+            int depth = rs.getInt("depth");
+            Map<Integer, SecurityPolicy> cursor = policyArray[depth];
+            if (cursor == null)
+                cursor = new HashMap<>();
+
+            Integer pid = rs.getInt("pid");
+            SecurityPolicy policy = cursor.get(pid);
+            if (policy == null) {
+                policy = new SecurityRowMappers.PolicyRowMapper().mapRow(rs, 0);
+                cursor.put(pid, policy);
+            }
+            PolicyCondition c = new PolicyCondition(rs.getString("group_ref"));
+
+            if (!policy.contains(c))
+                policy.addCondition(c);
+        }
+
+        List<SecurityPolicy>[] results;
+        if (policyArray == null) {
+            results = new List[1];
+            results[0] = new ArrayList<>();
+        }else {
+            results = new List[policyArray.length];
+            for (int idx = 0; idx < policyArray.length; idx++) {
+                if (policyArray[idx] != null)
+                    results[idx] = new ArrayList<>(policyArray[idx].values());
+                else
+                    results[idx] = new ArrayList<>();
+            }
+        }
+        return results;
+    }
+
+    public static class HierarchicalResultExtractor
+            implements ResultSetExtractor<List<KorAPResource.Container>> {
+
+        private boolean _withpid;
+
+        //        public HierarchicalResultExtractor(boolean wpid) {
+        //            this._withpid = wpid;
+        //        }
+
+        // todo: in order for this to work, all parent flags need to be matched in sql!
+        public List<KorAPResource.Container> extractData(ResultSet rs)
+                throws SQLException, DataAccessException {
+            // contains the container with the highest available name_path to retrieve partial matches!
+            PrefixTreeMap<KorAPResource.Container[]> containerMap = new PrefixTreeMap<>();
+            Map<Integer, SecurityPolicy> trace = new HashMap<>();
+
+            while (rs.next()) {
+                KorAPResource.Container[] cursor;
+                Integer pid = rs.getInt("pid");
+
+                SecurityPolicy policy = trace.get(pid);
+                if (policy == null | pid == -1) {
+                    //                    Integer id = rs.getInt("id");
+                    String persistentId = rs.getString("persistent_id");
+                    int depth = rs.getInt("depth");
+                    String namePath = rs.getString("name_path");
+                    policy = new SecurityRowMappers.PolicyRowMapper()
+                            .mapRow(rs, 0);
+
+                    //todo: put active status here!
+                    trace.put(pid, policy);
+
+                    //fixme: since leaves are mentioned first, maybe retrieve
+                    SortedMap<String, KorAPResource.Container[]> submatch;
+                    if ((submatch = containerMap.getPrefixSubMap(namePath))
+                            == null) {
+
+                        cursor = new KorAPResource.Container[depth + 1];
+                        cursor[depth] = new KorAPResource.Container(
+                                persistentId,
+                                ResourceFactory.getResource(rs.getInt("type"))
+                                        .getClass());
+                        containerMap.put(namePath, cursor);
+                    }else {
+                        KorAPResource.Container[] values = submatch
+                                .get(submatch.firstKey());
+                        values[depth] = new KorAPResource.Container(
+                                persistentId,
+                                ResourceFactory.getResource(rs.getInt("type"))
+                                        .getClass());
+                    }
+                }
+            }
+
+            List<KorAPResource.Container> result = new ArrayList<>();
+            for (KorAPResource.Container[] values : containerMap.values()) {
+                for (KorAPResource.Container container : values)
+                    if (container == null)
+                        containerMap.remove(values);
+                result.add(values[values.length - 1]);
+            }
+            return result;
+        }
+    }
+
+}
diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties
new file mode 100644
index 0000000..602c496
--- /dev/null
+++ b/src/main/resources/log4j.properties
@@ -0,0 +1,22 @@
+
+# Root logger option
+#log4j.threshold=ALL
+log4j.rootLogger=INFO, stdout, debugLog
+log4j.logger.log=ERROR, errorLog
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd, HH:mm:ss} %C{6} - %M%n %-5p: %m%n
+
+log4j.appender.errorLog=org.apache.log4j.RollingFileAppender
+log4j.appender.errorLog.layout=org.apache.log4j.PatternLayout
+log4j.appender.errorLog.layout.ConversionPattern=%d{MMM dd, yyyy HH:mm:ss} %C{6} - %M %n %-5p: %m%n
+log4j.appender.errorLog.File=./logs/errors.log
+
+
+log4j.appender.debugLog=org.apache.log4j.RollingFileAppender
+log4j.appender.debugLog.layout=org.apache.log4j.PatternLayout
+log4j.appender.debugLog.layout.ConversionPattern=%d{MMM dd, yyyy HH:mm:ss} %C{6} - %M %n %-5p: %m%n
+log4j.appender.debugLog.File=./logs/logging.log
\ No newline at end of file
diff --git a/src/test/java/ClassLoaderTest.java b/src/test/java/de/ids_mannheim/korap/config/ClassLoaderTest.java
similarity index 77%
rename from src/test/java/ClassLoaderTest.java
rename to src/test/java/de/ids_mannheim/korap/config/ClassLoaderTest.java
index 207ef81..3303b37 100644
--- a/src/test/java/ClassLoaderTest.java
+++ b/src/test/java/de/ids_mannheim/korap/config/ClassLoaderTest.java
@@ -1,7 +1,10 @@
+package de.ids_mannheim.korap.config;
+
 import de.ids_mannheim.korap.config.BeanConfiguration;
 import de.ids_mannheim.korap.config.DefaultHandler;
-import de.ids_mannheim.korap.interfaces.AuditingIface;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
 import de.ids_mannheim.korap.interfaces.defaults.DefaultAuditing;
+import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -10,10 +13,15 @@
  */
 public class ClassLoaderTest {
 
+    @After
+    public void close() {
+        BeanConfiguration.closeApplication();
+    }
+
     @Test
     public void testBeanConfigurationLoaderThrowsNoException() {
-        BeanConfiguration.loadClasspathContext("classpath-config.xml");
-        assert BeanConfiguration.getBeans() != null;
+        BeanConfiguration.loadClasspathContext("default-config.xml");
+        assert BeanConfiguration.hasContext();
     }
 
     @Test
diff --git a/src/test/java/de/ids_mannheim/korap/config/ConfigTest.java b/src/test/java/de/ids_mannheim/korap/config/ConfigTest.java
new file mode 100644
index 0000000..2c11019
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/config/ConfigTest.java
@@ -0,0 +1,65 @@
+package de.ids_mannheim.korap.config;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.ServiceVersion;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @author hanl
+ * @date 02/09/2015
+ */
+public class ConfigTest {
+
+    @After
+    public void close() {
+        BeanConfiguration.closeApplication();
+    }
+
+    @Test
+    public void testServiceVersion() {
+        String v = ServiceVersion.getAPIVersion();
+        Assert.assertNotEquals("wrong version", "UNKNOWN", v);
+    }
+
+    @Test
+    public void testPropertiesOverride() {
+        BeanConfiguration.loadClasspathContext();
+
+        Assert.assertEquals("token layer does not match", "opennlp",
+                BeanConfiguration.getBeans().getConfiguration()
+                        .getDefault_token());
+        Assert.assertEquals("token expiration does not match",
+                TimeUtils.convertTimeToSeconds("150D"),
+                BeanConfiguration.getBeans().getConfiguration()
+                        .getLongTokenTTL());
+
+        BeanConfiguration.getBeans().getConfiguration().setPropertiesAsStream(
+                ConfigTest.class.getClassLoader()
+                        .getResourceAsStream("kustvakt_test.conf"));
+
+        Assert.assertEquals("token layer does not match", "tt",
+                BeanConfiguration.getBeans().getConfiguration()
+                        .getDefault_token());
+        Assert.assertEquals("token expiration does not match",
+                TimeUtils.convertTimeToSeconds("230D"),
+                BeanConfiguration.getBeans().getConfiguration()
+                        .getLongTokenTTL());
+    }
+
+    @Test(expected = KustvaktException.class)
+    public void testBeanOverrideInjection() throws KustvaktException {
+        BeanConfiguration.loadClasspathContext("default-config.xml");
+
+        BeanConfiguration.getBeans().getConfiguration().setPropertiesAsStream(
+                ConfigTest.class.getClassLoader()
+                        .getResourceAsStream("kustvakt_test.conf"));
+
+        String v = "testmail@ids-mannheim.de";
+        BeanConfiguration.getBeans().getEncryption().validateEmail(v);
+    }
+}
+
+
diff --git a/src/test/java/de/ids_mannheim/korap/config/TestHelper.java b/src/test/java/de/ids_mannheim/korap/config/TestHelper.java
new file mode 100644
index 0000000..cbfe2a0
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/config/TestHelper.java
@@ -0,0 +1,10 @@
+package de.ids_mannheim.korap.config;
+
+/**
+ *
+ *
+ * @author hanl
+ * @date 16/10/2015
+ */
+public class TestHelper {
+}
diff --git a/src/test/resources/kustvakt_test.conf b/src/test/resources/kustvakt_test.conf
new file mode 100644
index 0000000..887b854
--- /dev/null
+++ b/src/test/resources/kustvakt_test.conf
@@ -0,0 +1,37 @@
+## index dir
+lucene.indexDir	=	/Users/hanl/Projects/prep_corpus
+
+kustvakt.default.pos = tt
+kustvakt.default.lemma = tt
+kustvakt.default.token = opennlp
+kustvakt.default.dep = mate
+kustvakt.default.const = mate
+
+
+## options referring to the security module!
+
+## token expiration time in minutes!
+## decpricated, no function uses this anymore
+security.absoluteTimeoutDuration = 45M
+
+security.longTokenTTL=150D
+security.tokenTTL=72H
+security.shortTokenTTL=5S
+
+## specifies the user data field that is used to salt user passwords
+security.passcode.salt=accountCreation
+
+security.idleTimeoutDuration = 25M
+security.multipleLogIn = true
+security.loginAttemptNum = 3
+security.authAttemptTTL = 45M
+
+security.encryption.loadFactor = 8
+security.validation.stringLength = 150
+security.validation.emailLength = 50
+security.encryption.algo=BCRYPT
+security.sharedSecret=nHim5JB-YqkX7sS55jayGBnga8WmqgpkzieGe8UhojE
+security.adminToken=f61d02c04a0f18d60172f7b990955824
+
+## applicable: rewrite, foundry, filter, deny
+security.rewrite.strategies=filter, foundry, rewrite
\ No newline at end of file