You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/07/13 11:17:40 UTC

[3/3] syncope git commit: [SYNCOPE-678] Merge from 1_2_X

[SYNCOPE-678] Merge from 1_2_X


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/f45f2d9b
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/f45f2d9b
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/f45f2d9b

Branch: refs/heads/master
Commit: f45f2d9bf1ff5d7e2eb177fc07660916fbfe4ca6
Parents: aac3314 73d0975
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Jul 13 11:17:16 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Jul 13 11:17:16 2015 +0200

----------------------------------------------------------------------
 .../core/misc/security/PasswordGenerator.java   | 19 +++--
 .../misc/security/PasswordGeneratorTest.java    | 84 ++++++++++++--------
 2 files changed, 64 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/f45f2d9b/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
----------------------------------------------------------------------
diff --cc core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
index 48cafa3,0000000..eab59bb
mode 100644,000000..100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
@@@ -1,318 -1,0 +1,327 @@@
 +/*
 + * 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.misc.security;
 +
 +import java.util.ArrayList;
 +import java.util.List;
 +
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 +import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
 +import org.apache.syncope.core.misc.policy.PolicyPattern;
 +import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.Realm;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +
 +/**
 + * Generate random passwords according to given policies.
++ * When no minimum and / or maximum length are specified, default values are set.
 + *
 + * @see org.apache.syncope.core.persistence.api.entity.PasswordPolicy
 + */
 +@Component
 +public class PasswordGenerator {
 +
 +    private static final char[] SPECIAL_CHARS = { '!', '£', '%', '&', '(', ')', '?', '#', '$' };
 +
++    private static final int VERY_MIN_LENGTH = 0;
++
++    private static final int VERY_MAX_LENGTH = 64;
++
++    private static final int MIN_LENGTH_IF_ZERO = 6;
++
 +    @Autowired
 +    private UserDAO userDAO;
 +
 +    @Autowired
 +    private RealmDAO realmDAO;
 +
 +    public String generate(final List<PasswordPolicySpec> ppSpecs)
 +            throws InvalidPasswordPolicySpecException {
 +
 +        PasswordPolicySpec policySpec = merge(ppSpecs);
 +
 +        check(policySpec);
 +
 +        return generate(policySpec);
 +    }
 +
 +    public String generate(final User user)
 +            throws InvalidPasswordPolicySpecException {
 +
 +        List<PasswordPolicySpec> ppSpecs = new ArrayList<>();
 +
 +        for (Realm ancestor : realmDAO.findAncestors(user.getRealm())) {
 +            if (ancestor.getPasswordPolicy() != null
 +                    && ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
 +
 +                ppSpecs.add(ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
 +            }
 +        }
 +
 +        for (ExternalResource resource : userDAO.findAllResources(user)) {
 +            if (resource.getPasswordPolicy() != null
 +                    && resource.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
 +
 +                ppSpecs.add(resource.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
 +            }
 +        }
 +
 +        PasswordPolicySpec policySpec = merge(ppSpecs);
 +        check(policySpec);
 +        return generate(policySpec);
 +    }
 +
 +    private PasswordPolicySpec merge(final List<PasswordPolicySpec> ppSpecs) {
 +        PasswordPolicySpec fpps = new PasswordPolicySpec();
-         fpps.setMinLength(0);
-         fpps.setMaxLength(1000);
++        fpps.setMinLength(VERY_MIN_LENGTH);
++        fpps.setMaxLength(VERY_MAX_LENGTH);
 +
 +        for (PasswordPolicySpec policySpec : ppSpecs) {
 +            if (policySpec.getMinLength() > fpps.getMinLength()) {
 +                fpps.setMinLength(policySpec.getMinLength());
 +            }
 +
 +            if ((policySpec.getMaxLength() != 0) && ((policySpec.getMaxLength() < fpps.getMaxLength()))) {
 +                fpps.setMaxLength(policySpec.getMaxLength());
 +            }
 +            fpps.getPrefixesNotPermitted().addAll(policySpec.getPrefixesNotPermitted());
 +            fpps.getSuffixesNotPermitted().addAll(policySpec.getSuffixesNotPermitted());
 +
 +            if (!fpps.isNonAlphanumericRequired()) {
 +                fpps.setNonAlphanumericRequired(policySpec.isNonAlphanumericRequired());
 +            }
 +
 +            if (!fpps.isAlphanumericRequired()) {
 +                fpps.setAlphanumericRequired(policySpec.isAlphanumericRequired());
 +            }
 +            if (!fpps.isDigitRequired()) {
 +                fpps.setDigitRequired(policySpec.isDigitRequired());
 +            }
 +
 +            if (!fpps.isLowercaseRequired()) {
 +                fpps.setLowercaseRequired(policySpec.isLowercaseRequired());
 +            }
 +            if (!fpps.isUppercaseRequired()) {
 +                fpps.setUppercaseRequired(policySpec.isUppercaseRequired());
 +            }
 +            if (!fpps.isMustStartWithDigit()) {
 +                fpps.setMustStartWithDigit(policySpec.isMustStartWithDigit());
 +            }
 +            if (!fpps.isMustntStartWithDigit()) {
 +                fpps.setMustntStartWithDigit(policySpec.isMustntStartWithDigit());
 +            }
 +            if (!fpps.isMustEndWithDigit()) {
 +                fpps.setMustEndWithDigit(policySpec.isMustEndWithDigit());
 +            }
 +            if (fpps.isMustntEndWithDigit()) {
 +                fpps.setMustntEndWithDigit(policySpec.isMustntEndWithDigit());
 +            }
 +            if (!fpps.isMustStartWithAlpha()) {
 +                fpps.setMustStartWithAlpha(policySpec.isMustStartWithAlpha());
 +            }
 +            if (!fpps.isMustntStartWithAlpha()) {
 +                fpps.setMustntStartWithAlpha(policySpec.isMustntStartWithAlpha());
 +            }
 +            if (!fpps.isMustStartWithNonAlpha()) {
 +                fpps.setMustStartWithNonAlpha(policySpec.isMustStartWithNonAlpha());
 +            }
 +            if (!fpps.isMustntStartWithNonAlpha()) {
 +                fpps.setMustntStartWithNonAlpha(policySpec.isMustntStartWithNonAlpha());
 +            }
 +            if (!fpps.isMustEndWithNonAlpha()) {
 +                fpps.setMustEndWithNonAlpha(policySpec.isMustEndWithNonAlpha());
 +            }
 +            if (!fpps.isMustntEndWithNonAlpha()) {
 +                fpps.setMustntEndWithNonAlpha(policySpec.isMustntEndWithNonAlpha());
 +            }
 +            if (!fpps.isMustEndWithAlpha()) {
 +                fpps.setMustEndWithAlpha(policySpec.isMustEndWithAlpha());
 +            }
 +            if (!fpps.isMustntEndWithAlpha()) {
 +                fpps.setMustntEndWithAlpha(policySpec.isMustntEndWithAlpha());
 +            }
 +        }
++
++        if (fpps.getMinLength() == 0) {
++            fpps.setMinLength(fpps.getMaxLength() < MIN_LENGTH_IF_ZERO ? fpps.getMaxLength() : MIN_LENGTH_IF_ZERO);
++        }
++
 +        return fpps;
 +    }
 +
 +    private void check(final PasswordPolicySpec policySpec)
 +            throws InvalidPasswordPolicySpecException {
 +
-         if (policySpec.getMinLength() == 0) {
-             throw new InvalidPasswordPolicySpecException("Minimum length is zero");
-         }
 +        if (policySpec.isMustEndWithAlpha() && policySpec.isMustntEndWithAlpha()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustEndWithAlpha and mustntEndWithAlpha are both true");
 +        }
 +        if (policySpec.isMustEndWithAlpha() && policySpec.isMustEndWithDigit()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustEndWithAlpha and mustEndWithDigit are both true");
 +        }
 +        if (policySpec.isMustEndWithDigit() && policySpec.isMustntEndWithDigit()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustEndWithDigit and mustntEndWithDigit are both true");
 +        }
 +        if (policySpec.isMustEndWithNonAlpha() && policySpec.isMustntEndWithNonAlpha()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustEndWithNonAlpha and mustntEndWithNonAlpha are both true");
 +        }
 +        if (policySpec.isMustStartWithAlpha() && policySpec.isMustntStartWithAlpha()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustStartWithAlpha and mustntStartWithAlpha are both true");
 +        }
 +        if (policySpec.isMustStartWithAlpha() && policySpec.isMustStartWithDigit()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustStartWithAlpha and mustStartWithDigit are both true");
 +        }
 +        if (policySpec.isMustStartWithDigit() && policySpec.isMustntStartWithDigit()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustStartWithDigit and mustntStartWithDigit are both true");
 +        }
 +        if (policySpec.isMustStartWithNonAlpha() && policySpec.isMustntStartWithNonAlpha()) {
 +            throw new InvalidPasswordPolicySpecException(
 +                    "mustStartWithNonAlpha and mustntStartWithNonAlpha are both true");
 +        }
 +        if (policySpec.getMinLength() > policySpec.getMaxLength()) {
 +            throw new InvalidPasswordPolicySpecException("Minimun length (" + policySpec.getMinLength() + ")"
 +                    + "is greater than maximum length (" + policySpec.getMaxLength() + ")");
 +        }
 +    }
 +
 +    private String generate(final PasswordPolicySpec policySpec) {
 +        String[] generatedPassword = new String[policySpec.getMinLength()];
 +
 +        for (int i = 0; i < generatedPassword.length; i++) {
 +            generatedPassword[i] = "";
 +        }
 +
 +        checkStartChar(generatedPassword, policySpec);
 +
 +        checkEndChar(generatedPassword, policySpec);
 +
 +        checkRequired(generatedPassword, policySpec);
 +
 +        //filled empty chars
 +        for (int firstEmptyChar = firstEmptyChar(generatedPassword);
 +                firstEmptyChar < generatedPassword.length - 1; firstEmptyChar++) {
 +
 +            generatedPassword[firstEmptyChar] = SecureRandomUtils.generateRandomLetter();
 +        }
 +
 +        checkPrefixAndSuffix(generatedPassword, policySpec);
 +
 +        return StringUtils.join(generatedPassword);
 +    }
 +
 +    private void checkStartChar(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
 +        if (policySpec.isMustStartWithAlpha()) {
 +            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
 +        }
 +        if (policySpec.isMustStartWithNonAlpha() || policySpec.isMustStartWithDigit()) {
 +            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
 +        }
 +        if (policySpec.isMustntStartWithAlpha()) {
 +            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
 +        }
 +        if (policySpec.isMustntStartWithDigit()) {
 +            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
 +        }
 +        if (policySpec.isMustntStartWithNonAlpha()) {
 +            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
 +        }
 +    }
 +
 +    private void checkEndChar(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
 +        if (policySpec.isMustEndWithAlpha()) {
 +            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
 +        }
 +        if (policySpec.isMustEndWithNonAlpha() || policySpec.isMustEndWithDigit()) {
 +            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
 +        }
 +
 +        if (policySpec.isMustntEndWithAlpha()) {
 +            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
 +        }
 +        if (policySpec.isMustntEndWithDigit()) {
 +            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
 +        }
 +        if (policySpec.isMustntEndWithNonAlpha()) {
 +            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
 +        }
 +    }
 +
 +    private int firstEmptyChar(final String[] generatedPStrings) {
 +        int index = 0;
 +        while (!generatedPStrings[index].isEmpty()) {
 +            index++;
 +        }
 +        return index;
 +    }
 +
 +    private void checkRequired(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
 +        if (policySpec.isDigitRequired()
 +                && !PolicyPattern.DIGIT.matcher(StringUtils.join(generatedPassword)).matches()) {
 +
 +            generatedPassword[firstEmptyChar(generatedPassword)] = SecureRandomUtils.generateRandomNumber();
 +        }
 +
 +        if (policySpec.isUppercaseRequired()
 +                && !PolicyPattern.ALPHA_UPPERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
 +
 +            generatedPassword[firstEmptyChar(generatedPassword)] =
 +                    SecureRandomUtils.generateRandomLetter().toUpperCase();
 +        }
 +
 +        if (policySpec.isLowercaseRequired()
 +                && !PolicyPattern.ALPHA_LOWERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
 +
 +            generatedPassword[firstEmptyChar(generatedPassword)] =
 +                    SecureRandomUtils.generateRandomLetter().toLowerCase();
 +        }
 +
 +        if (policySpec.isNonAlphanumericRequired()
 +                && !PolicyPattern.NON_ALPHANUMERIC.matcher(StringUtils.join(generatedPassword)).matches()) {
 +
 +            generatedPassword[firstEmptyChar(generatedPassword)] =
 +                    SecureRandomUtils.generateRandomSpecialCharacter(SPECIAL_CHARS);
 +        }
 +    }
 +
 +    private void checkPrefixAndSuffix(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
 +        for (String prefix : policySpec.getPrefixesNotPermitted()) {
 +            if (StringUtils.join(generatedPassword).startsWith(prefix)) {
 +                checkStartChar(generatedPassword, policySpec);
 +            }
 +        }
 +
 +        for (String suffix : policySpec.getSuffixesNotPermitted()) {
 +            if (StringUtils.join(generatedPassword).endsWith(suffix)) {
 +                checkEndChar(generatedPassword, policySpec);
 +            }
 +        }
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/f45f2d9b/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
----------------------------------------------------------------------
diff --cc core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
index 9c959b3,0000000..e3e26e0
mode 100644,000000..100644
--- a/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
+++ b/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
@@@ -1,124 -1,0 +1,140 @@@
 +/*
 + * 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.misc.security;
 +
 +import static org.junit.Assert.assertTrue;
++import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.fail;
 +
 +import java.util.ArrayList;
++import java.util.Collections;
 +import java.util.List;
 +import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 +import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
 +import org.apache.syncope.core.misc.policy.PolicyPattern;
 +import org.junit.Test;
 +
 +public class PasswordGeneratorTest {
 +
 +    private final PasswordGenerator passwordGenerator = new PasswordGenerator();
 +
-     @Test
-     public void startEndWithDigit()
-             throws InvalidPasswordPolicySpecException {
++    private PasswordPolicySpec createBasePasswordPolicySpec() {
++        PasswordPolicySpec basePasswordPolicySpec = new PasswordPolicySpec();
++        basePasswordPolicySpec.setAlphanumericRequired(false);
++        basePasswordPolicySpec.setDigitRequired(false);
++        basePasswordPolicySpec.setLowercaseRequired(false);
++        basePasswordPolicySpec.setMaxLength(1000);
++        basePasswordPolicySpec.setMinLength(8);
++        basePasswordPolicySpec.setMustEndWithAlpha(false);
++        basePasswordPolicySpec.setMustEndWithDigit(false);
++        basePasswordPolicySpec.setMustEndWithNonAlpha(false);
++        basePasswordPolicySpec.setMustStartWithAlpha(false);
++        basePasswordPolicySpec.setMustStartWithDigit(false);
++        basePasswordPolicySpec.setMustStartWithNonAlpha(false);
++        basePasswordPolicySpec.setMustntEndWithAlpha(false);
++        basePasswordPolicySpec.setMustntEndWithDigit(false);
++        basePasswordPolicySpec.setMustntEndWithNonAlpha(false);
++        basePasswordPolicySpec.setMustntStartWithAlpha(false);
++        basePasswordPolicySpec.setMustntStartWithDigit(false);
++        basePasswordPolicySpec.setMustntStartWithNonAlpha(false);
++        basePasswordPolicySpec.setNonAlphanumericRequired(false);
++        basePasswordPolicySpec.setUppercaseRequired(false);
++        return basePasswordPolicySpec;
++    }
 +
++    @Test
++    public void startEndWithDigit() throws InvalidPasswordPolicySpecException {
 +        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setMustStartWithDigit(true);
 +
 +        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setMustEndWithDigit(true);
 +        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
 +        passwordPolicySpecs.add(passwordPolicySpec);
 +        passwordPolicySpecs.add(passwordPolicySpec2);
 +        String generatedPassword = passwordGenerator.generate(passwordPolicySpecs);
 +        assertTrue(Character.isDigit(generatedPassword.charAt(0)));
 +        assertTrue(Character.isDigit(generatedPassword.charAt(generatedPassword.length() - 1)));
 +    }
 +
 +    @Test
-     public void startWithDigitAndWithAlpha()
-             throws InvalidPasswordPolicySpecException {
- 
++    public void startWithDigitAndWithAlpha() throws InvalidPasswordPolicySpecException {
 +        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setMustStartWithDigit(true);
 +
 +        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setMustEndWithAlpha(true);
 +        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
 +        passwordPolicySpecs.add(passwordPolicySpec);
 +        passwordPolicySpecs.add(passwordPolicySpec2);
 +        String generatedPassword = passwordGenerator.generate(passwordPolicySpecs);
 +        assertTrue(Character.isDigit(generatedPassword.charAt(0)));
 +        assertTrue(Character.isLetter(generatedPassword.charAt(generatedPassword.length() - 1)));
 +    }
 +
 +    @Test
-     public void passwordWithNonAlpha()
-             throws InvalidPasswordPolicySpecException {
- 
++    public void passwordWithNonAlpha() throws InvalidPasswordPolicySpecException {
 +        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setNonAlphanumericRequired(true);
 +
 +        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setMustEndWithAlpha(true);
 +        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
 +        passwordPolicySpecs.add(passwordPolicySpec);
 +        passwordPolicySpecs.add(passwordPolicySpec2);
 +        String generatedPassword = passwordGenerator.generate(passwordPolicySpecs);
 +        assertTrue(PolicyPattern.NON_ALPHANUMERIC.matcher(generatedPassword).matches());
 +        assertTrue(Character.isLetter(generatedPassword.charAt(generatedPassword.length() - 1)));
 +    }
 +
 +    @Test(expected = InvalidPasswordPolicySpecException.class)
-     public void incopatiblePolicies()
-             throws InvalidPasswordPolicySpecException {
- 
++    public void incopatiblePolicies() throws InvalidPasswordPolicySpecException {
 +        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setMinLength(12);
 +
 +        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
 +        passwordPolicySpec.setMaxLength(10);
 +
 +        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
 +        passwordPolicySpecs.add(passwordPolicySpec);
 +        passwordPolicySpecs.add(passwordPolicySpec2);
 +        passwordGenerator.generate(passwordPolicySpecs);
 +    }
 +
-     private PasswordPolicySpec createBasePasswordPolicySpec() {
-         PasswordPolicySpec basePasswordPolicySpec = new PasswordPolicySpec();
-         basePasswordPolicySpec.setAlphanumericRequired(false);
-         basePasswordPolicySpec.setDigitRequired(false);
-         basePasswordPolicySpec.setLowercaseRequired(false);
-         basePasswordPolicySpec.setMaxLength(1000);
-         basePasswordPolicySpec.setMinLength(8);
-         basePasswordPolicySpec.setMustEndWithAlpha(false);
-         basePasswordPolicySpec.setMustEndWithDigit(false);
-         basePasswordPolicySpec.setMustEndWithNonAlpha(false);
-         basePasswordPolicySpec.setMustStartWithAlpha(false);
-         basePasswordPolicySpec.setMustStartWithDigit(false);
-         basePasswordPolicySpec.setMustStartWithNonAlpha(false);
-         basePasswordPolicySpec.setMustntEndWithAlpha(false);
-         basePasswordPolicySpec.setMustntEndWithDigit(false);
-         basePasswordPolicySpec.setMustntEndWithNonAlpha(false);
-         basePasswordPolicySpec.setMustntStartWithAlpha(false);
-         basePasswordPolicySpec.setMustntStartWithDigit(false);
-         basePasswordPolicySpec.setMustntStartWithNonAlpha(false);
-         basePasswordPolicySpec.setNonAlphanumericRequired(false);
-         basePasswordPolicySpec.setUppercaseRequired(false);
-         return basePasswordPolicySpec;
++    @Test
++    public void issueSYNCOPE678() {
++        String password = null;
++        try {
++            password = passwordGenerator.generate(Collections.<PasswordPolicySpec>emptyList());
++        } catch (InvalidPasswordPolicySpecException e) {
++            fail(e.getMessage());
++        }
++        assertNotNull(password);
++
++        PasswordPolicySpec ppSpec = createBasePasswordPolicySpec();
++        ppSpec.setMinLength(0);
++        password = null;
++        try {
++            password = passwordGenerator.generate(Collections.singletonList(ppSpec));
++        } catch (InvalidPasswordPolicySpecException e) {
++            fail(e.getMessage());
++        }
++        assertNotNull(password);
 +    }
 +}