You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by md...@apache.org on 2015/08/27 13:12:38 UTC

[11/33] syncope git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
index 0000000,0000000..8eaad0e
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
@@@ -1,0 -1,0 +1,218 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.dao;
++
++import java.util.List;
++import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.core.misc.policy.PasswordPolicyException;
++import org.apache.syncope.core.misc.policy.PolicyPattern;
++import org.apache.syncope.core.persistence.api.dao.PasswordRule;
++import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
++import org.apache.syncope.core.persistence.api.entity.PlainAttr;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.springframework.transaction.annotation.Transactional;
++
++@PasswordRuleConfClass(DefaultPasswordRuleConf.class)
++public class DefaultPasswordRule implements PasswordRule {
++
++    private DefaultPasswordRuleConf conf;
++
++    @Override
++    public void setConf(final PasswordRuleConf conf) {
++        if (conf instanceof DefaultPasswordRuleConf) {
++            this.conf = (DefaultPasswordRuleConf) conf;
++        } else {
++            throw new IllegalArgumentException(
++                    PasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
++        }
++    }
++
++    @Transactional(readOnly = true)
++    @Override
++    public void isCompliant(final User user) {
++        for (String schema : conf.getSchemasNotPermitted()) {
++            PlainAttr<?> attr = user.getPlainAttr(schema);
++            if (attr != null) {
++                List<String> values = attr.getValuesAsStrings();
++                if (values != null && !values.isEmpty()) {
++                    conf.getWordsNotPermitted().add(values.get(0));
++                }
++            }
++        }
++
++        String clearPassword = user.getClearPassword();
++        String password = user.getPassword();
++
++        if (password != null && clearPassword != null) {
++            // check length
++            if (conf.getMinLength() > 0 && conf.getMinLength() > clearPassword.length()) {
++                throw new PasswordPolicyException("Password too short");
++            }
++
++            if (conf.getMaxLength() > 0 && conf.getMaxLength() < clearPassword.length()) {
++                throw new PasswordPolicyException("Password too long");
++            }
++
++            // check words not permitted
++            for (String word : conf.getWordsNotPermitted()) {
++                if (clearPassword.contains(word)) {
++                    throw new PasswordPolicyException("Used word(s) not permitted");
++                }
++            }
++
++            // check digits occurrence
++            if (conf.isDigitRequired() && !checkDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain digit(s)");
++            }
++
++            // check lowercase alphabetic characters occurrence
++            if (conf.isLowercaseRequired() && !checkLowercase(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain lowercase alphabetic character(s)");
++            }
++
++            // check uppercase alphabetic characters occurrence
++            if (conf.isUppercaseRequired() && !checkUppercase(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain uppercase alphabetic character(s)");
++            }
++
++            // check prefix
++            for (String prefix : conf.getPrefixesNotPermitted()) {
++                if (clearPassword.startsWith(prefix)) {
++                    throw new PasswordPolicyException("Prefix not permitted");
++                }
++            }
++
++            // check suffix
++            for (String suffix : conf.getSuffixesNotPermitted()) {
++                if (clearPassword.endsWith(suffix)) {
++                    throw new PasswordPolicyException("Suffix not permitted");
++                }
++            }
++
++            // check digit first occurrence
++            if (conf.isMustStartWithDigit() && !checkFirstDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password must start with a digit");
++            }
++
++            if (conf.isMustntStartWithDigit() && checkFirstDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't start with a digit");
++            }
++
++            // check digit last occurrence
++            if (conf.isMustEndWithDigit() && !checkLastDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password must end with a digit");
++            }
++
++            if (conf.isMustntEndWithDigit() && checkLastDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't end with a digit");
++            }
++
++            // check alphanumeric characters occurence
++            if (conf.isAlphanumericRequired() && !checkAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain alphanumeric character(s)");
++            }
++
++            // check non alphanumeric characters occurence
++            if (conf.isNonAlphanumericRequired() && !checkNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain non-alphanumeric character(s)");
++            }
++
++            // check alphanumeric character first occurrence
++            if (conf.isMustStartWithAlpha() && !checkFirstAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must start with an alphanumeric character");
++            }
++
++            if (conf.isMustntStartWithAlpha() && checkFirstAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't start with an alphanumeric character");
++            }
++
++            // check alphanumeric character last occurrence
++            if (conf.isMustEndWithAlpha() && !checkLastAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must end with an alphanumeric character");
++            }
++
++            if (conf.isMustntEndWithAlpha() && checkLastAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't end with an alphanumeric character");
++            }
++
++            // check non alphanumeric character first occurrence
++            if (conf.isMustStartWithNonAlpha() && !checkFirstNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must start with a non-alphanumeric character");
++            }
++
++            if (conf.isMustntStartWithNonAlpha() && checkFirstNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't start with a non-alphanumeric character");
++            }
++
++            // check non alphanumeric character last occurrence
++            if (conf.isMustEndWithNonAlpha() && !checkLastNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must end with a non-alphanumeric character");
++            }
++
++            if (conf.isMustntEndWithNonAlpha() && checkLastNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't end with a non-alphanumeric character");
++            }
++        }
++    }
++
++    private boolean checkDigit(final String str) {
++        return PolicyPattern.DIGIT.matcher(str).matches();
++    }
++
++    private boolean checkLowercase(final String str) {
++        return PolicyPattern.ALPHA_LOWERCASE.matcher(str).matches();
++    }
++
++    private boolean checkUppercase(final String str) {
++        return PolicyPattern.ALPHA_UPPERCASE.matcher(str).matches();
++    }
++
++    private boolean checkFirstDigit(final String str) {
++        return PolicyPattern.FIRST_DIGIT.matcher(str).matches();
++    }
++
++    private boolean checkLastDigit(final String str) {
++        return PolicyPattern.LAST_DIGIT.matcher(str).matches();
++    }
++
++    private boolean checkAlphanumeric(final String str) {
++        return PolicyPattern.ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkFirstAlphanumeric(final String str) {
++        return PolicyPattern.FIRST_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkLastAlphanumeric(final String str) {
++        return PolicyPattern.LAST_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkNonAlphanumeric(final String str) {
++        return PolicyPattern.NON_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkFirstNonAlphanumeric(final String str) {
++        return PolicyPattern.FIRST_NON_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkLastNonAlphanumeric(final String str) {
++        return PolicyPattern.LAST_NON_ALPHANUMERIC.matcher(str).matches();
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java
index 0000000,0000000..843bbcd
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java
@@@ -1,0 -1,0 +1,67 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.EnumType;
++import javax.persistence.Enumerated;
++import javax.persistence.Id;
++import javax.persistence.Inheritance;
++import javax.persistence.InheritanceType;
++import javax.validation.constraints.NotNull;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.persistence.api.entity.Policy;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
++public abstract class AbstractPolicy extends AbstractEntity<Long> implements Policy {
++
++    private static final long serialVersionUID = -5844833125843247458L;
++
++    @Id
++    private Long id;
++
++    @NotNull
++    private String description;
++
++    @NotNull
++    @Enumerated(EnumType.STRING)
++    protected PolicyType type;
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    @Override
++    public String getDescription() {
++        return description;
++    }
++
++    @Override
++    public void setDescription(final String description) {
++        this.description = description;
++    }
++
++    @Override
++    public PolicyType getType() {
++        return type;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
index 0000000,0000000..87aa04f
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
@@@ -1,0 -1,0 +1,160 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import java.util.ArrayList;
++import java.util.HashSet;
++import java.util.List;
++import java.util.Set;
++import javax.persistence.Basic;
++import javax.persistence.CascadeType;
++import javax.persistence.Entity;
++import javax.persistence.FetchType;
++import javax.persistence.JoinColumn;
++import javax.persistence.JoinTable;
++import javax.persistence.ManyToMany;
++import javax.persistence.OneToMany;
++import javax.persistence.Table;
++import javax.validation.constraints.Max;
++import javax.validation.constraints.Min;
++import org.apache.commons.collections4.CollectionUtils;
++import org.apache.commons.collections4.Predicate;
++import org.apache.commons.collections4.Transformer;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
++import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
++import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
++
++@Entity
++@Table(name = JPAAccountPolicy.TABLE)
++public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
++
++    private static final long serialVersionUID = -2767606675667839060L;
++
++    public static final String TABLE = "AccountPolicy";
++
++    @Basic
++    @Min(0)
++    @Max(1)
++    private Integer propagateSuspension;
++
++    private int maxAuthenticationAttempts;
++
++    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "accountPolicy")
++    private List<JPAAccountRuleConfInstance> ruleConfs;
++
++    /**
++     * Resources for alternative user authentication: if empty, only internal storage will be used.
++     */
++    @ManyToMany(fetch = FetchType.EAGER)
++    @JoinTable(joinColumns =
++            @JoinColumn(name = "accountPolicy_id"),
++            inverseJoinColumns =
++            @JoinColumn(name = "resource_name"))
++    private Set<JPAExternalResource> resources = new HashSet<>();
++
++    public JPAAccountPolicy() {
++        super();
++        this.type = PolicyType.ACCOUNT;
++    }
++
++    @Override
++    public boolean isPropagateSuspension() {
++        return isBooleanAsInteger(propagateSuspension);
++    }
++
++    @Override
++    public void setPropagateSuspension(final boolean propagateSuspension) {
++        this.propagateSuspension = getBooleanAsInteger(propagateSuspension);
++    }
++
++    @Override
++    public int getMaxAuthenticationAttempts() {
++        return maxAuthenticationAttempts;
++    }
++
++    @Override
++    public void setMaxAuthenticationAttempts(final int maxAuthenticationAttempts) {
++        this.maxAuthenticationAttempts = maxAuthenticationAttempts;
++    }
++
++    @Override
++    public boolean add(final AccountRuleConf accountRuleConf) {
++        if (accountRuleConf == null) {
++            return false;
++        }
++
++        JPAAccountRuleConfInstance instance = new JPAAccountRuleConfInstance();
++        instance.setAccountPolicy(this);
++        instance.setInstance(accountRuleConf);
++
++        return ruleConfs.add(instance);
++    }
++
++    @Override
++    public boolean remove(final AccountRuleConf accountRuleConf) {
++        return CollectionUtils.filter(ruleConfs, new Predicate<JPAAccountRuleConfInstance>() {
++
++            @Override
++            public boolean evaluate(final JPAAccountRuleConfInstance object) {
++                return accountRuleConf.equals(object.getInstance());
++            }
++        });
++    }
++
++    @Override
++    public List<AccountRuleConf> getRuleConfs() {
++        return CollectionUtils.collect(ruleConfs, new Transformer<JPAAccountRuleConfInstance, AccountRuleConf>() {
++
++            @Override
++            public AccountRuleConf transform(final JPAAccountRuleConfInstance input) {
++                return input.getInstance();
++            }
++        }, new ArrayList<AccountRuleConf>());
++    }
++
++    @Override
++    public boolean add(final ExternalResource resource) {
++        checkType(resource, JPAExternalResource.class);
++        return resources.add((JPAExternalResource) resource);
++    }
++
++    @Override
++    public boolean remove(final ExternalResource resource) {
++        checkType(resource, JPAExternalResource.class);
++        return resources.remove((JPAExternalResource) resource);
++    }
++
++    @Override
++    public Set<? extends ExternalResource> getResources() {
++        return resources;
++    }
++
++    @Override
++    public Set<String> getResourceNames() {
++        return CollectionUtils.collect(getResources(), new Transformer<ExternalResource, String>() {
++
++            @Override
++            public String transform(final ExternalResource input) {
++                return input.getKey();
++            }
++        }, new HashSet<String>());
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
index 0000000,0000000..782e6b4
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
@@@ -1,0 -1,0 +1,73 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Id;
++import javax.persistence.Lob;
++import javax.persistence.ManyToOne;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Table(name = JPAAccountRuleConfInstance.TABLE)
++public class JPAAccountRuleConfInstance extends AbstractEntity<Long> {
++
++    private static final long serialVersionUID = -2436055132955674610L;
++
++    public static final String TABLE = "AccountRuleConfInstance";
++
++    @Id
++    private Long id;
++
++    @Lob
++    private String serializedInstance;
++
++    @ManyToOne
++    private JPAAccountPolicy accountPolicy;
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    public AccountPolicy getAccountPolicy() {
++        return accountPolicy;
++    }
++
++    public void setAccountPolicy(final AccountPolicy report) {
++        checkType(report, JPAAccountPolicy.class);
++        this.accountPolicy = (JPAAccountPolicy) report;
++    }
++
++    public AccountRuleConf getInstance() {
++        return serializedInstance == null
++                ? null
++                : POJOHelper.deserialize(serializedInstance, AccountRuleConf.class);
++    }
++
++    public void setInstance(final AccountRuleConf instance) {
++        this.serializedInstance = instance == null
++                ? null
++                : POJOHelper.serialize(instance);
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
index 0000000,0000000..1491c25
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
@@@ -1,0 -1,0 +1,115 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import java.util.ArrayList;
++import java.util.List;
++import javax.persistence.Basic;
++import javax.persistence.CascadeType;
++import javax.persistence.Entity;
++import javax.persistence.FetchType;
++import javax.persistence.OneToMany;
++import javax.persistence.Table;
++import javax.validation.constraints.Max;
++import javax.validation.constraints.Min;
++import org.apache.commons.collections4.CollectionUtils;
++import org.apache.commons.collections4.Predicate;
++import org.apache.commons.collections4.Transformer;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
++
++@Entity
++@Table(name = JPAPasswordPolicy.TABLE)
++public class JPAPasswordPolicy extends AbstractPolicy implements PasswordPolicy {
++
++    private static final long serialVersionUID = 9138550910385232849L;
++
++    public static final String TABLE = "PasswordPolicy";
++
++    @Basic
++    @Min(0)
++    @Max(1)
++    private Integer allowNullPassword;
++
++    private int historyLength;
++
++    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "passwordPolicy")
++    private List<JPAPasswordRuleConfInstance> ruleConfs;
++
++    public JPAPasswordPolicy() {
++        super();
++        this.type = PolicyType.PASSWORD;
++    }
++
++    @Override
++    public boolean isAllowNullPassword() {
++        return isBooleanAsInteger(allowNullPassword);
++    }
++
++    @Override
++    public void setAllowNullPassword(final boolean allowNullPassword) {
++        this.allowNullPassword = getBooleanAsInteger(allowNullPassword);
++    }
++
++    @Override
++    public int getHistoryLength() {
++        return historyLength;
++    }
++
++    @Override
++    public void setHistoryLength(final int historyLength) {
++        this.historyLength = historyLength;
++    }
++
++    @Override
++    public boolean add(final PasswordRuleConf passwordRuleConf) {
++        if (passwordRuleConf == null) {
++            return false;
++        }
++
++        JPAPasswordRuleConfInstance instance = new JPAPasswordRuleConfInstance();
++        instance.setPasswordPolicy(this);
++        instance.setInstance(passwordRuleConf);
++
++        return ruleConfs.add(instance);
++    }
++
++    @Override
++    public boolean remove(final PasswordRuleConf passwordRuleConf) {
++        return CollectionUtils.filter(ruleConfs, new Predicate<JPAPasswordRuleConfInstance>() {
++
++            @Override
++            public boolean evaluate(final JPAPasswordRuleConfInstance object) {
++                return passwordRuleConf.equals(object.getInstance());
++            }
++        });
++    }
++
++    @Override
++    public List<PasswordRuleConf> getRuleConfs() {
++        return CollectionUtils.collect(ruleConfs, new Transformer<JPAPasswordRuleConfInstance, PasswordRuleConf>() {
++
++            @Override
++            public PasswordRuleConf transform(final JPAPasswordRuleConfInstance input) {
++                return input.getInstance();
++            }
++        }, new ArrayList<PasswordRuleConf>());
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
index 0000000,0000000..4b39dc8
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
@@@ -1,0 -1,0 +1,73 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Id;
++import javax.persistence.Lob;
++import javax.persistence.ManyToOne;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Table(name = JPAPasswordRuleConfInstance.TABLE)
++public class JPAPasswordRuleConfInstance extends AbstractEntity<Long> {
++
++    private static final long serialVersionUID = -2436055132955674610L;
++
++    public static final String TABLE = "PasswordRuleConfInstance";
++
++    @Id
++    private Long id;
++
++    @Lob
++    private String serializedInstance;
++
++    @ManyToOne
++    private JPAPasswordPolicy passwordPolicy;
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    public PasswordPolicy getPasswordPolicy() {
++        return passwordPolicy;
++    }
++
++    public void setPasswordPolicy(final PasswordPolicy report) {
++        checkType(report, JPAPasswordPolicy.class);
++        this.passwordPolicy = (JPAPasswordPolicy) report;
++    }
++
++    public PasswordRuleConf getInstance() {
++        return serializedInstance == null
++                ? null
++                : POJOHelper.deserialize(serializedInstance, PasswordRuleConf.class);
++    }
++
++    public void setInstance(final PasswordRuleConf instance) {
++        this.serializedInstance = instance == null
++                ? null
++                : POJOHelper.serialize(instance);
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushPolicy.java
index 0000000,0000000..3bc0009
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushPolicy.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Lob;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.common.lib.policy.PushPolicySpec;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
++
++@Entity
++@Table(name = JPAPushPolicy.TABLE)
++public class JPAPushPolicy extends AbstractPolicy implements PushPolicy {
++
++    private static final long serialVersionUID = -5875589156893921113L;
++
++    public static final String TABLE = "PushPolicy";
++
++    @Lob
++    private String specification;
++
++    public JPAPushPolicy() {
++        super();
++        this.type = PolicyType.PUSH;
++    }
++
++    @Override
++    public PushPolicySpec getSpecification() {
++        return POJOHelper.deserialize(specification, PushPolicySpec.class);
++    }
++
++    @Override
++    public void setSpecification(final PushPolicySpec policy) {
++        this.specification = POJOHelper.serialize(policy);
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPASyncPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPASyncPolicy.java
index 0000000,0000000..913ee54
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPASyncPolicy.java
@@@ -1,0 -1,0 +1,54 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Lob;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.policy.SyncPolicySpec;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
++
++@Entity
++@Table(name = JPASyncPolicy.TABLE)
++public class JPASyncPolicy extends AbstractPolicy implements SyncPolicy {
++
++    private static final long serialVersionUID = -6090413855809521279L;
++
++    public static final String TABLE = "SyncPolicy";
++
++    @Lob
++    private String specification;
++
++    public JPASyncPolicy() {
++        super();
++        this.type = PolicyType.SYNC;
++    }
++
++    @Override
++    public SyncPolicySpec getSpecification() {
++        return POJOHelper.deserialize(specification, SyncPolicySpec.class);
++    }
++
++    @Override
++    public void setSpecification(final SyncPolicySpec policy) {
++        this.specification = POJOHelper.serialize(policy);
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
index 0000000,0000000..36c52a3
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
@@@ -1,0 -1,0 +1,96 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.task;
++
++import java.util.ArrayList;
++import java.util.List;
++
++import javax.persistence.CascadeType;
++import javax.persistence.DiscriminatorColumn;
++import javax.persistence.Entity;
++import javax.persistence.EnumType;
++import javax.persistence.Enumerated;
++import javax.persistence.Id;
++import javax.persistence.Inheritance;
++import javax.persistence.InheritanceType;
++import javax.persistence.OneToMany;
++import javax.persistence.Table;
++import javax.validation.constraints.NotNull;
++import org.apache.syncope.common.lib.types.TaskType;
++import org.apache.syncope.core.persistence.api.entity.task.Task;
++import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Table(name = AbstractTask.TABLE)
++@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
++@DiscriminatorColumn(name = "DTYPE")
++public abstract class AbstractTask extends AbstractEntity<Long> implements Task {
++
++    private static final long serialVersionUID = 5837401178128177511L;
++
++    public static final String TABLE = "Task";
++
++    /**
++     * Id.
++     */
++    @Id
++    private Long id;
++
++    @NotNull
++    @Enumerated(EnumType.STRING)
++    protected TaskType type;
++
++    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
++    private List<JPATaskExec> executions;
++
++    public AbstractTask() {
++        super();
++
++        executions = new ArrayList<>();
++    }
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    @Override
++    public TaskType getType() {
++        return type;
++    }
++
++    @Override
++    public boolean addExec(final TaskExec exec) {
++        checkType(exec, JPATaskExec.class);
++        return exec != null && !executions.contains((JPATaskExec) exec) && executions.add((JPATaskExec) exec);
++    }
++
++    @Override
++    public boolean removeExec(final TaskExec exec) {
++        checkType(exec, JPATaskExec.class);
++        return exec != null && executions.remove((JPATaskExec) exec);
++    }
++
++    @Override
++    public List<? extends TaskExec> getExecs() {
++        return executions;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRule.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRule.java
index 0000000,0000000..2b67f37
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRule.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.fit.core.reference;
++
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.core.misc.policy.AccountPolicyException;
++import org.apache.syncope.core.persistence.api.dao.AccountRule;
++import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.springframework.transaction.annotation.Transactional;
++
++@AccountRuleConfClass(TestAccountRuleConf.class)
++public class TestAccountRule implements AccountRule {
++
++    private TestAccountRuleConf conf;
++
++    @Transactional(readOnly = true)
++    @Override
++    public void enforce(final AccountRuleConf conf, final User user) {
++        if (conf instanceof TestAccountRuleConf) {
++            this.conf = TestAccountRuleConf.class.cast(conf);
++        } else {
++            throw new IllegalArgumentException(
++                    AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
++        }
++
++        if (!user.getUsername().contains(this.conf.getMustContainSubstring())) {
++            throw new AccountPolicyException("Username not containing " + this.conf.getMustContainSubstring());
++        }
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
index 0000000,0000000..8903058
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.fit.core.reference;
++
++import org.apache.syncope.common.lib.AbstractBaseBean;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++
++public class TestAccountRuleConf extends AbstractBaseBean implements AccountRuleConf {
++
++    private static final long serialVersionUID = -1803947511928491978L;
++
++    @Override
++    public String getName() {
++        return getClass().getName();
++    }
++
++    public String getMustContainSubstring() {
++        return "YYY";
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
index 0000000,0000000..2b67f37
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.fit.core.reference;
++
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.core.misc.policy.AccountPolicyException;
++import org.apache.syncope.core.persistence.api.dao.AccountRule;
++import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.springframework.transaction.annotation.Transactional;
++
++@AccountRuleConfClass(TestAccountRuleConf.class)
++public class TestAccountRule implements AccountRule {
++
++    private TestAccountRuleConf conf;
++
++    @Transactional(readOnly = true)
++    @Override
++    public void enforce(final AccountRuleConf conf, final User user) {
++        if (conf instanceof TestAccountRuleConf) {
++            this.conf = TestAccountRuleConf.class.cast(conf);
++        } else {
++            throw new IllegalArgumentException(
++                    AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
++        }
++
++        if (!user.getUsername().contains(this.conf.getMustContainSubstring())) {
++            throw new AccountPolicyException("Username not containing " + this.conf.getMustContainSubstring());
++        }
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
index 0000000,0000000..8903058
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.fit.core.reference;
++
++import org.apache.syncope.common.lib.AbstractBaseBean;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++
++public class TestAccountRuleConf extends AbstractBaseBean implements AccountRuleConf {
++
++    private static final long serialVersionUID = -1803947511928491978L;
++
++    @Override
++    public String getName() {
++        return getClass().getName();
++    }
++
++    public String getMustContainSubstring() {
++        return "YYY";
++    }
++}