From ed2f7f04e94f2d9a5892c853df584a81e4cde090 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Tue, 7 Mar 2017 19:12:31 +0100 Subject: [PATCH 1/7] Protector and ProtectedString support for hasing (PBKDF) --- .../midpoint/prism/PrismConstants.java | 4 + .../midpoint/prism/crypto/BaseProtector.java | 2 +- .../midpoint/prism/crypto/ProtectedData.java | 14 +- .../midpoint/prism/crypto/Protector.java | 41 +--- .../{AESProtector.java => ProtectorImpl.java} | 212 ++++++++++++++-- .../prism/marshaller/BeanMarshaller.java | 6 +- .../prism/marshaller/XNodeProcessorUtil.java | 9 + .../ns/_public/types_3/DigestMethodType.java | 146 +++++++++++ .../ns/_public/types_3/HashedDataType.java | 107 +++++++++ .../xml/ns/_public/types_3/ObjectFactory.java | 17 +- .../types_3/ProtectedByteArrayType.java | 5 + .../ns/_public/types_3/ProtectedDataType.java | 165 +++++++------ .../_public/types_3/ProtectedStringType.java | 52 ++-- .../main/resources/xml/ns/public/types-3.xsd | 37 ++- .../midpoint/prism/PrismInternalTestUtil.java | 16 +- .../midpoint/prism/crypto/TestProtector.java | 226 +++++++++++++++--- .../prism/lex/TestProtectedString.java | 33 ++- .../common/expression/ExpressionTestUtil.java | 8 +- .../common/expression/TestExpression.java | 4 +- .../expression/script/AbstractScriptTest.java | 4 +- .../script/TestExpressionFunctions.java | 4 +- .../expression/script/TestScriptCaching.java | 4 +- .../common/mapping/MappingTestEvaluator.java | 6 +- .../init/ConfigurableProtectorFactory.java | 4 +- .../midpoint/tools/ninja/KeyStoreDumper.java | 10 +- 25 files changed, 917 insertions(+), 219 deletions(-) rename infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/{AESProtector.java => ProtectorImpl.java} (70%) create mode 100644 infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/DigestMethodType.java create mode 100644 infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/HashedDataType.java diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java index 628acd24cef..f564669552e 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/PrismConstants.java @@ -48,6 +48,10 @@ public class PrismConstants { public static final String NS_MATCHING_RULE = NS_PREFIX + "matching-rule-3"; public static final String PREFIX_NS_MATCHING = "mr"; + + public static final String NS_PREFIX_CRYPTO = NS_PREFIX + "crypto/"; + public static final String NS_PREFIX_CRYPTO_ALGORITHM = NS_PREFIX_CRYPTO + "algorithm/"; + public static final String NS_CRYPTO_ALGORITHM_PBKD = NS_PREFIX_CRYPTO_ALGORITHM + "pbkd-3"; // Annotations diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java index bea17186aca..cb71b26563d 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/BaseProtector.java @@ -68,5 +68,5 @@ public boolean isEncrypted(ProtectedStringType ps) { Validate.notNull(ps, "Protected string must not be null."); return ps.isEncrypted(); } - + } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java index a3e425a15ba..2e40d9470e9 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectedData.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2014 Evolveum + * Copyright (c) 2014-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package com.evolveum.midpoint.prism.crypto; import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; +import com.evolveum.prism.xml.ns._public.types_3.HashedDataType; /** * @author Radovan Semancik @@ -27,6 +28,10 @@ public interface ProtectedData { abstract void setClearBytes(byte[] bytes); + abstract T getClearValue(); + + abstract void setClearValue(T data); + abstract void destroyCleartext(); EncryptedDataType getEncryptedDataType(); @@ -35,4 +40,11 @@ public interface ProtectedData { boolean isEncrypted(); + HashedDataType getHashedDataType(); + + void setHashedData(HashedDataType hashedDataType); + + boolean isHashed(); + + boolean canSupportType(Class type); } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java index 53510b69758..6317d1b063a 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/Protector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,19 +52,6 @@ public interface Protector { */ String decryptString(ProtectedStringType protectedString) throws EncryptionException; -// /** -// * -// * @param protectedString -// * @return decrypted DOM {@link Element} -// * @throws EncryptionException -// * this is thrown probably in case JRE/JDK doesn't have JCE -// * installed -// * @throws IllegalArgumentException -// * if protectedString argument is null or EncryptedData in -// * protectedString argument is null -// */ -// Element decrypt(ProtectedStringType protectedString) throws EncryptionException; - /** * * @param text @@ -76,29 +63,15 @@ public interface Protector { */ ProtectedStringType encryptString(String text) throws EncryptionException; -// /** -// * -// * @param plain -// * @return {@link ProtectedStringType} with encrypted element inside it. If -// * input argument is null, method returns null. -// * @throws EncryptionException -// * this is thrown probably in case JRE/JDK doesn't have JCE -// * installed -// */ -// ProtectedStringType encrypt(Element plain) throws EncryptionException; -// -// /** -// * Encrypts the ProtectedStringType "in place". -// * @param ps -// * @throws EncryptionException -// */ -// void encrypt(ProtectedStringType ps) throws EncryptionException; -// /** * Returns true if protected string contains encrypted data that seems valid. + * DEPRECATED. Use ProtectedStringType.isEncrypted() instead */ + @Deprecated boolean isEncrypted(ProtectedStringType ps); - - boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException; + + void hash(ProtectedData protectedData) throws EncryptionException, SchemaException; + + boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException, SchemaException; } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/AESProtector.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectorImpl.java similarity index 70% rename from infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/AESProtector.java rename to infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectorImpl.java index 6a4b725aa04..5f06de2728e 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/AESProtector.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/crypto/ProtectorImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,19 +26,26 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.SecureRandom; import java.security.UnrecoverableKeyException; +import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; import java.util.List; +import java.util.Random; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.xml.namespace.QName; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; @@ -47,31 +54,44 @@ import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.utils.Base64; +import com.evolveum.midpoint.prism.PrismConstants; +import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.prism.xml.ns._public.types_3.CipherDataType; +import com.evolveum.prism.xml.ns._public.types_3.DigestMethodType; import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; import com.evolveum.prism.xml.ns._public.types_3.EncryptionMethodType; +import com.evolveum.prism.xml.ns._public.types_3.HashedDataType; import com.evolveum.prism.xml.ns._public.types_3.KeyInfoType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; /** - * Class that manages encrypted string values. Java Cryptography Extension is + * Class that manages encrypted and hashed values. Java Cryptography Extension is * needed because this class is using AES-256 for encrypting/decrypting xml * data. * * @author Radovan Semancik * @author lazyman */ -public class AESProtector extends BaseProtector { +public class ProtectorImpl extends BaseProtector { + + private static final String ALGORITHM_PKKDF2_NAME = "PBKDF2WithHmacSHA512"; + private static final QName ALGORITH_PBKDF2_WITH_HMAC_SHA512_QNAME = new QName(PrismConstants.NS_CRYPTO_ALGORITHM_PBKD, ALGORITHM_PKKDF2_NAME); + private static final String ALGORITH_PBKDF2_WITH_HMAC_SHA512_URI = QNameUtil.qNameToUri(ALGORITH_PBKDF2_WITH_HMAC_SHA512_QNAME); private static final String KEY_DIGEST_TYPE = "SHA1"; private static final String DEFAULT_ENCRYPTION_ALGORITHM = XMLCipher.AES_128; private static final char[] KEY_PASSWORD = "midpoint".toCharArray(); - private static final Trace LOGGER = TraceManager.getTrace(AESProtector.class); + private static final String DEFAULT_DIGEST_ALGORITHM = ALGORITH_PBKDF2_WITH_HMAC_SHA512_URI; +// "http://www.w3.org/2009/xmlenc11#pbkdf2" + + private Random randomNumberGenerator; + + private static final Trace LOGGER = TraceManager.getTrace(ProtectorImpl.class); private String keyStorePath; private String keyStorePassword; @@ -79,6 +99,7 @@ public class AESProtector extends BaseProtector { private String requestedJceProviderName = null; private String encryptionAlgorithm; + private String digestAlgorithm; private List trustManagers; private static final KeyStore keyStore; @@ -92,7 +113,7 @@ public class AESProtector extends BaseProtector { } /** - * @throws SystemException if jceks keystore is not available on {@link AESProtector#getKeyStorePath} + * @throws SystemException if jceks keystore is not available on {@link ProtectorImpl#getKeyStorePath} */ public void init() { InputStream stream = null; @@ -113,11 +134,11 @@ public void init() { LOGGER.warn("Using default keystore from classpath ({}).", getKeyStorePath()); // Read from class path - stream = AESProtector.class.getClassLoader().getResourceAsStream(getKeyStorePath()); + stream = ProtectorImpl.class.getClassLoader().getResourceAsStream(getKeyStorePath()); // ugly dirty hack to have second chance to find keystore on // class path if (stream == null) { - stream = AESProtector.class.getClassLoader().getResourceAsStream( + stream = ProtectorImpl.class.getClassLoader().getResourceAsStream( "com/../../" + getKeyStorePath()); } } @@ -147,6 +168,8 @@ public void init() { new Object[]{getKeyStorePath(), ex.getMessage()}, ex); throw new SystemException(ex.getMessage(), ex); } + + randomNumberGenerator = new SecureRandom(); } public String getRequestedJceProviderName() { @@ -173,6 +196,28 @@ private String getCipherAlgorithm() { } } + private String getDigestAlgorithm() { + if (digestAlgorithm != null) { + return digestAlgorithm; + } else { + return DEFAULT_DIGEST_ALGORITHM; + } + } + + // TODO: make it configurable + + private int getPbkdKeyLength() { + return 256; + } + + private int getPbkdIterations() { + return 10000; + } + + private int getPbkdSaltLength() { + return 32; + } + /** * @return the encryptionKeyAlias * @throws IllegalStateException if encryption key digest is null or empty string @@ -428,24 +473,161 @@ private SecretKey getSecretKeyByDigest(String digest) throws EncryptionException throw new EncryptionException("Key '" + digest + "' is not in keystore."); } + + @Override + public void hash(ProtectedData protectedData) throws EncryptionException, SchemaException { + if (protectedData.isHashed()) { + throw new IllegalArgumentException("Attempt to hash protected data that are already hashed"); + } + String algorithmUri = getDigestAlgorithm(); + QName algorithmQName = QNameUtil.uriToQName(algorithmUri); + String algorithmNamespace = algorithmQName.getNamespaceURI(); + if (algorithmNamespace == null) { + throw new SchemaException("No algorithm namespace"); + } + + HashedDataType hashedDataType; + switch (algorithmNamespace) { + case PrismConstants.NS_CRYPTO_ALGORITHM_PBKD: + if (!protectedData.canSupportType(String.class)) { + throw new SchemaException("Non-string proteted data"); + } + hashedDataType = hashPbkd((ProtectedData) protectedData, algorithmUri, algorithmQName.getLocalPart()); + break; + default: + throw new SchemaException("Unkown namespace "+algorithmNamespace); + } + + protectedData.setHashedData(hashedDataType); + protectedData.destroyCleartext(); + } + + private HashedDataType hashPbkd(ProtectedData protectedData, String algorithmUri, String algorithmName) throws EncryptionException { + + char[] clearChars = protectedData.getClearValue().toCharArray(); + byte[] salt = generatePbkdSalt(); + int iterations = getPbkdIterations(); + + SecretKeyFactory secretKeyFactory; + try { + secretKeyFactory = SecretKeyFactory.getInstance( algorithmName ); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } + PBEKeySpec keySpec = new PBEKeySpec( clearChars, salt, iterations, getPbkdKeyLength() ); + SecretKey key; + try { + key = secretKeyFactory.generateSecret( keySpec ); + } catch (InvalidKeySpecException e) { + throw new EncryptionException(e.getMessage(), e); + } + byte[] hashBytes = key.getEncoded( ); + + HashedDataType hashedDataType = new HashedDataType(); + + DigestMethodType digestMethod = new DigestMethodType(); + digestMethod.setAlgorithm(algorithmUri); + digestMethod.setSalt(salt); + digestMethod.setWorkFactor(iterations); + hashedDataType.setDigestMethod(digestMethod); + + hashedDataType.setDigestValue(hashBytes); + + return hashedDataType; + } + + private byte[] generatePbkdSalt() { + byte[] salt = new byte[getPbkdSaltLength()/8]; + randomNumberGenerator.nextBytes(salt); + return salt; + } @Override - public boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException { + public boolean compare(ProtectedStringType a, ProtectedStringType b) throws EncryptionException, SchemaException { + if (a == b) { + return true; + } if (a == null && b == null) { return true; } if (a == null || b == null) { return false; } - String aClear = decryptString(a); - String bClear = decryptString(b); - if (aClear == null && bClear == null) { - return true; + if (a.isHashed() && b.isHashed()) { + throw new SchemaException("Cannot compare two hased protected strings"); } - if (aClear == null || bClear == null) { - return false; + + if (a.isHashed() || b.isHashed()) { + String clear; + ProtectedStringType hashedPs; + if (a.isHashed()) { + hashedPs = a; + clear = decryptString(b); + } else { + hashedPs = b; + clear = decryptString(a); + } + return compareHashed(hashedPs, clear.toCharArray()); + + } else { + String aClear = decryptString(a); + String bClear = decryptString(b); + if (aClear == null && bClear == null) { + return true; + } + if (aClear == null || bClear == null) { + return false; + } + return aClear.equals(bClear); + } + } + + private boolean compareHashed(ProtectedStringType hashedPs, char[] clearChars) throws SchemaException, EncryptionException { + HashedDataType hashedDataType = hashedPs.getHashedDataType(); + DigestMethodType digestMethodType = hashedDataType.getDigestMethod(); + if (digestMethodType == null) { + throw new SchemaException("No digest type"); + } + String algorithmUri = digestMethodType.getAlgorithm(); + QName algorithmQName = QNameUtil.uriToQName(algorithmUri); + String algorithmNamespace = algorithmQName.getNamespaceURI(); + if (algorithmNamespace == null) { + throw new SchemaException("No algorithm namespace"); + } + + switch (algorithmNamespace) { + case PrismConstants.NS_CRYPTO_ALGORITHM_PBKD: + return compareHashedPbkd(hashedDataType, algorithmQName.getLocalPart(), clearChars); + default: + throw new SchemaException("Unkown namespace "+algorithmNamespace); } - return aClear.equals(bClear); } + + private boolean compareHashedPbkd(HashedDataType hashedDataType, String algorithmName, char[] clearChars) throws EncryptionException { + DigestMethodType digestMethodType = hashedDataType.getDigestMethod(); + byte[] salt = digestMethodType.getSalt(); + Integer workFactor = digestMethodType.getWorkFactor(); + byte[] digestValue = hashedDataType.getDigestValue(); + int keyLen = digestValue.length * 8; + + SecretKeyFactory secretKeyFactory; + try { + secretKeyFactory = SecretKeyFactory.getInstance( algorithmName ); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } + PBEKeySpec keySpec = new PBEKeySpec( clearChars, salt, workFactor, keyLen ); + SecretKey key; + try { + key = secretKeyFactory.generateSecret( keySpec ); + } catch (InvalidKeySpecException e) { + throw new EncryptionException(e.getMessage(), e); + } + byte[] hashBytes = key.getEncoded( ); + + return Arrays.equals(digestValue, hashBytes); + } + + } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java index 1e093f34fc3..7f48c706c83 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/BeanMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -454,6 +454,10 @@ public MapXNode marshalProtectedDataType(Object o, SerializationContext sc) EncryptedDataType encryptedDataType = protectedType.getEncryptedDataType(); MapXNode xEncryptedDataType = (MapXNode) marshall(encryptedDataType); xmap.put(ProtectedDataType.F_ENCRYPTED_DATA, xEncryptedDataType); + } else if (protectedType.getHashedDataType() != null) { + HashedDataType hashedDataType = protectedType.getHashedDataType(); + MapXNode xHashedDataType = (MapXNode) marshall(hashedDataType); + xmap.put(ProtectedDataType.F_HASHED_DATA, xHashedDataType); } else if (protectedType.getClearValue() != null){ QName type = XsdTypeMapper.toXsdType(protectedType.getClearValue().getClass()); PrimitiveXNode xClearValue = createPrimitiveXNode(protectedType.getClearValue(), type); diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java index deafc191db8..085fa747461 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/marshaller/XNodeProcessorUtil.java @@ -31,6 +31,7 @@ import com.evolveum.midpoint.util.DOMUtil; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.prism.xml.ns._public.types_3.EncryptedDataType; +import com.evolveum.prism.xml.ns._public.types_3.HashedDataType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedDataType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; @@ -84,6 +85,14 @@ public static void parseProtectedType(ProtectedDataType protectedType, Ma } } } + RootXNode xHashedData = xmap.getEntryAsRoot(ProtectedDataType.F_HASHED_DATA); + if (xHashedData != null) { + if (!(xHashedData.getSubnode() instanceof MapXNode)) { + throw new SchemaException("Cannot parse hashedData from "+xHashedData); + } + HashedDataType hashedDataType = prismContext.parserFor(xHashedData).context(pc).parseRealValue(HashedDataType.class); + protectedType.setHashedData(hashedDataType); + } // protected data empty..check for clear value if (protectedType.isEmpty()){ XNode xClearValue = xmap.get(ProtectedDataType.F_CLEAR_VALUE); diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/DigestMethodType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/DigestMethodType.java new file mode 100644 index 00000000000..0db44b7aabb --- /dev/null +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/DigestMethodType.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 Evolveum + * + * Licensed 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 com.evolveum.prism.xml.ns._public.types_3; + +import java.io.Serializable; +import java.util.Arrays; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlSchemaType; +import javax.xml.bind.annotation.XmlType; + + +/** + * JAXB representation of DigestMethodType. + * Manually created (not generated) + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "DigestMethodType", propOrder = { + "algorithm", + "salt", + "workFactor" +}) +public class DigestMethodType implements Serializable, Cloneable { + + @XmlElement(required = true) + @XmlSchemaType(name = "anyURI") + protected String algorithm; + + @XmlElement(required = false) + protected byte[] salt; + + @XmlElement(required = false) + protected Integer workFactor; + + /** + * Gets the value of the algorithm property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getAlgorithm() { + return algorithm; + } + + /** + * Sets the value of the algorithm property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setAlgorithm(String value) { + this.algorithm = value; + } + + public byte[] getSalt() { + return salt; + } + + public void setSalt(byte[] salt) { + this.salt = salt; + } + + public Integer getWorkFactor() { + return workFactor; + } + + public void setWorkFactor(Integer workFactor) { + this.workFactor = workFactor; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((algorithm == null) ? 0 : algorithm.hashCode()); + result = prime * result + Arrays.hashCode(salt); + result = prime * result + ((workFactor == null) ? 0 : workFactor.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + DigestMethodType other = (DigestMethodType) obj; + if (algorithm == null) { + if (other.algorithm != null) { + return false; + } + } else if (!algorithm.equals(other.algorithm)) { + return false; + } + if (!Arrays.equals(salt, other.salt)) { + return false; + } + if (workFactor == null) { + if (other.workFactor != null) { + return false; + } + } else if (!workFactor.equals(other.workFactor)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "DigestMethodType(algorithm=" + algorithm + ", salt=" + (salt==null?"null":"["+salt.length+" bytes]") + ", workFactor=" + workFactor + ")"; + } + + public DigestMethodType clone() { + DigestMethodType cloned = new DigestMethodType(); + cloned.setAlgorithm(getAlgorithm()); + cloned.setSalt(salt.clone()); + cloned.setWorkFactor(getWorkFactor()); + return cloned; + } + +} diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/HashedDataType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/HashedDataType.java new file mode 100644 index 00000000000..9eac9a8c971 --- /dev/null +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/HashedDataType.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Evolveum + * + * Licensed 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 com.evolveum.prism.xml.ns._public.types_3; + +import java.io.Serializable; +import java.util.Arrays; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + + +/** + * JAXB representation of HashedDataType. + * Manually created (not generated) + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "HashedDataType", propOrder = { + "digestMethod", + "digestValue" +}) +public class HashedDataType implements Serializable, Cloneable { + + @XmlElement(required = true) + protected DigestMethodType digestMethod; + + @XmlElement(required = true) + protected byte[] digestValue; + + public DigestMethodType getDigestMethod() { + return digestMethod; + } + + public void setDigestMethod(DigestMethodType digestMethod) { + this.digestMethod = digestMethod; + } + + public byte[] getDigestValue() { + return digestValue; + } + + public void setDigestValue(byte[] digestValue) { + this.digestValue = digestValue; + } + + @Override + public String toString() { + return "HashedDataType(digestMethod=" + digestMethod + ", digestValue=" + (digestValue==null?"null":"["+digestValue.length+" bytes])"); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((digestMethod == null) ? 0 : digestMethod.hashCode()); + result = prime * result + Arrays.hashCode(digestValue); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + HashedDataType other = (HashedDataType) obj; + if (digestMethod == null) { + if (other.digestMethod != null) { + return false; + } + } else if (!digestMethod.equals(other.digestMethod)) { + return false; + } + if (!Arrays.equals(digestValue, other.digestValue)) { + return false; + } + return true; + } + + @Override + public HashedDataType clone() { + HashedDataType cloned = new HashedDataType(); + cloned.setDigestMethod(getDigestMethod().clone()); + cloned.setDigestValue(digestValue.clone()); + return cloned; + } +} diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java index b534ea9982e..dff1b211c30 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ObjectFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,6 +55,7 @@ public class ObjectFactory implements Serializable { private final static QName _PolyStringTypeNorm_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "norm"); private final static QName _PolyStringTypeOrig_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "orig"); private final static QName _ProtectedDataTypeEncryptedData_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "encryptedData"); + private final static QName _ProtectedDataTypeHashedData_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "hashedData"); private final static QName _ItemPathType_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "path"); private final static QName _Object_QNAME = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "object"); @@ -241,6 +242,20 @@ public JAXBElement createProtectedStringTypeEncryptedData(Enc return new JAXBElement(_ProtectedDataTypeEncryptedData_QNAME, EncryptedDataType.class, ProtectedStringType.class, value); } + /** + * Create an instance of {@link JAXBElement }{@code <}{@link HashedDataType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://prism.evolveum.com/xml/ns/public/types-3", name = "hashedData", scope = ProtectedDataType.class) + public JAXBElement createProtectedDataTypeHashedData(HashedDataType value) { + return new JAXBElement(_ProtectedDataTypeHashedData_QNAME, HashedDataType.class, ProtectedDataType.class, value); + } + + @XmlElementDecl(namespace = "http://prism.evolveum.com/xml/ns/public/types-3", name = "hashedData", scope = ProtectedStringType.class) + public JAXBElement createProtectedStringTypeHashedData(HashedDataType value) { + return new JAXBElement(_ProtectedDataTypeHashedData_QNAME, HashedDataType.class, ProtectedStringType.class, value); + } + @XmlElementDecl(namespace = "http://prism.evolveum.com/xml/ns/public/types-3", name = "path", scope = ItemPathType.class) public JAXBElement createItemPathType(ItemPath value) { return new JAXBElement(_ItemPathType_QNAME, ItemPath.class, ItemPathType.class, value); diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java index 1c937d9c61f..b7bc02e0060 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedByteArrayType.java @@ -52,5 +52,10 @@ public void setClearBytes(byte[] bytes) { setClearValue(ArrayUtils.toObject(bytes)); } + @Override + public boolean canSupportType(Class type) { + return byte[].class.isAssignableFrom(type); + } + } diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java index 0f526327bf4..b1d62f231f8 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedDataType.java @@ -1,11 +1,18 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2014.02.04 at 01:34:24 PM CET -// - - +/** + * Copyright (c) 2010-2017 Evolveum + * + * Licensed 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 com.evolveum.prism.xml.ns._public.types_3; import java.io.Serializable; @@ -37,33 +44,7 @@ /** - * - * TODO - * May be either encrypted or hashed or provided in the clear (e.g. for debugging). - * - * This type is marked as "mixed" because it may have alternative representation where - * just the plaintext value is presented as the only value. - * - * This is considered to be primitive built-in type for prism objects. - * - * - *

Java class for ProtectedDataType complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="ProtectedDataType">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="encryptedData" type="{http://prism.evolveum.com/xml/ns/public/types-3}EncryptedDataType" minOccurs="0"/>
- *         <any namespace='##other'/>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * + * This class was originally generated. But it was heavily modified by hand. * */ @XmlAccessorType(XmlAccessType.FIELD) @@ -78,6 +59,7 @@ public abstract class ProtectedDataType implements ProtectedData, Serializ public static final QName COMPLEX_TYPE = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "ProtectedDataType"); public final static QName F_ENCRYPTED_DATA = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "encryptedData"); + public final static QName F_HASHED_DATA = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "hashedData"); public final static QName F_CLEAR_VALUE = new QName("http://prism.evolveum.com/xml/ns/public/types-3", "clearValue"); public static final String NS_XML_ENC = "http://www.w3.org/2001/04/xmlenc#"; @@ -95,6 +77,9 @@ public abstract class ProtectedDataType implements ProtectedData, Serializ @XmlTransient private EncryptedDataType encryptedDataType; + @XmlTransient + private HashedDataType hashedDataType; + @XmlTransient private T clearValue; @@ -157,10 +142,27 @@ public boolean isEncrypted() { return encryptedDataType != null; } + @Override + public HashedDataType getHashedDataType() { + return hashedDataType; + } + + @Override + public void setHashedData(HashedDataType hashedDataType) { + this.hashedDataType = hashedDataType; + } + + @Override + public boolean isHashed() { + return hashedDataType != null; + } + + @Override public T getClearValue() { return clearValue; } + @Override public void setClearValue(T clearValue) { this.clearValue = clearValue; } @@ -175,8 +177,13 @@ private JAXBElement toJaxbElement(EncryptedDataType encrypted return new JAXBElement(F_ENCRYPTED_DATA, EncryptedDataType.class, encryptedDataType); } + private JAXBElement toJaxbElement(HashedDataType hashedDataType) { + return new JAXBElement(F_ENCRYPTED_DATA, HashedDataType.class, hashedDataType); + } + private void clearContent() { encryptedDataType = null; + hashedDataType = null; } private boolean addContent(Object newObject) { @@ -192,6 +199,9 @@ private boolean addContent(Object newObject) { if (QNameUtil.match(F_ENCRYPTED_DATA, jaxbElement.getName())) { encryptedDataType = (EncryptedDataType) jaxbElement.getValue(); return true; + } else if (QNameUtil.match(F_HASHED_DATA, jaxbElement.getName())) { + hashedDataType = (HashedDataType) jaxbElement.getValue(); + return true; } else { throw new IllegalArgumentException("Attempt to add unknown JAXB element "+jaxbElement); } @@ -250,37 +260,52 @@ private EncryptedDataType convertXmlEncToEncryptedDate(Element eEncryptedData) { } public boolean isEmpty() { - return encryptedDataType == null && clearValue == null; + return encryptedDataType == null && hashedDataType == null && clearValue == null; } - + @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((clearValue == null) ? 0 : clearValue.hashCode()); result = prime * result + ((encryptedDataType == null) ? 0 : encryptedDataType.hashCode()); + result = prime * result + ((hashedDataType == null) ? 0 : hashedDataType.hashCode()); return result; } @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } ProtectedDataType other = (ProtectedDataType) obj; if (clearValue == null) { - if (other.clearValue != null) + if (other.clearValue != null) { return false; - } else if (!clearValue.equals(other.clearValue)) + } + } else if (!clearValue.equals(other.clearValue)) { return false; + } if (encryptedDataType == null) { - if (other.encryptedDataType != null) + if (other.encryptedDataType != null) { return false; - } else if (!encryptedDataType.equals(other.encryptedDataType)) + } + } else if (!encryptedDataType.equals(other.encryptedDataType)) { return false; + } + if (hashedDataType == null) { + if (other.hashedDataType != null) { + return false; + } + } else if (!hashedDataType.equals(other.hashedDataType)) { + return false; + } return true; } @@ -292,6 +317,10 @@ public String toString() { sb.append("encrypted="); sb.append(encryptedDataType); } + if (hashedDataType != null) { + sb.append("hashed="); + sb.append(hashedDataType); + } if (clearValue != null) { sb.append("clearValue="); sb.append(clearValue); @@ -303,36 +332,16 @@ public String toString() { protected void cloneTo(ProtectedDataType cloned) { cloned.clearValue = CloneUtil.clone(clearValue); cloned.encryptedDataType = CloneUtil.clone(encryptedDataType); - + cloned.hashedDataType = CloneUtil.clone(hashedDataType); + // content is virtual, there is no point in copying it - -// for (Object o : getContent()) { -// if (o instanceof JAXBElement) { -// JAXBElement je = (JAXBElement) o; -// Object v = je.getValue(); -// if (v == null) { -// // do nothing -// } else if (v instanceof EncryptedDataType) { -// EncryptedDataType edt = (EncryptedDataType) v; -// cloned.addContent(new JAXBElement(je.getName(), (Class) je.getDeclaredType(), edt.clone())); -// } else { -// throw new IllegalStateException("Unknown JAXB element "+je.getName()+" ("+je.getValue().getClass()+") in ProtectedDataType"); -// } -// } else if (o instanceof Element) { -// cloned.addContent(((Element) o).cloneNode(true)); -// } else if (o instanceof String) { -// cloned.addContent(o); // will this work? -// } else { -// throw new IllegalStateException("Unknown object of type "+o.getClass()+ " in ProtectedDataType"); -// } -// } } class ContentList implements List, Serializable { @Override public int size() { - if (encryptedDataType != null) { + if (encryptedDataType != null || hashedDataType != null) { return 1; } else { return 0; @@ -341,7 +350,7 @@ public int size() { @Override public boolean isEmpty() { - return encryptedDataType == null; + return encryptedDataType == null && hashedDataType == null; } @Override @@ -363,7 +372,11 @@ public boolean hasNext() { public Object next() { if (index == 0) { index++; - return toJaxbElement(encryptedDataType); + if (encryptedDataType == null) { + return toJaxbElement(hashedDataType); + } else { + return toJaxbElement(encryptedDataType); + } } else { return null; } @@ -378,11 +391,15 @@ public void remove() { @Override public Object[] toArray() { - if (encryptedDataType == null) { + if (encryptedDataType == null && hashedDataType == null) { return new Object[0]; } else { Object[] a = new Object[1]; - a[0] = toJaxbElement(encryptedDataType); + if (encryptedDataType == null) { + a[0] = toJaxbElement(hashedDataType); + } else { + a[0] = toJaxbElement(encryptedDataType); + } return a; } } @@ -448,7 +465,11 @@ public void clear() { public Object get(int index) { if (index == 0) { // what if encryptedDataType is null and clearValue is set? [pm] - return toJaxbElement(encryptedDataType); + if (encryptedDataType == null) { + return toJaxbElement(hashedDataType); + } else { + return toJaxbElement(encryptedDataType); + } } else { return null; } diff --git a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java index 332425e0e50..6ea2223c581 100644 --- a/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java +++ b/infra/prism/src/main/java/com/evolveum/prism/xml/ns/_public/types_3/ProtectedStringType.java @@ -1,11 +1,18 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2014.02.04 at 01:34:24 PM CET -// - - +/** + * Copyright (c) 2010-2017 Evolveum + * + * Licensed 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 com.evolveum.prism.xml.ns._public.types_3; import java.io.UnsupportedEncodingException; @@ -19,27 +26,7 @@ /** - * - * Specific subtype for protected STRING data. - * - * - *

Java class for ProtectedStringType complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="ProtectedStringType">
- *   <complexContent>
- *     <extension base="{http://prism.evolveum.com/xml/ns/public/types-3}ProtectedDataType">
- *       <sequence>
- *         <element name="clearValue" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
- *       </sequence>
- *     </extension>
- *   </complexContent>
- * </complexType>
- * 
- * - * + * This class was originally generated. But it was heavily modified by hand. */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "ProtectedStringType") @@ -71,6 +58,12 @@ public void setClearBytes(byte[] bytes) { } } + @Override + public boolean canSupportType(Class type) { + return String.class.isAssignableFrom(type); + } + + @Override public boolean equals(Object obj) { return super.equals(obj); @@ -96,4 +89,5 @@ public static String bytesToString(byte[] clearBytes) { throw new SystemException("Unsupported charset '"+CHARSET+"', is this system from 19th century?", e); } } + } diff --git a/infra/prism/src/main/resources/xml/ns/public/types-3.xsd b/infra/prism/src/main/resources/xml/ns/public/types-3.xsd index a2bcc23346f..a9e58143ccf 100644 --- a/infra/prism/src/main/resources/xml/ns/public/types-3.xsd +++ b/infra/prism/src/main/resources/xml/ns/public/types-3.xsd @@ -174,7 +174,7 @@ - + @@ -184,6 +184,7 @@ + @@ -250,6 +251,40 @@ + + + + TODO + Contains data protected by (non-reversible) hashing (message digest). + + Loosely based on XML digital signature standard. But we cannot use full + standard as we are not bound to XML. We need this to work also for + JSON and YAML and other languages. + + + + + + + + + + + + TODO + + Loosely based on XML encryption standard. But we cannot use full + standard as we are not bound to XML. We need this to work also for + JSON and YAML and other languages. + + + + + + + + + diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java index 5621b2f3309..d206fb9a4da 100644 --- a/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java +++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/PrismInternalTestUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; +import com.evolveum.midpoint.prism.crypto.Protector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.foo.AccountConstructionType; import com.evolveum.midpoint.prism.schema.SchemaRegistryImpl; @@ -211,6 +213,9 @@ public class PrismInternalTestUtil implements PrismContextFactory { public static final QName WEAPONS_WEAPON_BRAND_TYPE_QNAME = new QName(NS_WEAPONS, "WeaponBrandType"); + public static final String KEYSTORE_PATH = "src/test/resources/keystore.jceks"; + public static final String KEYSTORE_PASSWORD = "changeit"; + public static PrismContextImpl constructInitializedPrismContext() throws SchemaException, SAXException, IOException { PrismContextImpl context = constructPrismContext(); context.initialize(); @@ -483,4 +488,13 @@ public static void assertContainerDefinition(PrismContainer container, String co public static PrismSchema getFooSchema(PrismContext prismContext) { return prismContext.getSchemaRegistry().findSchemaByNamespace(NS_FOO); } + + public static Protector createProtector(String xmlCipher){ + ProtectorImpl protector = new ProtectorImpl(); + protector.setKeyStorePassword(KEYSTORE_PASSWORD); + protector.setKeyStorePath(KEYSTORE_PATH); + protector.setEncryptionAlgorithm(xmlCipher); + protector.init(); + return protector; + } } diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java index 760f18b264c..85af21be71c 100644 --- a/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java +++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/crypto/TestProtector.java @@ -1,54 +1,206 @@ +/** + * Copyright (c) 2010-2017 Evolveum + * + * Licensed 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 com.evolveum.midpoint.prism.crypto; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertNull; +import static org.testng.AssertJUnit.assertTrue; +import static org.testng.AssertJUnit.assertFalse; import org.apache.xml.security.encryption.XMLCipher; import org.testng.AssertJUnit; import org.testng.annotations.Test; import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismInternalTestUtil; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; public class TestProtector { - public static final String KEYSTORE_PATH = "src/test/resources/keystore.jceks"; - public static final String KEYSTORE_PASSWORD = "changeit"; - - private PrismContext prismContext; - private static transient Trace LOGGER = TraceManager.getTrace(TestProtector.class); - public static Protector createProtector(String xmlCipher){ - AESProtector protector = new AESProtector(); - protector.setKeyStorePassword(KEYSTORE_PASSWORD); - protector.setKeyStorePath(KEYSTORE_PATH); - protector.setEncryptionAlgorithm(xmlCipher); - protector.init(); - return protector; + @Test + public void testProtectorEncryptionRoundTrip() throws Exception { + String value = "someValue"; + + Protector protector256 = PrismInternalTestUtil.createProtector(XMLCipher.AES_256); + Protector protector128 = PrismInternalTestUtil.createProtector(XMLCipher.AES_128); + + ProtectedStringType pdt = new ProtectedStringType(); + pdt.setClearValue(value); + assertFalse(pdt.isEmpty()); + assertFalse(pdt.isHashed()); + assertFalse(pdt.isEncrypted()); + + // WHEN + protector256.encrypt(pdt); + + // THEN + assertFalse(pdt.isEmpty()); + assertTrue(pdt.isEncrypted()); + assertFalse(pdt.isHashed()); + assertNull(pdt.getClearValue()); + + // WHEN + protector128.decrypt(pdt); + + // THEN + assertFalse(pdt.isEmpty()); + assertFalse(pdt.isEncrypted()); + assertFalse(pdt.isHashed()); + AssertJUnit.assertEquals(value, pdt.getClearValue()); + + // WHEN + ProtectedStringType pstEnc = protector256.encryptString(value); + + // THEN + assertFalse(pstEnc.isEmpty()); + assertTrue(pstEnc.isEncrypted()); + assertFalse(pstEnc.isHashed()); + + // WHEN + String clear = protector256.decryptString(pstEnc); + assertNotNull(clear); + + // THEN + AssertJUnit.assertEquals(value, clear); + + // WHEN + boolean compare1 = protector256.compare(pdt, pstEnc); + + // THEN + assertTrue("compare1 failed", compare1); + + // WHEN + boolean compare2 = protector256.compare(pstEnc, pdt); + + // THEN + assertTrue("compare2 failed", compare2); + + ProtectedStringType wrongPst = new ProtectedStringType(); + wrongPst.setClearValue("nonono This is not it"); + + // WHEN + boolean compare5 = protector256.compare(pdt, wrongPst); + + // THEN + assertFalse("compare5 unexpected success", compare5); + + // WHEN + boolean compare6 = protector256.compare(wrongPst, pdt); + + // THEN + assertFalse("compare6 unexpected success", compare6); + } + + @Test + public void testProtectorHashRoundTrip() throws Exception { + String value = "someValue"; + ProtectedStringType pst = new ProtectedStringType(); + pst.setClearValue(value); + assertFalse(pst.isEmpty()); + + Protector protector256 = PrismInternalTestUtil.createProtector(XMLCipher.AES_256); + + // WHEN + protector256.hash(pst); + + // THEN + assertFalse(pst.isEmpty()); + assertTrue(pst.isHashed()); + assertFalse(pst.isEncrypted()); + assertNull(pst.getClearValue()); + + ProtectedStringType checkPstClear = new ProtectedStringType(); + checkPstClear.setClearValue(value); + + // WHEN + boolean compare1 = protector256.compare(pst, checkPstClear); + + // THEN + assertTrue("compare1 failed", compare1); + + // WHEN + boolean compare2 = protector256.compare(checkPstClear, pst); + + // THEN + assertTrue("compare2 failed", compare2); + + ProtectedStringType checkPstEnc = new ProtectedStringType(); + checkPstEnc.setClearValue(value); + protector256.encrypt(checkPstEnc); + + // WHEN + boolean compare3 = protector256.compare(pst, checkPstEnc); + + // THEN + assertTrue("compare3 failed", compare3); + + // WHEN + boolean compare4 = protector256.compare(checkPstEnc, pst); + + // THEN + assertTrue("compare4 failed", compare4); + + ProtectedStringType wrongPst = new ProtectedStringType(); + wrongPst.setClearValue("nonono This is not it"); + + // WHEN + boolean compare5 = protector256.compare(pst, wrongPst); + + // THEN + assertFalse("compare5 unexpected success", compare5); + + // WHEN + boolean compare6 = protector256.compare(wrongPst, pst); + + // THEN + assertFalse("compare6 unexpected success", compare6); + + ProtectedStringType wrongPstEnc = new ProtectedStringType(); + wrongPstEnc.setClearValue("nonono This is not it"); + protector256.encrypt(wrongPstEnc); + + // WHEN + boolean compare7 = protector256.compare(pst, wrongPstEnc); + + // THEN + assertFalse("compare7 unexpected success", compare7); + + // WHEN + boolean compare8 = protector256.compare(wrongPstEnc, pst); + + // THEN + assertFalse("compare8 unexpected success", compare8); + + // change the hash ... comparison should fail + pst.getHashedDataType().getDigestValue()[1] = 0x12; + + // WHEN + boolean compare9 = protector256.compare(pst, checkPstClear); + + // THEN + assertFalse("compare9 unexpected success", compare9); + + // WHEN + boolean compare10 = protector256.compare(checkPstClear, pst); + + // THEN + assertFalse("compare10 unexpected success", compare10); + } - - - @Test - public void testProtectorKeyStore() throws Exception{ - - - String value = "someValue"; - - Protector protector256 = createProtector(XMLCipher.AES_256); - - ProtectedStringType pdt = new ProtectedStringType(); - pdt.setClearValue(value); - protector256.encrypt(pdt); - - Protector protector128 = createProtector(XMLCipher.AES_128); - protector128.decrypt(pdt); - - AssertJUnit.assertEquals(value, pdt.getClearValue()); - - ProtectedStringType pst = protector256.encryptString(value); - String clear = protector256.decryptString(pst); - - AssertJUnit.assertEquals(value, clear); - - } } diff --git a/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java b/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java index 02338435e9d..5feec07e764 100644 --- a/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java +++ b/infra/prism/src/test/java/com/evolveum/midpoint/prism/lex/TestProtectedString.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,12 +52,12 @@ public void setupDebug() throws SchemaException, SAXException, IOException { } @Test - public void testParseProtectedString() throws Exception { - final String TEST_NAME = "testParseProtectedString"; + public void testParseProtectedStringEncrypted() throws Exception { + final String TEST_NAME = "testParseProtectedStringEncrypted"; displayTestTitle(TEST_NAME); // GIVEN - Protector protector = TestProtector.createProtector(XMLCipher.AES_128); // TODO move to a util class + Protector protector = PrismInternalTestUtil.createProtector(XMLCipher.AES_128); ProtectedStringType protectedStringType = protector.encryptString("salalala"); PrismContext prismContext = PrismTestUtil.getPrismContext(); @@ -67,6 +67,31 @@ public void testParseProtectedString() throws Exception { MapXNode protectedStringTypeXNode = ((PrismContextImpl) prismContext).getBeanMarshaller().marshalProtectedDataType(protectedStringType, null); System.out.println("Protected string type XNode: " + protectedStringTypeXNode.debugDump()); + // THEN + ProtectedStringType unmarshalled = new ProtectedStringType(); + XNodeProcessorUtil.parseProtectedType(unmarshalled, protectedStringTypeXNode, prismContext, ParsingContext.createDefault()); + System.out.println("Unmarshalled value: " + unmarshalled); + assertEquals("Unmarshalled value differs from the original", protectedStringType, unmarshalled); + } + + @Test + public void testParseProtectedStringHashed() throws Exception { + final String TEST_NAME = "testParseProtectedStringHashed"; + displayTestTitle(TEST_NAME); + + // GIVEN + ProtectedStringType protectedStringType = new ProtectedStringType(); + protectedStringType.setClearValue("blabla"); + Protector protector = PrismInternalTestUtil.createProtector(XMLCipher.AES_128); + protector.hash(protectedStringType); + + PrismContext prismContext = PrismTestUtil.getPrismContext(); + + // WHEN + + MapXNode protectedStringTypeXNode = ((PrismContextImpl) prismContext).getBeanMarshaller().marshalProtectedDataType(protectedStringType, null); + System.out.println("Protected string type XNode: " + protectedStringTypeXNode.debugDump()); + // THEN ProtectedStringType unmarshalled = new ProtectedStringType(); XNodeProcessorUtil.parseProtectedType(unmarshalled, protectedStringTypeXNode, prismContext, ParsingContext.createDefault()); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java index af32213f130..532d3faa553 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/ExpressionTestUtil.java @@ -31,7 +31,7 @@ import com.evolveum.midpoint.model.common.expression.script.xpath.XPathScriptEvaluator; import com.evolveum.midpoint.model.common.stringpolicy.ValuePolicyGenerator; import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.schema.util.ObjectResolver; import com.evolveum.midpoint.security.api.SecurityEnforcer; import com.evolveum.midpoint.test.util.MidPointTestConstants; @@ -42,8 +42,8 @@ */ public class ExpressionTestUtil { - public static AESProtector createInitializedProtector(PrismContext prismContext) { - AESProtector protector = new AESProtector(); + public static ProtectorImpl createInitializedProtector(PrismContext prismContext) { + ProtectorImpl protector = new ProtectorImpl(); protector.setKeyStorePath(MidPointTestConstants.KEYSTORE_PATH); protector.setKeyStorePassword(MidPointTestConstants.KEYSTORE_PASSWORD); //protector.setPrismContext(prismContext); @@ -51,7 +51,7 @@ public static AESProtector createInitializedProtector(PrismContext prismContext) return protector; } - public static ExpressionFactory createInitializedExpressionFactory(ObjectResolver resolver, AESProtector protector, + public static ExpressionFactory createInitializedExpressionFactory(ObjectResolver resolver, ProtectorImpl protector, PrismContext prismContext, SecurityEnforcer securityEnforcer) { ExpressionFactory expressionFactory = new ExpressionFactory(resolver, prismContext); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java index 44aa3968dbc..37558b35b8a 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/TestExpression.java @@ -26,7 +26,7 @@ import org.testng.annotations.Test; import org.xml.sax.SAXException; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.MidPointPrismContextFactory; @@ -70,7 +70,7 @@ public void setup() throws SchemaException, SAXException, IOException { prismContext = PrismTestUtil.createInitializedPrismContext(); ObjectResolver resolver = new DirectoryFileObjectResolver(MidPointTestConstants.OBJECTS_DIR); - AESProtector protector = ExpressionTestUtil.createInitializedProtector(prismContext); + ProtectorImpl protector = ExpressionTestUtil.createInitializedProtector(prismContext); expressionFactory = ExpressionTestUtil.createInitializedExpressionFactory(resolver, protector, prismContext, null); } diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java index 13cc13c29fa..2680c88ecf0 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/AbstractScriptTest.java @@ -25,7 +25,7 @@ import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.MidPointPrismContextFactory; @@ -90,7 +90,7 @@ public void setup() throws SchemaException, SAXException, IOException { public void setupFactory() { PrismContext prismContext = PrismTestUtil.getPrismContext(); ObjectResolver resolver = new DirectoryFileObjectResolver(OBJECTS_DIR); - Protector protector = new AESProtector(); + Protector protector = new ProtectorImpl(); Collection functions = new ArrayList(); functions.add(ExpressionUtil.createBasicFunctionLibrary(prismContext, protector)); scriptExpressionfactory = new ScriptExpressionFactory(resolver, prismContext, protector); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java index 46bf8234d4d..6a34ecee024 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestExpressionFunctions.java @@ -18,7 +18,7 @@ import com.evolveum.midpoint.model.common.expression.functions.BasicExpressionFunctions; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.util.PrismTestUtil; @@ -381,7 +381,7 @@ public void testParseDateTime() throws Exception { private BasicExpressionFunctions createBasicFunctions() throws SchemaException, SAXException, IOException { PrismContext prismContext = PrismTestUtil.createInitializedPrismContext(); - Protector protector = new AESProtector(); + Protector protector = new ProtectorImpl(); return new BasicExpressionFunctions(prismContext, protector); } diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java index 48f9ca9ecdb..2461d3c9dd3 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/expression/script/TestScriptCaching.java @@ -20,7 +20,7 @@ import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary; import com.evolveum.midpoint.model.common.expression.script.jsr223.Jsr223ScriptEvaluator; import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.util.PrismTestUtil; import com.evolveum.midpoint.schema.MidPointPrismContextFactory; @@ -79,7 +79,7 @@ public void setupFactory() { System.out.println("Setting up expression factory and evaluator"); PrismContext prismContext = PrismTestUtil.getPrismContext(); ObjectResolver resolver = new DirectoryFileObjectResolver(OBJECTS_DIR); - Protector protector = new AESProtector(); + Protector protector = new ProtectorImpl(); Collection functions = new ArrayList(); functions.add(ExpressionUtil.createBasicFunctionLibrary(prismContext, protector)); scriptExpressionfactory = new ScriptExpressionFactory(resolver, prismContext, protector); diff --git a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java index 0ce3b81d0d2..0fe06eb1bab 100644 --- a/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java +++ b/model/model-common/src/test/java/com/evolveum/midpoint/model/common/mapping/MappingTestEvaluator.java @@ -40,7 +40,7 @@ import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; @@ -84,7 +84,7 @@ public class MappingTestEvaluator { private PrismContext prismContext; private MappingFactory mappingFactory; - AESProtector protector; + ProtectorImpl protector; public PrismContext getPrismContext() { return prismContext; @@ -108,7 +108,7 @@ public void init() throws SchemaException, SAXException, IOException { mappingFactory.setProtector(protector); } - public AESProtector getProtector() { + public ProtectorImpl getProtector() { return protector; } diff --git a/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java b/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java index cc715a89007..318911912a9 100644 --- a/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java +++ b/repo/system-init/src/main/java/com/evolveum/midpoint/init/ConfigurableProtectorFactory.java @@ -17,7 +17,7 @@ package com.evolveum.midpoint.init; import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; @@ -82,7 +82,7 @@ public void init() { } public Protector getProtector() { - AESProtector protector = new AESProtector(); + ProtectorImpl protector = new ProtectorImpl(); protector.setEncryptionKeyAlias(protectorConfig.getEncryptionKeyAlias()); protector.setKeyStorePassword(protectorConfig.getKeyStorePassword()); protector.setKeyStorePath(protectorConfig.getKeyStorePath()); diff --git a/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java b/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java index df4ebe9d191..79da8d35d54 100644 --- a/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java +++ b/tools/repo-ninja/src/main/java/com/evolveum/midpoint/tools/ninja/KeyStoreDumper.java @@ -15,7 +15,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; -import com.evolveum.midpoint.prism.crypto.AESProtector; +import com.evolveum.midpoint.prism.crypto.ProtectorImpl; import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.crypto.Protector; @@ -32,8 +32,8 @@ public void execute(){ System.out.println("###################################################"); System.out.println("Printing keys from key store"); - if (protector instanceof AESProtector){ - AESProtector aesProtector = (AESProtector) protector; + if (protector instanceof ProtectorImpl){ + ProtectorImpl aesProtector = (ProtectorImpl) protector; System.out.println("Using key store from location: " + aesProtector.getKeyStorePath()); // System.out.println("Cipher: " + aesProtector.getXmlCipher()); @@ -65,8 +65,8 @@ public void execute(){ System.out.println(" Algorithm: " + key.getAlgorithm()); System.out.println(" Format: " + key.getFormat()); System.out.println(" Key length: " + key.getEncoded().length * 8); - if (protector instanceof AESProtector) { - System.out.println(" Key name: " + ((AESProtector) protector).getSecretKeyDigest(key)); + if (protector instanceof ProtectorImpl) { + System.out.println(" Key name: " + ((ProtectorImpl) protector).getSecretKeyDigest(key)); } // Cipher cipher = Cipher.getInstance(key.getAlgorithm()); // System.out.println(" Cipher algorithm" + cipher.getAlgorithm()); From 89b5fbfb1a30fc4d754f88f40d416218aaeacc04 Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Wed, 8 Mar 2017 18:55:32 +0100 Subject: [PATCH 2/7] Password hashing: refactoring password policy processor, switch everything to security policy. Preparing the tests. --- .../MidPointGuiAuthorizationEvaluator.java | 6 +- .../midpoint/prism/delta/ObjectDelta.java | 10 +- .../midpoint/prism/delta/ReferenceDelta.java | 10 + .../xml/ns/public/common/common-core-3.xsd | 138 ++++++++- .../model/impl/lens/AssignmentEvaluator.java | 4 +- .../model/impl/lens/ChangeExecutor.java | 150 +++++---- .../midpoint/model/impl/lens/LensContext.java | 16 - .../model/impl/lens/LensFocusContext.java | 12 +- .../impl/lens/LensProjectionContext.java | 12 - .../midpoint/model/impl/lens/LensUtil.java | 154 ---------- .../model/impl/lens/ObjectDeltaWaves.java | 6 +- .../impl/lens/projector/ContextLoader.java | 17 +- .../lens/projector/CredentialsProcessor.java | 118 ++++++- .../impl/lens/projector/InboundProcessor.java | 13 +- .../impl/lens/projector/MappingEvaluator.java | 182 ++++++++++- .../projector/ObjectTemplateProcessor.java | 16 +- .../projector/PasswordPolicyProcessor.java | 289 +++++++----------- .../security/AuthenticationEvaluatorImpl.java | 17 +- .../MidpointRestAuthenticationHandler.java | 10 +- .../model/impl/security/SecurityHelper.java | 137 ++++++++- ...ringAuthenticationInjectorInterceptor.java | 13 +- .../impl/security/UserProfileServiceImpl.java | 35 +-- .../lens/TestPasswordPolicyProcessor.java | 29 +- .../security/TestAuthenticationEvaluator.java | 6 +- ...bstractConfiguredModelIntegrationTest.java | 3 + .../midpoint/model/intest/TestAudit.java | 14 +- .../intest/TestLoggingConfiguration.java | 2 + .../model/intest/TestMultiResource.java | 3 +- .../AbstractPasswordTest.java} | 103 ++++--- .../intest/password/TestPasswordDefault.java | 49 +++ .../password/TestPasswordDefaultHashing.java | 48 +++ .../src/test/resources/logback-test.xml | 8 +- ...ecurity-policy-default-storage-hashing.xml | 39 +++ .../security-policy-password-storage-none.xml | 39 +++ model/model-intest/testng-integration.xml | 7 +- .../test/AbstractModelIntegrationTest.java | 22 +- .../security/api/SecurityEnforcer.java | 6 +- .../security/api/UserProfileService.java | 7 +- .../security/impl/SecurityEnforcerImpl.java | 6 +- .../quartzimpl/TaskManagerQuartzImpl.java | 11 +- .../quartzimpl/execution/JobExecutor.java | 9 +- 41 files changed, 1151 insertions(+), 625 deletions(-) rename model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/{TestPassword.java => password/AbstractPasswordTest.java} (90%) create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefault.java create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java create mode 100644 model/model-intest/src/test/resources/password/security-policy-default-storage-hashing.xml create mode 100644 model/model-intest/src/test/resources/password/security-policy-password-storage-none.xml diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java index 28dc4ce9515..df7cf9af7b1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/MidPointGuiAuthorizationEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,7 +77,7 @@ public void setupPreAuthenticatedSecurityContext(Authentication authentication) } @Override - public void setupPreAuthenticatedSecurityContext(PrismObject user) { + public void setupPreAuthenticatedSecurityContext(PrismObject user) throws SchemaException { securityEnforcer.setupPreAuthenticatedSecurityContext(user); } @@ -203,7 +203,7 @@ public ObjectFilter preProcessObjec } @Override - public T runAs(Producer producer, PrismObject user) { + public T runAs(Producer producer, PrismObject user) throws SchemaException { return securityEnforcer.runAs(producer, user); } diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java index 78ce5e8a039..c5899268aaa 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ObjectDelta.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -414,6 +414,10 @@ private void removeModification(QName itemName, Class d public void removeReferenceModification(QName itemName) { removeModification(itemName, ReferenceDelta.class); } + + public void removeReferenceModification(ItemPath itemPath) { + removeModification(itemPath, ReferenceDelta.class); + } public void removeContainerModification(QName itemName) { removeModification(itemName, ContainerDelta.class); @@ -423,6 +427,10 @@ public void removePropertyModification(QName itemName) { removeModification(itemName, PropertyDelta.class); } + public void removePropertyModification(ItemPath itemPath) { + removeModification(itemPath, PropertyDelta.class); + } + public boolean isEmpty() { if (getChangeType() == ChangeType.DELETE) { // Delete delta is never empty diff --git a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ReferenceDelta.java b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ReferenceDelta.java index aaef0a25004..23769e7a6ec 100644 --- a/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ReferenceDelta.java +++ b/infra/prism/src/main/java/com/evolveum/midpoint/prism/delta/ReferenceDelta.java @@ -110,6 +110,16 @@ public static ReferenceDelta createModificationReplace(ItemPath path, PrismObjec return createModificationReplace(path, objectDefinition, new PrismReferenceValue(oid)); } + public static ReferenceDelta createModificationReplace(QName refName, Class type, PrismContext ctx , String oid) { + PrismObjectDefinition objectDefinition = ctx.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type); + return createModificationReplace(refName, objectDefinition, new PrismReferenceValue(oid)); + } + + public static ReferenceDelta createModificationReplace(ItemPath path, Class type, PrismContext ctx, String oid) { + PrismObjectDefinition objectDefinition = ctx.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(type); + return createModificationReplace(path, objectDefinition, new PrismReferenceValue(oid)); + } + public static ReferenceDelta createModificationReplace(ItemPath path, PrismObjectDefinition objectDefinition, PrismReferenceValue refValue) { PrismReferenceDefinition referenceDefinition = objectDefinition.findItemDefinition(path, PrismReferenceDefinition.class); diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index c52dc6cef70..88598ba9e2c 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -9163,17 +9163,38 @@ + + + +

+ Reference to the security policy settings which will be used for this organization. +

+
+ + tns:SecurityPolicyType + OrgType.securityPolicy + 230 + 3.6 + +
+
+

Reference to the password policy settings which will be used for generate/validate password for this organization. + Use either securityPolicyRef or passwordPolicyRef in orgs. Do not use both at the same time. This is not supported. +

+

+ DEPRECATED. Use securityPolicyRef instead.

tns:ValuePolicyType OrgType.passwordPolicy - 230 + 240 + true
@@ -9416,7 +9437,7 @@ - + Minimal number of value occurences. minOccurs set to zero means that the value @@ -9431,6 +9452,7 @@ Maximal number of value occurences. + If not specified then the default schema limitation is imposed. @@ -11265,11 +11287,26 @@ + + + + Common setting applied to all other credetials type. Any of this + setting can be overridden in the individual credentials setting. + + + + + + Nonce settings used to generate one-time random values. + Used in self-registration, e-mail-based password reset and possibly also + other scenarios. + + @@ -11537,7 +11574,7 @@
- + Structure that specifies common elements to all the credential policies. @@ -11547,6 +11584,8 @@ + + @@ -11607,7 +11646,16 @@ - + + + + The number of entries to keep in the credential history. Also specifies the + number of past credential values that will be checked before accepting a new + credential change. + + + + @@ -11728,6 +11776,77 @@ + + + + + Specifies the method of storing the credential in midPoint. + + + + + + + + + + The type of credential storage. + + + + + + + + + + + + + + + + + + Credential will be stored in an ecrypted form. + This is a symetric (reversible) encryption. + MidPoint will be able to get a cleartext form of + the credential if needed. + + + + + + + + + + Credential will be stored in a hashed form. + One-way (ireversible) cryptographic hash or key derivation function + will be used to transform the credential before storage. + MidPoint will NOT be able to get a cleartext form of + the credential, but it can still compare credential values. + + + + + + + + + + MidPoint will not store the credential at all. + MidPoitn will only work with credential in the memory + while it is needed to complete current operation. + The credential will be discarded after the operation. + + + + + + + + @@ -11740,7 +11859,7 @@ - + @@ -11767,7 +11886,12 @@ The number of entries to keep in the password history. Also specifies the number of past passwords that will be checked before accepting a new password change. + DEPRECATED. use historyLength instead. + This is not even implemented. It does not work. + + true + @@ -11820,7 +11944,7 @@ - + @@ -11905,7 +12029,7 @@ - + diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java index f874646f321..d2e0deaef5d 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/AssignmentEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2015 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -451,7 +451,7 @@ private void evaluateFocusMappings(EvaluatedAssignmentImpl evaluatedAssignmen AssignmentPathVariables assignmentPathVariables = LensUtil.computeAssignmentPathVariables(assignmentPath); for (MappingType mappingType: mappingsType.getMapping()) { - Mapping mapping = LensUtil.createFocusMapping(mappingFactory, lensContext, mappingType, source, focusOdo, + Mapping mapping = mappingEvaluator.createFocusMapping(mappingFactory, lensContext, mappingType, source, focusOdo, assignmentPathVariables, systemConfiguration, now, sourceDescription, task, result); if (mapping == null) { continue; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java index 0e42e9960de..ee983118527 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,10 +34,12 @@ import com.evolveum.midpoint.model.common.expression.ExpressionVariables; import com.evolveum.midpoint.model.impl.ModelObjectResolver; import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder; +import com.evolveum.midpoint.model.impl.lens.projector.CredentialsProcessor; import com.evolveum.midpoint.model.impl.lens.projector.FocusConstraintsChecker; import com.evolveum.midpoint.model.impl.lens.projector.PolicyRuleProcessor; import com.evolveum.midpoint.model.impl.util.Utils; import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.ObjectDelta; @@ -135,6 +137,9 @@ public class ChangeExecutor { @Autowired private PolicyRuleProcessor policyRuleProcessor; + + @Autowired + private CredentialsProcessor credentialsProcessor; private PrismObjectDefinition userDefinition = null; private PrismObjectDefinition shadowDefinition = null; @@ -149,7 +154,7 @@ private void locateDefinitions() { // returns true if current operation has to be restarted, see // ObjectAlreadyExistsException handling (TODO specify more exactly) - public boolean executeChanges(LensContext syncContext, Task task, + public boolean executeChanges(LensContext context, Task task, OperationResult parentResult) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { @@ -158,13 +163,13 @@ public boolean executeChanges(LensContext syncContext, // FOCUS - syncContext.checkAbortRequested(); + context.checkAbortRequested(); - LensFocusContext focusContext = syncContext.getFocusContext(); + LensFocusContext focusContext = context.getFocusContext(); if (focusContext != null) { - ObjectDelta focusDelta = focusContext.getWaveExecutableDelta(syncContext.getExecutionWave()); + ObjectDelta focusDelta = focusContext.getWaveExecutableDelta(context.getExecutionWave()); - focusDelta = policyRuleProcessor.applyAssignmentSituation(syncContext, focusDelta); + focusDelta = policyRuleProcessor.applyAssignmentSituation(context, focusDelta); if (focusDelta != null) { @@ -174,15 +179,21 @@ public boolean executeChanges(LensContext syncContext, OperationResult subResult = result.createSubresult( OPERATION_EXECUTE_FOCUS + "." + focusContext.getObjectTypeClass().getSimpleName()); + try { - syncContext.reportProgress(new ProgressInformation(FOCUS_OPERATION, ENTERING)); - executeDelta(focusDelta, focusContext, syncContext, null, null, task, subResult); + // Will remove credential deltas or hash them + focusDelta = credentialsProcessor.transformFocusExectionDelta(context, focusDelta); + } catch (EncryptionException e) { + recordFatalError(subResult, result, null, e); + throw new SystemException(e.getMessage(), e); + } + try { + context.reportProgress(new ProgressInformation(FOCUS_OPERATION, ENTERING)); + executeDelta(focusDelta, focusContext, context, null, null, task, subResult); subResult.computeStatus(); - } catch (SchemaException e) { - recordFatalError(subResult, result, null, e); - throw e; - } catch (ObjectNotFoundException e) { + } catch (SchemaException | ObjectNotFoundException | CommunicationException | ConfigurationException + | SecurityViolationException | ExpressionEvaluationException e) { recordFatalError(subResult, result, null, e); throw e; } catch (ObjectAlreadyExistsException e) { @@ -192,23 +203,11 @@ public boolean executeChanges(LensContext syncContext, } result.computeStatusComposite(); throw e; - } catch (CommunicationException e) { - recordFatalError(subResult, result, null, e); - throw e; - } catch (ConfigurationException e) { - recordFatalError(subResult, result, null, e); - throw e; - } catch (SecurityViolationException e) { - recordFatalError(subResult, result, null, e); - throw e; - } catch (ExpressionEvaluationException e) { - recordFatalError(subResult, result, null, e); - throw e; } catch (RuntimeException e) { recordFatalError(subResult, result, null, e); throw e; } finally { - syncContext.reportProgress(new ProgressInformation(FOCUS_OPERATION, subResult)); + context.reportProgress(new ProgressInformation(FOCUS_OPERATION, subResult)); } } else { LOGGER.trace("Skipping focus change execute, because user delta is null"); @@ -217,89 +216,89 @@ public boolean executeChanges(LensContext syncContext, // PROJECTIONS - syncContext.checkAbortRequested(); + context.checkAbortRequested(); boolean restartRequested = false; - for (LensProjectionContext accCtx : syncContext.getProjectionContexts()) { - if (accCtx.getWave() != syncContext.getExecutionWave()) { + for (LensProjectionContext projCtx : context.getProjectionContexts()) { + if (projCtx.getWave() != context.getExecutionWave()) { continue; } - if (!accCtx.isCanProject()) { + if (!projCtx.isCanProject()) { continue; } // we should not get here, but just to be sure - if (accCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.IGNORE) { - LOGGER.trace("Skipping ignored projection context {}", accCtx.toHumanReadableString()); + if (projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.IGNORE) { + LOGGER.trace("Skipping ignored projection context {}", projCtx.toHumanReadableString()); continue; } OperationResult subResult = result.createSubresult( - OPERATION_EXECUTE_PROJECTION + "." + accCtx.getObjectTypeClass().getSimpleName()); - subResult.addContext("discriminator", accCtx.getResourceShadowDiscriminator()); - if (accCtx.getResource() != null) { - subResult.addParam("resource", accCtx.getResource().getName()); + OPERATION_EXECUTE_PROJECTION + "." + projCtx.getObjectTypeClass().getSimpleName()); + subResult.addContext("discriminator", projCtx.getResourceShadowDiscriminator()); + if (projCtx.getResource() != null) { + subResult.addParam("resource", projCtx.getResource().getName()); } try { - syncContext.checkAbortRequested(); + context.checkAbortRequested(); - syncContext.reportProgress(new ProgressInformation(RESOURCE_OBJECT_OPERATION, - accCtx.getResourceShadowDiscriminator(), ENTERING)); + context.reportProgress(new ProgressInformation(RESOURCE_OBJECT_OPERATION, + projCtx.getResourceShadowDiscriminator(), ENTERING)); - executeReconciliationScript(accCtx, syncContext, BeforeAfterType.BEFORE, task, subResult); + executeReconciliationScript(projCtx, context, BeforeAfterType.BEFORE, task, subResult); - ObjectDelta accDelta = accCtx.getExecutableDelta(); + ObjectDelta projDelta = projCtx.getExecutableDelta(); - if (shouldBeDeleted(accDelta, accCtx)) { - accDelta = ObjectDelta.createDeleteDelta(accCtx.getObjectTypeClass(), accCtx.getOid(), + if (shouldBeDeleted(projDelta, projCtx)) { + projDelta = ObjectDelta.createDeleteDelta(projCtx.getObjectTypeClass(), projCtx.getOid(), prismContext); } - if (accCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.BROKEN) { - if (syncContext.getFocusContext() != null - && syncContext.getFocusContext().getDelta() != null - && syncContext.getFocusContext().getDelta().isDelete() - && syncContext.getOptions() != null - && ModelExecuteOptions.isForce(syncContext.getOptions())) { - if (accDelta == null) { - accDelta = ObjectDelta.createDeleteDelta(accCtx.getObjectTypeClass(), - accCtx.getOid(), prismContext); + if (projCtx.getSynchronizationPolicyDecision() == SynchronizationPolicyDecision.BROKEN) { + if (context.getFocusContext() != null + && context.getFocusContext().getDelta() != null + && context.getFocusContext().getDelta().isDelete() + && context.getOptions() != null + && ModelExecuteOptions.isForce(context.getOptions())) { + if (projDelta == null) { + projDelta = ObjectDelta.createDeleteDelta(projCtx.getObjectTypeClass(), + projCtx.getOid(), prismContext); } } - if (accDelta != null && accDelta.isDelete()) { + if (projDelta != null && projDelta.isDelete()) { - executeDelta(accDelta, accCtx, syncContext, null, accCtx.getResource(), task, + executeDelta(projDelta, projCtx, context, null, projCtx.getResource(), task, subResult); } } else { - if (accDelta == null || accDelta.isEmpty()) { + if (projDelta == null || projDelta.isEmpty()) { if (LOGGER.isTraceEnabled()) { - LOGGER.trace("No change for " + accCtx.getResourceShadowDiscriminator()); + LOGGER.trace("No change for " + projCtx.getResourceShadowDiscriminator()); } if (focusContext != null) { - updateLinks(focusContext, accCtx, task, subResult); + updateLinks(focusContext, projCtx, task, subResult); } // Make sure post-reconcile delta is always executed, // even if there is no change - executeReconciliationScript(accCtx, syncContext, BeforeAfterType.AFTER, task, + executeReconciliationScript(projCtx, context, BeforeAfterType.AFTER, task, subResult); subResult.computeStatus(); subResult.recordNotApplicableIfUnknown(); continue; - } else if (accDelta.isDelete() && accCtx.getResourceShadowDiscriminator() != null - && accCtx.getResourceShadowDiscriminator().getOrder() > 0) { + } else if (projDelta.isDelete() && projCtx.getResourceShadowDiscriminator() != null + && projCtx.getResourceShadowDiscriminator().getOrder() > 0) { // HACK ... for higher-order context check if this was // already deleted - LensProjectionContext lowerOrderContext = LensUtil.findLowerOrderContext(syncContext, - accCtx); + LensProjectionContext lowerOrderContext = LensUtil.findLowerOrderContext(context, + projCtx); if (lowerOrderContext != null && lowerOrderContext.isDelete()) { // We assume that this was already executed subResult.setStatus(OperationResultStatus.NOT_APPLICABLE); @@ -307,25 +306,25 @@ public boolean executeChanges(LensContext syncContext, } } - executeDelta(accDelta, accCtx, syncContext, null, accCtx.getResource(), task, subResult); + executeDelta(projDelta, projCtx, context, null, projCtx.getResource(), task, subResult); } if (focusContext != null) { - updateLinks(focusContext, accCtx, task, subResult); + updateLinks(focusContext, projCtx, task, subResult); } - executeReconciliationScript(accCtx, syncContext, BeforeAfterType.AFTER, task, subResult); + executeReconciliationScript(projCtx, context, BeforeAfterType.AFTER, task, subResult); subResult.computeStatus(); subResult.recordNotApplicableIfUnknown(); } catch (SchemaException e) { - recordProjectionExecutionException(e, accCtx, subResult, + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } catch (ObjectNotFoundException e) { - recordProjectionExecutionException(e, accCtx, subResult, + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } catch (ObjectAlreadyExistsException e) { @@ -335,8 +334,8 @@ public boolean executeChanges(LensContext syncContext, // "Users" is SAM Account Name which is used by a built-in group // - in such case, mark the context as broken - if (isRepeatedAlreadyExistsException(accCtx)) { - recordProjectionExecutionException(e, accCtx, subResult, + if (isRepeatedAlreadyExistsException(projCtx)) { + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } @@ -355,28 +354,28 @@ public boolean executeChanges(LensContext syncContext, break; // we will process remaining projections when retrying // the wave } catch (CommunicationException e) { - recordProjectionExecutionException(e, accCtx, subResult, + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } catch (ConfigurationException e) { - recordProjectionExecutionException(e, accCtx, subResult, + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } catch (SecurityViolationException e) { - recordProjectionExecutionException(e, accCtx, subResult, + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } catch (ExpressionEvaluationException e) { - recordProjectionExecutionException(e, accCtx, subResult, + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } catch (RuntimeException e) { - recordProjectionExecutionException(e, accCtx, subResult, + recordProjectionExecutionException(e, projCtx, subResult, SynchronizationPolicyDecision.BROKEN); continue; } finally { - syncContext.reportProgress(new ProgressInformation(RESOURCE_OBJECT_OPERATION, - accCtx.getResourceShadowDiscriminator(), subResult)); + context.reportProgress(new ProgressInformation(RESOURCE_OBJECT_OPERATION, + projCtx.getResourceShadowDiscriminator(), subResult)); } } @@ -1372,7 +1371,6 @@ private String modifyProvisioningOb ProvisioningOperationTypeType.MODIFY, resource, task, result); } Utils.setRequestee(task, context); - LOGGER.info("MOD options {}", options); String changedOid = provisioning.modifyObject(objectTypeClass, oid, modifications, scripts, options, task, result); Utils.clearRequestee(task); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java index 58da4e5ae0c..8941a28fed1 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensContext.java @@ -96,7 +96,6 @@ public class LensContext implements ModelContext { transient private ObjectTemplateType focusTemplate; transient private ProjectionPolicyType accountSynchronizationSettings; - transient private ValuePolicyType globalPasswordPolicy; transient private DeltaSetTriple evaluatedAssignmentTriple; @@ -319,14 +318,6 @@ public void setAccountSynchronizationSettings(ProjectionPolicyType accountSynchr this.accountSynchronizationSettings = accountSynchronizationSettings; } - public ValuePolicyType getGlobalPasswordPolicy() { - return globalPasswordPolicy; - } - - public void setGlobalPasswordPolicy(ValuePolicyType globalPasswordPolicy) { - this.globalPasswordPolicy = globalPasswordPolicy; - } - public int getProjectionWave() { return projectionWave; } @@ -1141,13 +1132,6 @@ public String toString() { + focusContext + ", " + projectionContexts + ")"; } - public ValuePolicyType getEffectivePasswordPolicy() { - if (getFocusContext().getOrgPasswordPolicy() != null) { - return getFocusContext().getOrgPasswordPolicy(); - } - return globalPasswordPolicy; - } - public void setProgressListeners(Collection progressListeners) { this.progressListeners = progressListeners; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensFocusContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensFocusContext.java index eb51c8aabdb..c795fdec349 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensFocusContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensFocusContext.java @@ -44,7 +44,7 @@ public class LensFocusContext extends LensElementContext secondaryDeltas = new ObjectDeltaWaves(); - transient private ValuePolicyType orgPasswordPolicy; + transient private SecurityPolicyType securityPolicy; transient private ObjectPolicyConfigurationType objectPolicyConfigurationType; private int getProjectionWave() { @@ -55,12 +55,12 @@ private int getExecutionWave() { return getLensContext().getProjectionWave(); } - public void setOrgPasswordPolicy(ValuePolicyType orgPasswordPolicy) { - this.orgPasswordPolicy = orgPasswordPolicy; + public SecurityPolicyType getSecurityPolicy() { + return securityPolicy; } - - public ValuePolicyType getOrgPasswordPolicy() { - return orgPasswordPolicy; + + public void setSecurityPolicy(SecurityPolicyType securityPolicy) { + this.securityPolicy = securityPolicy; } public ObjectPolicyConfigurationType getObjectPolicyConfigurationType() { diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java index 1d7c3521431..842eb03259a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensProjectionContext.java @@ -677,19 +677,7 @@ public boolean isCanProject() { public void setAccountPasswordPolicy(ValuePolicyType accountPasswordPolicy) { this.accountPasswordPolicy = accountPasswordPolicy; } - - public ValuePolicyType getEffectivePasswordPolicy() { - if (accountPasswordPolicy != null) { - return accountPasswordPolicy; - } - - if (getLensContext().getFocusContext().getOrgPasswordPolicy() != null){ - return getLensContext().getFocusContext().getOrgPasswordPolicy(); - } - return getLensContext().getGlobalPasswordPolicy(); - } - public AssignmentPolicyEnforcementType getAssignmentPolicyEnforcementType() { // TODO: per-resource assignment enforcement ResourceType resource = getResource(); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java index 16ba45f8d2a..4b7766d5a6c 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensUtil.java @@ -868,160 +868,6 @@ private static boolean isValid(String lifecycleState, ActivationType activationT return effectiveStatus == ActivationStatusType.ENABLED; } - public static Mapping createFocusMapping(final MappingFactory mappingFactory, - final LensContext context, final MappingType mappingType, ObjectType originObject, - ObjectDeltaObject focusOdo, AssignmentPathVariables assignmentPathVariables, PrismObject configuration, - XMLGregorianCalendar now, String contextDesc, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { - Integer iteration = null; - String iterationToken = null; - if (focusOdo.getNewObject() != null) { - F focusNewType = focusOdo.getNewObject().asObjectable(); - iteration = focusNewType.getIteration(); - iterationToken = focusNewType.getIterationToken(); - } else if (focusOdo.getOldObject() != null) { - F focusOldType = focusOdo.getOldObject().asObjectable(); - iteration = focusOldType.getIteration(); - iterationToken = focusOldType.getIterationToken(); - } - return createFocusMapping(mappingFactory, context, mappingType, originObject, focusOdo, assignmentPathVariables, - iteration, iterationToken, configuration, now, contextDesc, task, result); - } - - public static Mapping createFocusMapping(final MappingFactory mappingFactory, - final LensContext context, final MappingType mappingType, ObjectType originObject, - ObjectDeltaObject focusOdo, AssignmentPathVariables assignmentPathVariables, - Integer iteration, String iterationToken, PrismObject configuration, - XMLGregorianCalendar now, String contextDesc, final Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { - - if (!Mapping.isApplicableToChannel(mappingType, context.getChannel())) { - LOGGER.trace("Mapping {} not applicable to channel {}, skipping.", mappingType, context.getChannel()); - return null; - } - - StringPolicyResolver stringPolicyResolver = new StringPolicyResolver() { - private ItemPath outputPath; - private ItemDefinition outputDefinition; - @Override - public void setOutputPath(ItemPath outputPath) { - this.outputPath = outputPath; - } - - @Override - public void setOutputDefinition(ItemDefinition outputDefinition) { - this.outputDefinition = outputDefinition; - } - - @Override - public StringPolicyType resolve() { - if (outputDefinition.getName().equals(PasswordType.F_VALUE)) { - ValuePolicyType passwordPolicy = context.getEffectivePasswordPolicy(); - if (passwordPolicy == null) { - return null; - } - return passwordPolicy.getStringPolicy(); - } - if (mappingType.getExpression() != null){ - List> evaluators = mappingType.getExpression().getExpressionEvaluator(); - if (evaluators != null) { - for (JAXBElement jaxbEvaluator : evaluators) { - Object object = jaxbEvaluator.getValue(); - if (object instanceof GenerateExpressionEvaluatorType && ((GenerateExpressionEvaluatorType) object).getValuePolicyRef() != null) { - ObjectReferenceType ref = ((GenerateExpressionEvaluatorType) object).getValuePolicyRef(); - try { - ValuePolicyType valuePolicyType = mappingFactory.getObjectResolver().resolve(ref, ValuePolicyType.class, - null, "resolving value policy for generate attribute "+ outputDefinition.getName()+" value", task, new OperationResult("Resolving value policy")); - if (valuePolicyType != null) { - return valuePolicyType.getStringPolicy(); - } - } catch (CommonException ex) { - throw new SystemException(ex.getMessage(), ex); - } - } - } - - } - } - return null; - - } - }; - - ExpressionVariables variables = new ExpressionVariables(); - variables.addVariableDefinition(ExpressionConstants.VAR_USER, focusOdo); - variables.addVariableDefinition(ExpressionConstants.VAR_FOCUS, focusOdo); - variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION, iteration); - variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION_TOKEN, iterationToken); - variables.addVariableDefinition(ExpressionConstants.VAR_CONFIGURATION, configuration); - - Collection targetValues = computeTargetValues(mappingType.getTarget(), focusOdo, variables, mappingFactory.getObjectResolver(), contextDesc, task, result); - - Mapping.Builder mappingBuilder = mappingFactory.createMappingBuilder(mappingType, contextDesc) - .sourceContext(focusOdo) - .targetContext(context.getFocusContext().getObjectDefinition()) - .variables(variables) - .originalTargetValues(targetValues) - .originType(OriginType.USER_POLICY) - .originObject(originObject) - .stringPolicyResolver(stringPolicyResolver) - .rootNode(focusOdo) - .now(now); - - mappingBuilder = addAssignmentPathVariables(mappingBuilder, assignmentPathVariables); - - Mapping mapping = mappingBuilder.build(); - - ItemPath itemPath = mapping.getOutputPath(); - if (itemPath == null) { - // no output element, i.e. this is a "validation mapping" - return mapping; - } - - PrismObject focusNew = focusOdo.getNewObject(); - if (focusNew != null) { - Item existingUserItem = (Item) focusNew.findItem(itemPath); - if (existingUserItem != null && !existingUserItem.isEmpty() - && mapping.getStrength() == MappingStrengthType.WEAK) { - // This valueConstruction only applies if the property does not have a value yet. - // ... but it does - LOGGER.trace("Mapping {} is weak and focus already has a value {}, skipping.", mapping, existingUserItem); - return null; - } - } - - return mapping; - } - - private static Collection computeTargetValues(VariableBindingDefinitionType target, - ObjectDeltaObject defaultSource, ExpressionVariables variables, ObjectResolver objectResolver, String contextDesc, - Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { - if (target == null) { - // Is this correct? What about default targets? - return null; - } - - ItemPathType itemPathType = target.getPath(); - if (itemPathType == null) { - // Is this correct? What about default targets? - return null; - } - ItemPath path = itemPathType.getItemPath(); - - Object object = ExpressionUtil.resolvePath(path, variables, defaultSource, objectResolver, contextDesc, task, result); - if (object == null) { - return new ArrayList<>(); - } else if (object instanceof Item) { - return ((Item) object).getValues(); - } else if (object instanceof PrismValue) { - return (List) Collections.singletonList((PrismValue) object); - } else if (object instanceof ItemDeltaItem) { - ItemDeltaItem idi = (ItemDeltaItem) object; - PrismValueDeltaSetTriple triple = idi.toDeltaSetTriple(); - return triple != null ? triple.getNonNegativeValues() : new ArrayList(); - } else { - throw new IllegalStateException("Unsupported target value(s): " + object.getClass() + " (" + object + ")"); - } - } - public static AssignmentPathVariables computeAssignmentPathVariables(AssignmentPathImpl assignmentPath) throws SchemaException { if (assignmentPath == null || assignmentPath.isEmpty()) { return null; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ObjectDeltaWaves.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ObjectDeltaWaves.java index 2322582215a..63a221c0de4 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ObjectDeltaWaves.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ObjectDeltaWaves.java @@ -402,12 +402,12 @@ public ObjectDeltaWavesType toObjectDeltaWavesType() throws SchemaException { } // don't forget to apply provisioning definitions to resulting deltas (it's the client responsibility) - public static ObjectDeltaWaves fromObjectDeltaWavesType(ObjectDeltaWavesType secondaryDeltas, PrismContext prismContext) throws SchemaException { + public static ObjectDeltaWaves fromObjectDeltaWavesType(ObjectDeltaWavesType secondaryDeltas, PrismContext prismContext) throws SchemaException { if (secondaryDeltas == null) { return null; } - ObjectDeltaWaves retval = new ObjectDeltaWaves(); + ObjectDeltaWaves retval = new ObjectDeltaWaves<>(); int max = 0; for (ObjectDeltaWaveType odwt : secondaryDeltas.getWave()) { @@ -416,7 +416,7 @@ public static ObjectDeltaWaves fromObjectDeltaWavesType(ObjectDeltaWavesType sec } } - ObjectDelta[] wavesAsArray = new ObjectDelta[max+1]; + ObjectDelta[] wavesAsArray = new ObjectDelta[max+1]; for (ObjectDeltaWaveType odwt : secondaryDeltas.getWave()) { wavesAsArray[odwt.getNumber()] = DeltaConvertor.createObjectDelta(odwt.getDelta(), prismContext); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java index 09883176e62..9da968af79a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ContextLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -410,21 +410,6 @@ private void loadFromSystemConfig(LensContext context, context.setAccountSynchronizationSettings(globalAccountSynchronizationSettings); } - if (context.getGlobalPasswordPolicy() == null){ - - ValuePolicyType globalPasswordPolicy = systemConfigurationType.getGlobalPasswordPolicy(); - - if (globalPasswordPolicy == null){ - if (systemConfigurationType.getGlobalPasswordPolicyRef() != null){ - PrismObject passwordPolicy = cacheRepositoryService.getObject(ValuePolicyType.class, systemConfigurationType.getGlobalPasswordPolicyRef().getOid(), null, result); - if (passwordPolicy != null){ - globalPasswordPolicy = passwordPolicy.asObjectable(); - } - } - } - - context.setGlobalPasswordPolicy(globalPasswordPolicy); - } } // expects that object policy configuration is already set in focusContext diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java index 71fc5812db4..5a60f512586 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,13 +22,17 @@ import com.evolveum.midpoint.model.common.expression.StringPolicyResolver; import com.evolveum.midpoint.model.common.mapping.Mapping; import com.evolveum.midpoint.model.common.mapping.MappingFactory; +import com.evolveum.midpoint.model.impl.ModelObjectResolver; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; import com.evolveum.midpoint.model.impl.lens.LensUtil; import com.evolveum.midpoint.model.impl.lens.OperationalDataManager; +import com.evolveum.midpoint.model.impl.security.SecurityHelper; import com.evolveum.midpoint.model.impl.util.Utils; import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.delta.*; import com.evolveum.midpoint.prism.delta.builder.DeltaBuilder; import com.evolveum.midpoint.prism.path.ItemPath; @@ -60,10 +64,6 @@ /** * Processor that takes password from user and synchronizes it to accounts. - *

- * The implementation is very simple now. It only cares about password value, - * not expiration or other password facets. It completely ignores other - * credential types. * * @author Radovan Semancik */ @@ -86,14 +86,41 @@ public class CredentialsProcessor { @Autowired(required=true) private OperationalDataManager metadataManager; + + @Autowired(required = true) + private SecurityHelper securityHelper; + + @Autowired(required = true) + Protector protector; public void processFocusCredentials(LensContext context, XMLGregorianCalendar now, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException { + processSecurityPolicy(context, now, task, result); processFocusPassword(context, now, task, result); processFocusNonce(context, now, task, result); } - + + private void processSecurityPolicy(LensContext context, XMLGregorianCalendar now, + Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, + SchemaException, PolicyViolationException { + LensFocusContext focusContext = context.getFocusContext(); + SecurityPolicyType securityPolicy = focusContext.getSecurityPolicy(); + if (securityPolicy == null) { + securityPolicy = securityHelper.locateSecurityPolicy(focusContext.getObjectAny(), context.getSystemConfiguration(), task, result); + if (securityPolicy == null) { + // store empty policy to avoid repeated lookups + securityPolicy = new SecurityPolicyType(); + } + focusContext.setSecurityPolicy(securityPolicy); + } + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Security policy:\n{}", securityPolicy==null?null:securityPolicy.asPrismObject().debugDump(1)); + } else { + LOGGER.debug("Security policy: {}", securityPolicy); + } + } + private void processFocusPassword(LensContext context, XMLGregorianCalendar now, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, PolicyViolationException { @@ -188,7 +215,7 @@ public void setOutputDefinition(ItemDefinition outputDefinition) { } @Override public StringPolicyType resolve() { - ValuePolicyType passwordPolicy = projCtx.getEffectivePasswordPolicy(); + ValuePolicyType passwordPolicy = passwordPolicyProcessor.determinePasswordPolicy(context, projCtx, task, result); if (passwordPolicy == null) { return null; } @@ -264,8 +291,7 @@ private void checkExistingDeltaSanity(LensProjectionContext projCtx, } private void processFocusCredentialsCommon(LensContext context, - ItemPath credentialsPath, - XMLGregorianCalendar now, Task task, OperationResult result) + ItemPath credentialsPath, XMLGregorianCalendar now, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException { LensFocusContext focusContext = context.getFocusContext(); @@ -380,4 +406,78 @@ private boolean hasMetadata(PrismContainerVal return false; } + /** + * Called from ChangeExecutor. Will modify the execution deltas to hash or remove credentials if needed. + */ + public ObjectDelta transformFocusExectionDelta(LensContext context, ObjectDelta focusDelta) throws SchemaException, EncryptionException { + LensFocusContext focusContext = context.getFocusContext(); + SecurityPolicyType securityPolicy = focusContext.getSecurityPolicy(); + if (securityPolicy == null) { + return focusDelta; + } + CredentialsPolicyType credsType = securityPolicy.getCredentials(); + if (credsType == null) { + return focusDelta; + } + ObjectDelta transformedDelta = focusDelta.clone(); + transformFocusExectionDeltaCredential(context, credsType, credsType.getPassword(), SchemaConstants.PATH_PASSWORD_VALUE, transformedDelta); + // TODO: nonce and others + + return transformedDelta; + } + + private void transformFocusExectionDeltaCredential(LensContext context, + CredentialsPolicyType credsType, CredentialPolicyType credPolicyType, + ItemPath valuePropertyPath, ObjectDelta delta) throws SchemaException, EncryptionException { + if (delta.isDelete()) { + return; + } + CredentialPolicyType defaltCredPolicyType = credsType.getDefault(); + CredentialsStorageMethodType storageMethod = null; + if (credPolicyType != null && credPolicyType.getStorageMethod() != null) { + storageMethod = credPolicyType.getStorageMethod(); + } else if (defaltCredPolicyType != null && defaltCredPolicyType.getStorageMethod() != null) { + storageMethod = defaltCredPolicyType.getStorageMethod(); + } + if (storageMethod == null) { + return; + } + CredentialsStorageTypeType storageType = storageMethod.getStorageType(); + if (storageType == null || storageType == CredentialsStorageTypeType.ENCRYPTION) { + return; + } else if (storageType == CredentialsStorageTypeType.HASHING) { + PrismPropertyValue pval = null; + if (delta.isAdd()) { + PrismProperty prop = delta.getObjectToAdd().findProperty(valuePropertyPath); + hashValues(prop.getValues(), storageMethod); + } else { + PropertyDelta propDelta = delta.findPropertyDelta(valuePropertyPath); + if (propDelta != null) { + hashValues(propDelta.getValuesToAdd(), storageMethod); + hashValues(propDelta.getValuesToReplace(), storageMethod); + hashValues(propDelta.getValuesToDelete(), storageMethod); + } + } + } else if (storageType == CredentialsStorageTypeType.NONE) { + if (delta.isAdd()) { + delta.getObjectToAdd().removeProperty(valuePropertyPath); + } else { + delta.removePropertyModification(valuePropertyPath); + } + } else { + throw new SchemaException("Unkwnon storage type "+storageType); + } + + } + + private void hashValues(Collection> values, + CredentialsStorageMethodType storageMethod) throws SchemaException, EncryptionException { + if (values == null) { + return; + } + for (PrismPropertyValue pval: values) { + protector.hash(pval.getValue()); + } + } + } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/InboundProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/InboundProcessor.java index 66b4138a534..ab98c672ad5 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/InboundProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/InboundProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -78,6 +78,9 @@ public class InboundProcessor { @Autowired private ContextLoader contextLoader; + @Autowired + private PasswordPolicyProcessor passwordPolicyProcessor; + @Autowired private MappingEvaluator mappingEvaluator; @@ -423,7 +426,7 @@ private .addVariableDefinition(ExpressionConstants.VAR_ACCOUNT, account) .addVariableDefinition(ExpressionConstants.VAR_SHADOW, account) .addVariableDefinition(ExpressionConstants.VAR_RESOURCE, resource) - .stringPolicyResolver(createStringPolicyResolver(context)) + .stringPolicyResolver(createStringPolicyResolver(context, task, result)) .originType(OriginType.INBOUND) .originObject(resource) .build(); @@ -563,7 +566,7 @@ private return outputFocusItemDelta.isEmpty() ? null : outputFocusItemDelta; } - private StringPolicyResolver createStringPolicyResolver(final LensContext context) { + private StringPolicyResolver createStringPolicyResolver(final LensContext context, final Task task, final OperationResult result) { StringPolicyResolver stringPolicyResolver = new StringPolicyResolver() { private ItemPath outputPath; private ItemDefinition outputDefinition; @@ -582,7 +585,7 @@ public StringPolicyType resolve() { if (!outputDefinition.getName().equals(PasswordType.F_VALUE)) { return null; } - ValuePolicyType passwordPolicy = context.getEffectivePasswordPolicy(); + ValuePolicyType passwordPolicy = passwordPolicyProcessor.determinePasswordPolicy(context.getFocusContext(), task, result); if (passwordPolicy == null) { return null; } @@ -704,7 +707,7 @@ public Mapping.Builder initialize(Mapping.Builder builder) throws SchemaExceptio builder = builder.addVariableDefinition(ExpressionConstants.VAR_ACCOUNT, accountNew) .addVariableDefinition(ExpressionConstants.VAR_SHADOW, accountNew) .addVariableDefinition(ExpressionConstants.VAR_RESOURCE, accContext.getResource()) - .stringPolicyResolver(createStringPolicyResolver(context)) + .stringPolicyResolver(createStringPolicyResolver(context, task, opResult)) .originType(OriginType.INBOUND) .originObject(accContext.getResource()); return builder; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java index b9732e82c08..acb1bb004a5 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/MappingEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Evolveum + * Copyright (c) 2013-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,26 +17,37 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import javax.xml.bind.JAXBElement; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.XMLGregorianCalendar; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.evolveum.midpoint.model.common.expression.ExpressionUtil; +import com.evolveum.midpoint.model.common.expression.ExpressionVariables; +import com.evolveum.midpoint.model.common.expression.ItemDeltaItem; +import com.evolveum.midpoint.model.common.expression.ObjectDeltaObject; +import com.evolveum.midpoint.model.common.expression.StringPolicyResolver; import com.evolveum.midpoint.model.common.mapping.Mapping; import com.evolveum.midpoint.model.common.mapping.MappingFactory; import com.evolveum.midpoint.model.impl.expr.ExpressionEnvironment; import com.evolveum.midpoint.model.impl.expr.ModelExpressionThreadLocalHolder; +import com.evolveum.midpoint.model.impl.lens.AssignmentPathVariables; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensElementContext; import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; +import com.evolveum.midpoint.model.impl.lens.LensUtil; import com.evolveum.midpoint.model.impl.trigger.RecomputeTriggerHandler; import com.evolveum.midpoint.prism.Item; import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.OriginType; import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismContext; @@ -47,18 +58,30 @@ import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple; import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectResolver; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.exception.CommonException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.GenerateExpressionEvaluatorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingStrengthType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.StringPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; +import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; /** * @author Radovan Semancik @@ -74,6 +97,9 @@ public class MappingEvaluator { @Autowired private MappingFactory mappingFactory; + + @Autowired + private PasswordPolicyProcessor passwordPolicyProcessor; public void evaluateMapping( Mapping mapping, LensContext lensContext, Task task, OperationResult parentResult) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException { @@ -423,5 +449,159 @@ public void setStrongMappingWasUsed(boolean strongMappingWasUsed) { } + + public Mapping createFocusMapping(final MappingFactory mappingFactory, + final LensContext context, final MappingType mappingType, ObjectType originObject, + ObjectDeltaObject focusOdo, AssignmentPathVariables assignmentPathVariables, PrismObject configuration, + XMLGregorianCalendar now, String contextDesc, Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { + Integer iteration = null; + String iterationToken = null; + if (focusOdo.getNewObject() != null) { + F focusNewType = focusOdo.getNewObject().asObjectable(); + iteration = focusNewType.getIteration(); + iterationToken = focusNewType.getIterationToken(); + } else if (focusOdo.getOldObject() != null) { + F focusOldType = focusOdo.getOldObject().asObjectable(); + iteration = focusOldType.getIteration(); + iterationToken = focusOldType.getIterationToken(); + } + return createFocusMapping(mappingFactory, context, mappingType, originObject, focusOdo, assignmentPathVariables, + iteration, iterationToken, configuration, now, contextDesc, task, result); + } + + public Mapping createFocusMapping(final MappingFactory mappingFactory, + final LensContext context, final MappingType mappingType, ObjectType originObject, + ObjectDeltaObject focusOdo, AssignmentPathVariables assignmentPathVariables, + Integer iteration, String iterationToken, PrismObject configuration, + XMLGregorianCalendar now, String contextDesc, final Task task, OperationResult result) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException { + + if (!Mapping.isApplicableToChannel(mappingType, context.getChannel())) { + LOGGER.trace("Mapping {} not applicable to channel {}, skipping.", mappingType, context.getChannel()); + return null; + } + + StringPolicyResolver stringPolicyResolver = new StringPolicyResolver() { + private ItemPath outputPath; + private ItemDefinition outputDefinition; + @Override + public void setOutputPath(ItemPath outputPath) { + this.outputPath = outputPath; + } + + @Override + public void setOutputDefinition(ItemDefinition outputDefinition) { + this.outputDefinition = outputDefinition; + } + + @Override + public StringPolicyType resolve() { + if (outputDefinition.getName().equals(PasswordType.F_VALUE)) { + ValuePolicyType passwordPolicy = passwordPolicyProcessor.determinePasswordPolicy(context.getFocusContext(), task, result); + if (passwordPolicy == null) { + return null; + } + return passwordPolicy.getStringPolicy(); + } + if (mappingType.getExpression() != null){ + List> evaluators = mappingType.getExpression().getExpressionEvaluator(); + if (evaluators != null) { + for (JAXBElement jaxbEvaluator : evaluators) { + Object object = jaxbEvaluator.getValue(); + if (object instanceof GenerateExpressionEvaluatorType && ((GenerateExpressionEvaluatorType) object).getValuePolicyRef() != null) { + ObjectReferenceType ref = ((GenerateExpressionEvaluatorType) object).getValuePolicyRef(); + try { + ValuePolicyType valuePolicyType = mappingFactory.getObjectResolver().resolve(ref, ValuePolicyType.class, + null, "resolving value policy for generate attribute "+ outputDefinition.getName()+" value", task, new OperationResult("Resolving value policy")); + if (valuePolicyType != null) { + return valuePolicyType.getStringPolicy(); + } + } catch (CommonException ex) { + throw new SystemException(ex.getMessage(), ex); + } + } + } + + } + } + return null; + + } + }; + + ExpressionVariables variables = new ExpressionVariables(); + variables.addVariableDefinition(ExpressionConstants.VAR_USER, focusOdo); + variables.addVariableDefinition(ExpressionConstants.VAR_FOCUS, focusOdo); + variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION, iteration); + variables.addVariableDefinition(ExpressionConstants.VAR_ITERATION_TOKEN, iterationToken); + variables.addVariableDefinition(ExpressionConstants.VAR_CONFIGURATION, configuration); + + Collection targetValues = computeTargetValues(mappingType.getTarget(), focusOdo, variables, mappingFactory.getObjectResolver(), contextDesc, task, result); + + Mapping.Builder mappingBuilder = mappingFactory.createMappingBuilder(mappingType, contextDesc) + .sourceContext(focusOdo) + .targetContext(context.getFocusContext().getObjectDefinition()) + .variables(variables) + .originalTargetValues(targetValues) + .originType(OriginType.USER_POLICY) + .originObject(originObject) + .stringPolicyResolver(stringPolicyResolver) + .rootNode(focusOdo) + .now(now); + + mappingBuilder = LensUtil.addAssignmentPathVariables(mappingBuilder, assignmentPathVariables); + + Mapping mapping = mappingBuilder.build(); + + ItemPath itemPath = mapping.getOutputPath(); + if (itemPath == null) { + // no output element, i.e. this is a "validation mapping" + return mapping; + } + + PrismObject focusNew = focusOdo.getNewObject(); + if (focusNew != null) { + Item existingUserItem = (Item) focusNew.findItem(itemPath); + if (existingUserItem != null && !existingUserItem.isEmpty() + && mapping.getStrength() == MappingStrengthType.WEAK) { + // This valueConstruction only applies if the property does not have a value yet. + // ... but it does + LOGGER.trace("Mapping {} is weak and focus already has a value {}, skipping.", mapping, existingUserItem); + return null; + } + } + + return mapping; + } + + private Collection computeTargetValues(VariableBindingDefinitionType target, + ObjectDeltaObject defaultSource, ExpressionVariables variables, ObjectResolver objectResolver, String contextDesc, + Task task, OperationResult result) throws SchemaException, ObjectNotFoundException { + if (target == null) { + // Is this correct? What about default targets? + return null; + } + + ItemPathType itemPathType = target.getPath(); + if (itemPathType == null) { + // Is this correct? What about default targets? + return null; + } + ItemPath path = itemPathType.getItemPath(); + + Object object = ExpressionUtil.resolvePath(path, variables, defaultSource, objectResolver, contextDesc, task, result); + if (object == null) { + return new ArrayList<>(); + } else if (object instanceof Item) { + return ((Item) object).getValues(); + } else if (object instanceof PrismValue) { + return (List) Collections.singletonList((PrismValue) object); + } else if (object instanceof ItemDeltaItem) { + ItemDeltaItem idi = (ItemDeltaItem) object; + PrismValueDeltaSetTriple triple = idi.toDeltaSetTriple(); + return triple != null ? triple.getNonNegativeValues() : new ArrayList(); + } else { + throw new IllegalStateException("Unsupported target value(s): " + object.getClass() + " (" + object + ")"); + } + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ObjectTemplateProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ObjectTemplateProcessor.java index 0328f2710d5..e7798ffb5ba 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ObjectTemplateProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/ObjectTemplateProcessor.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Map.Entry; +import javax.xml.bind.JAXBElement; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; @@ -38,11 +39,14 @@ import com.evolveum.midpoint.common.ActivationComputer; import com.evolveum.midpoint.model.common.expression.ExpressionFactory; +import com.evolveum.midpoint.model.common.expression.ExpressionVariables; import com.evolveum.midpoint.model.common.expression.ObjectDeltaObject; +import com.evolveum.midpoint.model.common.expression.StringPolicyResolver; import com.evolveum.midpoint.model.common.mapping.Mapping; import com.evolveum.midpoint.model.common.mapping.MappingFactory; import com.evolveum.midpoint.model.common.mapping.PrismValueDeltaSetTripleProducer; import com.evolveum.midpoint.model.impl.ModelObjectResolver; +import com.evolveum.midpoint.model.impl.lens.AssignmentPathVariables; import com.evolveum.midpoint.model.impl.lens.ItemValueWithOrigin; import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.model.impl.lens.LensFocusContext; @@ -50,6 +54,7 @@ import com.evolveum.midpoint.model.impl.trigger.RecomputeTriggerHandler; import com.evolveum.midpoint.prism.Item; import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.OriginType; import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismContext; @@ -62,18 +67,23 @@ import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.exception.CommonException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.PolicyViolationException; import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.GenerateExpressionEvaluatorType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingStrengthType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType; import com.evolveum.midpoint.xml.ns._public.common.common_3.VariableBindingDefinitionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateItemDefinitionType; @@ -81,7 +91,11 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateMappingType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectTemplateType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.StringPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; /** * Processor to handle object template. @@ -427,7 +441,7 @@ private XM if (mappingPhase != phase) { continue; } - Mapping mapping = LensUtil.createFocusMapping(mappingFactory, context, mappingType, objectTemplateType, focusOdo, + Mapping mapping = mappingEvaluator.createFocusMapping(mappingFactory, context, mappingType, objectTemplateType, focusOdo, null, iteration, iterationToken, context.getSystemConfiguration(), now, contextDesc, task, result); if (mapping == null) { continue; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java index 078e84a2213..5ce5c208083 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java @@ -69,6 +69,7 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.CharacterClassType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CheckExpressionType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; @@ -76,9 +77,11 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordCredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordHistoryEntryType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordLifeTimeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.common.common_3.StringLimitType; import com.evolveum.midpoint.xml.ns._public.common.common_3.StringPolicyType; @@ -173,22 +176,55 @@ void processPasswordPolicy(LensFocusContext focusContex } } - ValuePolicyType passwordPolicy; - if (focusContext.getOrgPasswordPolicy() == null) { - passwordPolicy = determineValuePolicy(userDelta, focusContext.getObjectAny(), context, task, result); - focusContext.setOrgPasswordPolicy(passwordPolicy); - } else { - passwordPolicy = focusContext.getOrgPasswordPolicy(); - } - + ValuePolicyType passwordPolicy = determinePasswordPolicy(focusContext, task, result); processPasswordPolicy(passwordPolicy, focusContext.getObjectOld(), null, passwordValueProperty, task, result); if (passwordValueProperty != null && isPasswordChange) { - processPasswordHistoryDeltas(focusContext, context, now, task, result); + processPasswordHistoryDeltas(focusContext, context, focusContext.getSecurityPolicy(), now, task, result); } } + public ValuePolicyType determinePasswordPolicy(LensContext context, LensProjectionContext projectionContext, Task task, OperationResult result) { + ValuePolicyType passwordPolicy = projectionContext.getAccountPasswordPolicy(); + + if (passwordPolicy == null) { + passwordPolicy = determinePasswordPolicy(context.getFocusContext(), task, result); + } + + return passwordPolicy; + } + + public ValuePolicyType determinePasswordPolicy(LensFocusContext focusContext, Task task, OperationResult result) { + SecurityPolicyType securityPolicy = focusContext.getSecurityPolicy(); + if (securityPolicy == null) { + return null; + } + CredentialsPolicyType creds = securityPolicy.getCredentials(); + if (creds == null) { + return null; + } + PasswordCredentialsPolicyType password = creds.getPassword(); + if (password == null) { + return null; + } + ObjectReferenceType passwordPolicyRef = password.getPasswordPolicyRef(); + if (passwordPolicyRef == null) { + return null; + } + PrismObject passwordPolicy = passwordPolicyRef.asReferenceValue().getObject(); + if (passwordPolicy == null) { + try { + passwordPolicy = resolver.resolve(passwordPolicyRef.asReferenceValue(), "password policy", task, result); + } catch (ObjectNotFoundException e) { + LOGGER.error("Missing password policy {}", passwordPolicyRef, e); + return null; + } + passwordPolicyRef.asReferenceValue().setObject(passwordPolicy); + } + return passwordPolicy.asObjectable(); + } + private void processPasswordPolicy(ValuePolicyType passwordPolicy, PrismObject focus, PrismObject projection, PrismProperty passwordProperty, Task task, OperationResult result) throws PolicyViolationException, SchemaException, ObjectNotFoundException, ExpressionEvaluationException { @@ -241,132 +277,8 @@ private boolean wasExecuted(ObjectDelta userDelt return false; } - - //TODO: maybe some caching of orgs????? - protected ValuePolicyType determineValuePolicy(ObjectDelta userDelta, PrismObject object, LensContext context, Task task, OperationResult result) throws SchemaException{ - //check the modification of organization first - ValuePolicyType valuePolicy = determineValuePolicy(userDelta, task, result); - - //if null, check the existing organization - if (valuePolicy == null){ - valuePolicy = determineValuePolicy(object, task, result); - } - - //if still null, just use global policy - if (valuePolicy == null){ - valuePolicy = context.getEffectivePasswordPolicy(); - } - - if (valuePolicy != null){ - LOGGER.trace("Value policy {} will be user to check password.", valuePolicy.getName().getOrig()); - } - - return valuePolicy; - } - - protected ValuePolicyType determineValuePolicy(ObjectDelta userDelta, Task task, OperationResult result) - throws SchemaException { - if (userDelta == null) { - return null; - } - ReferenceDelta orgDelta = userDelta.findReferenceModification(UserType.F_PARENT_ORG_REF); - - LOGGER.trace("Determining password policy from org delta."); - if (orgDelta == null) { - return null; - } - - PrismReferenceValue orgRefValue = orgDelta.getAnyValue(); - if (orgRefValue == null) { // delta may be of type "replace to null" - return null; - } - - ValuePolicyType passwordPolicy = null; - try { - PrismObject org = resolver.resolve(orgRefValue, - "resolving parent org ref", null, null, result); - OrgType orgType = org.asObjectable(); - ObjectReferenceType ref = orgType.getPasswordPolicyRef(); - if (ref != null) { - LOGGER.trace("Org {} has specified password policy.", orgType); - passwordPolicy = resolver.resolve(ref, ValuePolicyType.class, null, - "resolving password policy for organization", task, result); - LOGGER.trace("Resolved password policy {}", passwordPolicy); - } - - if (passwordPolicy == null) { - passwordPolicy = determineValuePolicy(org, task, result); - } - } catch (ObjectNotFoundException e) { - throw new IllegalStateException(e); - } - - return passwordPolicy; - } - private ValuePolicyType determineValuePolicy(PrismObject object, Task task, OperationResult result) - throws SchemaException { - LOGGER.trace("Determining password policies from object: {}", ObjectTypeUtil.toShortString(object)); - PrismReference orgRef = object.findReference(ObjectType.F_PARENT_ORG_REF); - if (orgRef == null) { - return null; - } - List orgRefValues = orgRef.getValues(); - ValuePolicyType resultingValuePolicy = null; - List> orgs = new ArrayList>(); - try { - for (PrismReferenceValue orgRefValue : orgRefValues) { - if (orgRefValue != null) { - - PrismObject org = resolver.resolve(orgRefValue, "resolving parent org ref", null, null, result); - orgs.add(org); - ValuePolicyType valuePolicy = resolvePolicy(org, task, result); - - if (valuePolicy != null) { - if (resultingValuePolicy == null) { - resultingValuePolicy = valuePolicy; - } else if (!StringUtils.equals(valuePolicy.getOid(), resultingValuePolicy.getOid())) { - throw new IllegalStateException( - "Found more than one policy while trying to validate user's password. Please check your configuration"); - } - } - } - } - } catch (ObjectNotFoundException ex) { - throw new IllegalStateException(ex); - } - // go deeper - if (resultingValuePolicy == null) { - for (PrismObject orgType : orgs) { - resultingValuePolicy = determineValuePolicy(orgType, task, result); - if (resultingValuePolicy != null) { - return resultingValuePolicy; - } - } - } - return resultingValuePolicy; - } - - private ValuePolicyType resolvePolicy(PrismObject org, Task task, OperationResult result) - throws SchemaException { - try { - OrgType orgType = org.asObjectable(); - ObjectReferenceType ref = orgType.getPasswordPolicyRef(); - if (ref == null) { - return null; - } - - return resolver.resolve(ref, ValuePolicyType.class, null, - "resolving password policy for organization", task, result); - - } catch (ObjectNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - throw new IllegalStateException(e); - } - - } void processPasswordPolicy(LensProjectionContext projectionContext, LensContext context, Task task, OperationResult result) throws SchemaException, PolicyViolationException, ObjectNotFoundException, ExpressionEvaluationException{ @@ -405,14 +317,7 @@ void processPasswordPolicy(LensProjectionContext projecti password = (PrismProperty) passwordValueDelta.getItemNewMatchingPath(null); } - ValuePolicyType passwordPolicy = null; - if (isCheckOrgPolicy(context)){ - passwordPolicy = determineValuePolicy(context.getFocusContext().getObjectAny(), task, result); - context.getFocusContext().setOrgPasswordPolicy(passwordPolicy); - } - if (passwordPolicy == null) { - passwordPolicy = projectionContext.getEffectivePasswordPolicy(); - } + ValuePolicyType passwordPolicy = determinePasswordPolicy(context, projectionContext, task, result); if (accountShadow == null) { accountShadow = projectionContext.getObjectNew(); @@ -421,28 +326,7 @@ void processPasswordPolicy(LensProjectionContext projecti processPasswordPolicy(passwordPolicy, null, accountShadow, password, task, result); } - private boolean isCheckOrgPolicy(LensContext context) throws SchemaException{ - LensFocusContext focusCtx = context.getFocusContext(); - if (focusCtx == null) { - return false; // TODO - ok? - } - if (focusCtx.getDelta() != null){ - if (focusCtx.getDelta().isAdd()){ - return false; - } - - if (focusCtx.getDelta().isModify() && focusCtx.getDelta().hasItemDelta(SchemaConstants.PATH_PASSWORD_VALUE)){ - return false; - } - } - - if (focusCtx.getOrgPasswordPolicy() != null){ - return false; - } - - return true; - } public boolean validatePassword(String newPassword, PasswordType currentPasswordType, ValuePolicyType pp, PrismObject object, String shortDesc, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { @@ -453,8 +337,8 @@ public boolean validatePassword(String newPassword, Passw result.addParam("policyName", pp.getName()); normalize(pp); - if (newPassword == null && pp.getMinOccurs() != null - && XsdTypeMapper.multiplicityToInteger(pp.getMinOccurs()) == 0) { + if (newPassword == null && + (pp.getMinOccurs() == null || XsdTypeMapper.multiplicityToInteger(pp.getMinOccurs()) == 0)) { // No password is allowed result.recordSuccess(); return true; @@ -472,8 +356,11 @@ public boolean validatePassword(String newPassword, Passw testMaximalLength(newPassword, lims, result, message); testMinimalUniqueCharacters(newPassword, lims, result, message); - testPasswordHistoryEntries(newPassword, currentPasswordType, pp, result, message); - + try { + testPasswordHistoryEntries(newPassword, currentPasswordType, pp, result, message); + } catch (EncryptionException e) { + throw new SystemException(e.getMessage(), e); + } if (lims.getLimit() == null || lims.getLimit().isEmpty()) { if (message.toString() == null || message.toString().isEmpty()) { @@ -545,12 +432,18 @@ private void normalize(ValuePolicyType pp) { } private void testPasswordHistoryEntries(String newPassword, PasswordType currentPasswordType, - ValuePolicyType pp, OperationResult result, StringBuilder message) { + ValuePolicyType pp, OperationResult result, StringBuilder message) throws SchemaException, EncryptionException { + if (newPassword == null) { + return; + } if (currentPasswordType == null) { return; } + ProtectedStringType newPasswordPs = new ProtectedStringType(); + newPasswordPs.setClearValue(newPassword); + PasswordLifeTimeType lifetime = pp.getLifetime(); if (lifetime == null) { return; @@ -561,7 +454,7 @@ private void testPasswordHistoryEntries(String newPassword, PasswordType current return; } - if (passwordEquals(newPassword, currentPasswordType.getValue())) { + if (passwordEquals(newPasswordPs, currentPasswordType.getValue())) { appendHistoryViolationMessage(result, message); return; } @@ -574,7 +467,7 @@ private void testPasswordHistoryEntries(String newPassword, PasswordType current // success (history has more entries than needed) return; } - if (passwordEquals(newPassword, historyEntry.getValue())) { + if (passwordEquals(newPasswordPs, historyEntry.getValue())) { LOGGER.trace("Password history entry #{} matched (changed {})", i, historyEntry.getChangeTimestamp()); appendHistoryViolationMessage(result, message); return; @@ -778,8 +671,11 @@ private void testCheckExpression(String newPassword, Limi } - private boolean passwordEquals(String newPassword, ProtectedStringType currentPassword) { - return determinePasswordValue(currentPassword).equals(newPassword); + private boolean passwordEquals(ProtectedStringType newPasswordPs, ProtectedStringType currentPassword) throws SchemaException, EncryptionException { + if (currentPassword == null) { + return newPasswordPs == null; + } + return protector.compare(newPasswordPs, currentPassword); } @@ -815,7 +711,7 @@ private String determinePasswordValue(ProtectedStringType passValue) { public void processPasswordHistoryDeltas(LensFocusContext focusContext, - LensContext context, XMLGregorianCalendar now, Task task, OperationResult result) + LensContext context, SecurityPolicyType securityPolicy, XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException { PrismObject focus = focusContext.getObjectOld(); Validate.notNull(focus, "Focus object must not be null"); @@ -828,7 +724,7 @@ public void processPasswordHistoryDeltas(LensFocusContext< PrismContainer historyEntries = password .findOrCreateContainer(PasswordType.F_HISTORY_ENTRY); - int maxPasswordsToSave = getMaxPasswordsToSave(context.getFocusContext(), context, task, result); + int maxPasswordsToSave = getMaxPasswordsToSave(context.getFocusContext(), context, securityPolicy, task, result); List historyEntryValues = getSortedHistoryList(historyEntries, true); int newHistoryEntries = 0; @@ -840,25 +736,50 @@ public void processPasswordHistoryDeltas(LensFocusContext< } private int getMaxPasswordsToSave(LensFocusContext focusContext, - LensContext context, Task task, OperationResult result) throws SchemaException { - ValuePolicyType passwordPolicy; - if (focusContext.getOrgPasswordPolicy() == null) { - passwordPolicy = determineValuePolicy(focusContext.getDelta(), - focusContext.getObjectAny(), context, task, result); - focusContext.setOrgPasswordPolicy(passwordPolicy); - } else { - passwordPolicy = focusContext.getOrgPasswordPolicy(); - } + LensContext context, SecurityPolicyType securityPolicy, Task task, OperationResult result) throws SchemaException { + if (securityPolicy == null) { + return 0; + } + + CredentialsPolicyType creds = securityPolicy.getCredentials(); + if (creds == null) { + return 0; + } + + PasswordCredentialsPolicyType passwordCredsType = creds.getPassword(); + if (passwordCredsType == null) { + return 0; + } + + Integer historyLength = passwordCredsType.getHistoryLength(); + if (historyLength != null) { + if (historyLength < 0) { + return 0; + } + // One less than the history. The "first" history entry is the current password itself. + return historyLength - 1; + } + + // LEGACY, deprecated + + ObjectReferenceType passwordPolicyRef = passwordCredsType.getPasswordPolicyRef(); + if (passwordPolicyRef == null) { + return 0; + } + + PrismObject passwordPolicy = passwordPolicyRef.asReferenceValue().getObject(); if (passwordPolicy == null) { return 0; } - if (passwordPolicy.getLifetime() == null) { + ValuePolicyType passwordPolicyType = passwordPolicy.asObjectable(); + + if (passwordPolicyType.getLifetime() == null) { return 0; } - Integer passwordHistoryLength = passwordPolicy.getLifetime().getPasswordHistoryLength(); + Integer passwordHistoryLength = passwordPolicyType.getLifetime().getPasswordHistoryLength(); if (passwordHistoryLength == null) { return 0; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/AuthenticationEvaluatorImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/AuthenticationEvaluatorImpl.java index 28d8e1898b3..1d089305d75 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/AuthenticationEvaluatorImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/AuthenticationEvaluatorImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016 Evolveum + * Copyright (c) 2016-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.security.api.UserProfileService; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; @@ -241,8 +242,12 @@ private MidPointPrincipal getAndCheckPrincipal(ConnectionEnvironment connEnv, St } catch (ObjectNotFoundException e) { recordAuthenticationFailure(enteredUsername, connEnv, "no user"); throw new UsernameNotFoundException("web.security.provider.invalid"); + } catch (SchemaException e) { + recordAuthenticationFailure(enteredUsername, connEnv, "schema error"); + throw new AccessDeniedException("web.security.provider.invalid"); } + if (principal == null) { recordAuthenticationFailure(enteredUsername, connEnv, "no user"); throw new UsernameNotFoundException("web.security.provider.invalid"); @@ -268,7 +273,7 @@ private boolean hasAnyAuthorization(MidPointPrincipal principal) { return false; } - private void checkPasswordValidityAndAge(ConnectionEnvironment connEnv, @NotNull MidPointPrincipal principal, ProtectedStringType protectedString, MetadataType passwordMetadata, + private void checkPasswordValidityAndAge(ConnectionEnvironment connEnv, @NotNull MidPointPrincipal principal, ProtectedStringType protectedString, MetadataType passwordMetadata, T passwordCredentialsPolicy) { if (protectedString == null) { recordAuthenticationFailure(principal, connEnv, "no stored password value"); @@ -319,21 +324,21 @@ private String getPassword(ConnectionEnvironment connEnv, @NotNull MidPointPrinc return decryptedPassword; } - private boolean isLockedOut(AbstractCredentialType credentialsType, AbstractCredentialPolicyType credentialsPolicy) { + private boolean isLockedOut(AbstractCredentialType credentialsType, CredentialPolicyType credentialsPolicy) { return isOverFailedLockoutAttempts(credentialsType, credentialsPolicy) && !isLockoutExpired(credentialsType, credentialsPolicy); } - private boolean isOverFailedLockoutAttempts(AbstractCredentialType credentialsType, AbstractCredentialPolicyType credentialsPolicy) { + private boolean isOverFailedLockoutAttempts(AbstractCredentialType credentialsType, CredentialPolicyType credentialsPolicy) { int failedLogins = credentialsType.getFailedLogins() != null ? credentialsType.getFailedLogins() : 0; return isOverFailedLockoutAttempts(failedLogins, credentialsPolicy); } - private boolean isOverFailedLockoutAttempts(int failedLogins, AbstractCredentialPolicyType credentialsPolicy) { + private boolean isOverFailedLockoutAttempts(int failedLogins, CredentialPolicyType credentialsPolicy) { return credentialsPolicy != null && credentialsPolicy.getLockoutMaxFailedAttempts() != null && credentialsPolicy.getLockoutMaxFailedAttempts() > 0 && failedLogins >= credentialsPolicy.getLockoutMaxFailedAttempts(); } - private boolean isLockoutExpired(AbstractCredentialType credentialsType, AbstractCredentialPolicyType credentialsPolicy) { + private boolean isLockoutExpired(AbstractCredentialType credentialsType, CredentialPolicyType credentialsPolicy) { Duration lockoutDuration = credentialsPolicy.getLockoutDuration(); if (lockoutDuration == null) { return false; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointRestAuthenticationHandler.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointRestAuthenticationHandler.java index b2a62560b12..03ca374527f 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointRestAuthenticationHandler.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointRestAuthenticationHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016 Evolveum + * Copyright (c) 2013-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -117,7 +117,13 @@ public void handleRequest(Message m, ContainerRequestContext requestCtx) { task.setOwner(user.asPrismObject()); m.put(RestServiceUtil.MESSAGE_PROPERTY_TASK_NAME, task); - securityEnforcer.setupPreAuthenticatedSecurityContext(user.asPrismObject()); + try { + securityEnforcer.setupPreAuthenticatedSecurityContext(user.asPrismObject()); + } catch (SchemaException e) { + securityHelper.auditLoginFailure(enteredUsername, user, connEnv, "Schema error: "+e.getMessage()); + requestCtx.abortWith(Response.status(Status.BAD_REQUEST).build()); + return; + } LOGGER.trace("Authenticated to REST service as {}", user); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java index bed7732bd5d..07fcfa973cb 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-2016 Evolveum + * Copyright (c) 2015-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,15 +19,38 @@ import com.evolveum.midpoint.audit.api.AuditEventStage; import com.evolveum.midpoint.audit.api.AuditEventType; import com.evolveum.midpoint.audit.api.AuditService; +import com.evolveum.midpoint.model.impl.ModelObjectResolver; +import com.evolveum.midpoint.model.impl.lens.LensContext; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismReference; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.delta.ReferenceDelta; +import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; +import com.evolveum.midpoint.schema.util.ObjectResolver; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.security.api.ConnectionEnvironment; +import com.evolveum.midpoint.security.api.MidPointPrincipal; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskManager; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OrgType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordCredentialsPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; + +import org.apache.commons.lang3.StringUtils; import org.apache.cxf.binding.soap.SoapMessage; import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor; import org.apache.wss4j.common.ext.WSSecurityException; @@ -40,6 +63,9 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import java.util.ArrayList; +import java.util.List; + import javax.xml.soap.SOAPMessage; /** @@ -55,8 +81,12 @@ public class SecurityHelper { @Autowired private TaskManager taskManager; + @Autowired private AuditService auditService; + + @Autowired(required = true) + private ModelObjectResolver objectResolver; public void auditLoginSuccess(@NotNull UserType user, @NotNull ConnectionEnvironment connEnv) { auditLogin(user.getName().getOrig(), user, connEnv, OperationResultStatus.SUCCESS, null); @@ -150,4 +180,109 @@ public SOAPMessage getSOAPMessage(SoapMessage msg) { SAAJInInterceptor.INSTANCE.handleMessage(msg); return msg.getContent(SOAPMessage.class); } + + public SecurityPolicyType locateSecurityPolicy(PrismObject user, PrismObject systemConfiguration, Task task, OperationResult result) throws SchemaException { + SecurityPolicyType orgSecurityPolicy = determineOrgSecurityPolicy(user, task, result); + if (orgSecurityPolicy != null) { + return orgSecurityPolicy; + } + if (systemConfiguration == null) { + return null; + } + ObjectReferenceType globalSecurityPolicyRef = systemConfiguration.asObjectable().getGlobalSecurityPolicyRef(); + if (globalSecurityPolicyRef == null) { + return null; + } + try { + return objectResolver.resolve(globalSecurityPolicyRef, SecurityPolicyType.class, null, "global security policy reference in system configuration", task, result); + } catch (ObjectNotFoundException | SchemaException e) { + LOGGER.error(e.getMessage(), e); + return null; + } + } + + private SecurityPolicyType determineOrgSecurityPolicy(PrismObject object, Task task, OperationResult result) + throws SchemaException { + LOGGER.trace("Determining security policies from object: {}", object); + PrismReference orgRef = object.findReference(ObjectType.F_PARENT_ORG_REF); + if (orgRef == null) { + return null; + } + List orgRefValues = orgRef.getValues(); + SecurityPolicyType resultingSecurityPolicy = null; + List> orgs = new ArrayList>(); + try { + for (PrismReferenceValue orgRefValue : orgRefValues) { + if (orgRefValue != null) { + + PrismObject org = objectResolver.resolve(orgRefValue, "resolving parent org ref", null, null, result); + orgs.add(org); + SecurityPolicyType securityPolicy = resolveOrgSecurityPolicy(org, task, result); + + if (securityPolicy != null) { + if (resultingSecurityPolicy == null) { + resultingSecurityPolicy = securityPolicy; + } else if (!StringUtils.equals(securityPolicy.getOid(), resultingSecurityPolicy.getOid())) { + throw new SchemaException( + "Found more than one security policy for user. Please check your configuration"); + } + } + } + } + } catch (ObjectNotFoundException ex) { + throw new IllegalStateException(ex); + } + // go deeper + if (resultingSecurityPolicy == null) { + for (PrismObject orgType : orgs) { + resultingSecurityPolicy = determineOrgSecurityPolicy(orgType, task, result); + if (resultingSecurityPolicy != null) { + return resultingSecurityPolicy; + } + } + } + return resultingSecurityPolicy; + } + + private SecurityPolicyType resolveOrgSecurityPolicy(PrismObject org, Task task, OperationResult result) + throws SchemaException { + try { + SecurityPolicyType securityPolicy = null; + OrgType orgType = org.asObjectable(); + ObjectReferenceType securityPolicyRef = orgType.getSecurityPolicyRef(); + if (securityPolicyRef != null) { + LOGGER.trace("Org {} has specified security policy.", orgType); + securityPolicy = objectResolver.resolve(securityPolicyRef, SecurityPolicyType.class, null, + "resolving security policy for organization", task, result); + LOGGER.trace("Resolved security policy {}", securityPolicy); + } else { + ObjectReferenceType paswordPolicyRef = orgType.getPasswordPolicyRef(); + if (paswordPolicyRef != null) { + LOGGER.trace("Org {} has specified password policy.", orgType); + ValuePolicyType passordPolicy = objectResolver.resolve(paswordPolicyRef, ValuePolicyType.class, null, + "resolving password policy for organization", task, result); + LOGGER.trace("Resolved password policy {}", securityPolicy); + securityPolicy = createSecurityPolicy(passordPolicy); + } + } + + return securityPolicy; + + } catch (ObjectNotFoundException e) { + LOGGER.error("Cannot find policy: {}", e.getMessage(), e); + return null; + } + } + + private SecurityPolicyType createSecurityPolicy(ValuePolicyType passordPolicy) { + SecurityPolicyType securityPolicy = new SecurityPolicyType(); + CredentialsPolicyType creds = new CredentialsPolicyType(); + PasswordCredentialsPolicyType passd = new PasswordCredentialsPolicyType(); + ObjectReferenceType passwordPolicyRef = new ObjectReferenceType(); + passwordPolicyRef.asReferenceValue().setObject(passordPolicy.asPrismObject()); + passd.setPasswordPolicyRef(passwordPolicyRef); + creds.setPassword(passd); + securityPolicy.setCredentials(creds); + return securityPolicy; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java index fde260fd76e..97a04a695fa 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -128,7 +128,16 @@ public void handleMessage(SoapMessage message) throws Fault { throw createFault(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION); } - MidPointPrincipal principal = userDetailsService.getPrincipal(username); + MidPointPrincipal principal; + try { + principal = userDetailsService.getPrincipal(username); + } catch (SchemaException e) { + LOGGER.debug("Access to web service denied for user '{}': schema error: {}", + username, e.getMessage(), e); + message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); + securityHelper.auditLoginFailure(username, null, connEnv, "Schema error: "+e.getMessage()); + throw new Fault(e); + } LOGGER.trace("Principal: {}", principal); if (principal == null) { message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java index 494b5d436c7..7077cae110b 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/UserProfileServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,6 +94,9 @@ public class UserProfileServiceImpl implements UserProfileService, UserDetailsSe @Autowired(required = true) private MappingEvaluator mappingEvaluator; + + @Autowired(required = true) + private SecurityHelper securityHelper; @Autowired(required = true) private UserComputer userComputer; @@ -111,7 +114,7 @@ public class UserProfileServiceImpl implements UserProfileService, UserDetailsSe private TaskManager taskManager; @Override - public MidPointPrincipal getPrincipal(String username) throws ObjectNotFoundException { + public MidPointPrincipal getPrincipal(String username) throws ObjectNotFoundException, SchemaException { OperationResult result = new OperationResult(OPERATION_GET_PRINCIPAL); PrismObject user = null; try { @@ -130,12 +133,12 @@ public MidPointPrincipal getPrincipal(String username) throws ObjectNotFoundExce } @Override - public MidPointPrincipal getPrincipal(PrismObject user) { + public MidPointPrincipal getPrincipal(PrismObject user) throws SchemaException { OperationResult result = new OperationResult(OPERATION_GET_PRINCIPAL); return createPrincipal(user, result); } - private MidPointPrincipal createPrincipal(PrismObject user, OperationResult result) { + private MidPointPrincipal createPrincipal(PrismObject user, OperationResult result) throws SchemaException { if (user == null) { return null; } @@ -180,7 +183,7 @@ private PrismObject findByUsername(String username, OperationResult re return list.get(0); } - private void initializePrincipalFromAssignments(MidPointPrincipal principal, PrismObject systemConfiguration) { + private void initializePrincipalFromAssignments(MidPointPrincipal principal, PrismObject systemConfiguration) throws SchemaException { UserType userType = principal.getUser(); Collection authorizations = principal.getAuthorities(); @@ -189,7 +192,7 @@ private void initializePrincipalFromAssignments(MidPointPrincipal principal, Pri Task task = taskManager.createTaskInstance(UserProfileServiceImpl.class.getName() + ".addAuthorizations"); OperationResult result = task.getResult(); - principal.setApplicableSecurityPolicy(locateSecurityPolicy(principal, systemConfiguration, task, result)); + principal.setApplicableSecurityPolicy(securityHelper.locateSecurityPolicy(userType.asPrismObject(), systemConfiguration, task, result)); if (!userType.getAssignment().isEmpty()) { AssignmentEvaluator assignmentEvaluator = new AssignmentEvaluator<>(); @@ -246,22 +249,6 @@ private void initializePrincipalFromAssignments(MidPointPrincipal principal, Pri principal.setAdminGuiConfiguration(AdminGuiConfigTypeUtil.compileAdminGuiConfiguration(adminGuiConfigurations, systemConfiguration)); } - private SecurityPolicyType locateSecurityPolicy(MidPointPrincipal principal, PrismObject systemConfiguration, Task task, OperationResult result) { - if (systemConfiguration == null) { - return null; - } - ObjectReferenceType globalSecurityPolicyRef = systemConfiguration.asObjectable().getGlobalSecurityPolicyRef(); - if (globalSecurityPolicyRef == null) { - return null; - } - try { - return objectResolver.resolve(globalSecurityPolicyRef, SecurityPolicyType.class, null, "global security policy reference in system configuration", task, result); - } catch (ObjectNotFoundException | SchemaException e) { - LOGGER.error(e.getMessage(), e); - return null; - } - } - private MidPointPrincipal save(MidPointPrincipal person, OperationResult result) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { UserType oldUserType = getUserByOid(person.getOid(), result); PrismObject oldUser = oldUserType.asPrismObject(); @@ -323,6 +310,8 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx return getPrincipal(username); } catch (ObjectNotFoundException e) { throw new UsernameNotFoundException(e.getMessage(), e); + } catch (SchemaException e) { + throw new SystemException(e.getMessage(), e); } } @@ -333,6 +322,8 @@ public UserDetails mapUserFromContext(DirContextOperations ctx, String username, return getPrincipal(username); } catch (ObjectNotFoundException e) { throw new UsernameNotFoundException(e.getMessage(), e); + } catch (SchemaException e) { + throw new SystemException(e.getMessage(), e); } } diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPasswordPolicyProcessor.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPasswordPolicyProcessor.java index d316bc8b7f3..cc58ec7b4dc 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPasswordPolicyProcessor.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/lens/TestPasswordPolicyProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2016 Evolveum + * Copyright (c) 2010-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,6 @@ import org.testng.annotations.Test; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.crypto.EncryptionException; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.result.OperationResult; @@ -39,11 +38,13 @@ import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.PolicyViolationException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordCredentialsPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordHistoryEntryType; import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; @@ -269,8 +270,8 @@ public void test202createUserJackNoPasswordHistory() throws Exception { } @Test - public void test203modifyUserJackPasswordNoPasswordHisotry() throws Exception { - final String TEST_NAME = "test202modifyUserJackPasswordNoPasswordHisotry"; + public void test203modifyUserJackPasswordNoPasswordHistory() throws Exception { + final String TEST_NAME = "test203modifyUserJackPasswordNoPasswordHistory"; TestUtil.displayTestTile(TEST_NAME); Task task = taskManager.createTaskInstance(TEST_NAME); OperationResult result = task.getResult(); @@ -306,20 +307,10 @@ private void initPasswordPolicy(String title, String passwordPolicyOid) throws E OperationResult result = task.getResult(); ObjectReferenceType passwordPolicyRef = ObjectTypeUtil.createObjectRef(passwordPolicyOid, - ObjectTypes.PASSWORD_POLICY); - modifyObjectReplaceReference(SystemConfigurationType.class, SYSTEM_CONFIGURATION_OID, - SystemConfigurationType.F_GLOBAL_PASSWORD_POLICY_REF, task, result, - passwordPolicyRef.asReferenceValue()); - - PrismObject systemConfiguration = getObject(SystemConfigurationType.class, - SYSTEM_CONFIGURATION_OID); - assertNotNull("System configuration cannot be null", systemConfiguration); - - SystemConfigurationType systemConfigurationType = systemConfiguration.asObjectable(); - ObjectReferenceType globalPasswordPolicy = systemConfigurationType.getGlobalPasswordPolicyRef(); - assertNotNull("Expected that global password policy is configured", globalPasswordPolicy); - assertEquals("Password policies don't match", passwordPolicyOid, globalPasswordPolicy.getOid()); - + ObjectTypes.PASSWORD_POLICY); + modifyObjectReplaceReference(SecurityPolicyType.class, SECURITY_POLICY_OID, + new ItemPath(SecurityPolicyType.F_CREDENTIALS, CredentialsPolicyType.F_PASSWORD, PasswordCredentialsPolicyType.F_PASSWORD_POLICY_REF), + task, result, passwordPolicyRef.asReferenceValue()); } } diff --git a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/security/TestAuthenticationEvaluator.java b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/security/TestAuthenticationEvaluator.java index 45530f959c8..e9353697d00 100644 --- a/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/security/TestAuthenticationEvaluator.java +++ b/model/model-impl/src/test/java/com/evolveum/midpoint/model/impl/security/TestAuthenticationEvaluator.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2016 Evolveum + * Copyright (c) 2016-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,14 +114,14 @@ public void updateUser(MidPointPrincipal principal) { } @Override - public MidPointPrincipal getPrincipal(PrismObject user) { + public MidPointPrincipal getPrincipal(PrismObject user) throws SchemaException { MidPointPrincipal principal = userProfileService.getPrincipal(user); addFakeAuthorization(principal); return principal; } @Override - public MidPointPrincipal getPrincipal(String username) throws ObjectNotFoundException { + public MidPointPrincipal getPrincipal(String username) throws ObjectNotFoundException, SchemaException { MidPointPrincipal principal = userProfileService.getPrincipal(username); addFakeAuthorization(principal); return principal; diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java index 1ff27272316..cf3594f67ec 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/AbstractConfiguredModelIntegrationTest.java @@ -28,6 +28,7 @@ import com.evolveum.midpoint.prism.schema.PrismSchema; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.DummyResourceContoller; @@ -467,6 +468,8 @@ public AbstractConfiguredModelIntegrationTest() { @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { LOGGER.trace("initSystem"); + // We want logging config from logback-test.xml and not from system config object + InternalsConfig.avoidLoggingChange = true; super.initSystem(initTask, initResult); modelService.postInit(initResult); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java index 763047a4a99..1654e9380df 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestAudit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Evolveum + * Copyright (c) 2016-2017 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -586,10 +586,10 @@ public void test300ConcurrentAudits() throws Exception { for (int i = 0; i < NUM_THREADS; i++) { final int index = i; Thread thread = new Thread(() -> { - login(userAdministrator); - Task threadTask = taskManager.createTaskInstance(TestAudit.class.getName() + "." + TEST_NAME); - OperationResult threadResult = threadTask.getResult(); try { + login(userAdministrator); + Task threadTask = taskManager.createTaskInstance(TestAudit.class.getName() + "." + TEST_NAME); + OperationResult threadResult = threadTask.getResult(); for (int iteration = 0; iteration < ITERATIONS; iteration++) { display("Executing iteration " + iteration + " on user " + index); ObjectDelta delta = DeltaBuilder.deltaFor(UserType.class, prismContext) @@ -657,10 +657,10 @@ public void test310ConcurrentAuditsRaw() throws Exception { for (int i = 0; i < NUM_THREADS; i++) { final int index = i; Thread thread = new Thread(() -> { - login(userAdministrator); - Task threadTask = taskManager.createTaskInstance(TestAudit.class.getName() + "." + TEST_NAME); - OperationResult threadResult = threadTask.getResult(); try { + login(userAdministrator); + Task threadTask = taskManager.createTaskInstance(TestAudit.class.getName() + "." + TEST_NAME); + OperationResult threadResult = threadTask.getResult(); for (int iteration = 0; iteration < ITERATIONS; iteration++) { display("Executing iteration " + iteration + " in worker " + index); AuditEventRecord record = new AuditEventRecord(AuditEventType.MODIFY_OBJECT, AuditEventStage.EXECUTION); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLoggingConfiguration.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLoggingConfiguration.java index 11a78af6c41..cbb18f3f823 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLoggingConfiguration.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestLoggingConfiguration.java @@ -36,6 +36,7 @@ import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.PropertyDelta; import com.evolveum.midpoint.prism.util.PrismTestUtil; +import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.task.api.Task; @@ -65,6 +66,7 @@ public class TestLoggingConfiguration extends AbstractConfiguredModelIntegration @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { + InternalsConfig.avoidLoggingChange = false; // DO NOT call super.initSystem() as this will install system config. We do not want that here. userAdministrator = repoAddObjectFromFile(USER_ADMINISTRATOR_FILE, initResult); repoAddObjectFromFile(ROLE_SUPERUSER_FILE, initResult); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMultiResource.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMultiResource.java index af8054d291a..6cff2b67752 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMultiResource.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestMultiResource.java @@ -32,6 +32,7 @@ import com.evolveum.icf.dummy.resource.DummyResource; import com.evolveum.icf.dummy.resource.SchemaViolationException; import com.evolveum.midpoint.model.api.ModelExecuteOptions; +import com.evolveum.midpoint.model.intest.password.AbstractPasswordTest; import com.evolveum.midpoint.model.intest.rbac.TestRbac; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismObject; @@ -1556,7 +1557,7 @@ public void test385ModifyUserJackPasswordA() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.NONE); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPassword.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java similarity index 90% rename from model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPassword.java rename to model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java index 0a3d0f80946..3b05b6e5539 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/TestPassword.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.evolveum.midpoint.model.intest; +package com.evolveum.midpoint.model.intest.password; import static com.evolveum.midpoint.test.IntegrationTestTools.display; import static org.testng.AssertJUnit.*; @@ -38,11 +38,13 @@ import com.evolveum.icf.dummy.resource.ConflictException; import com.evolveum.icf.dummy.resource.SchemaViolationException; +import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.schema.constants.SchemaConstants; +import com.evolveum.midpoint.schema.internals.InternalsConfig; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.task.api.Task; @@ -55,22 +57,22 @@ */ @ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) -public class TestPassword extends AbstractInitializedModelIntegrationTest { +public abstract class AbstractPasswordTest extends AbstractInitializedModelIntegrationTest { - private static final String USER_PASSWORD_1_CLEAR = "d3adM3nT3llN0Tal3s"; - private static final String USER_PASSWORD_2_CLEAR = "bl4ckP3arl"; - private static final String USER_PASSWORD_3_CLEAR = "wh3r3sTheRum?"; - private static final String USER_PASSWORD_4_CLEAR = "sh1v3rM3T1mb3rs"; - private static final String USER_PASSWORD_5_CLEAR = "s3tSa1al"; - private static final String USER_PASSWORD_A_CLEAR = "A"; // too short - private static final String USER_PASSWORD_JACK_CLEAR = "12jAcK34"; // contains username - private static final String USER_PASSWORD_SPARROW_CLEAR = "saRRow123"; // contains familyName - private static final String USER_PASSWORD_VALID_1 = "abcd123"; - private static final String USER_PASSWORD_VALID_2 = "abcd223"; - private static final String USER_PASSWORD_VALID_3 = "abcd323"; - private static final String USER_PASSWORD_VALID_4 = "abcd423"; - - private static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "password"); + protected static final String USER_PASSWORD_1_CLEAR = "d3adM3nT3llN0Tal3s"; + protected static final String USER_PASSWORD_2_CLEAR = "bl4ckP3arl"; + protected static final String USER_PASSWORD_3_CLEAR = "wh3r3sTheRum?"; + protected static final String USER_PASSWORD_4_CLEAR = "sh1v3rM3T1mb3rs"; + protected static final String USER_PASSWORD_5_CLEAR = "s3tSa1al"; + protected static final String USER_PASSWORD_A_CLEAR = "A"; // too short + protected static final String USER_PASSWORD_JACK_CLEAR = "12jAcK34"; // contains username + protected static final String USER_PASSWORD_SPARROW_CLEAR = "saRRow123"; // contains familyName + protected static final String USER_PASSWORD_VALID_1 = "abcd123"; + protected static final String USER_PASSWORD_VALID_2 = "abcd223"; + protected static final String USER_PASSWORD_VALID_3 = "abcd323"; + protected static final String USER_PASSWORD_VALID_4 = "abcd423"; + + protected static final File TEST_DIR = new File(MidPointTestConstants.TEST_RESOURCES_DIR, "password"); protected static final File RESOURCE_DUMMY_UGLY_FILE = new File(TEST_DIR, "resource-dummy-ugly.xml"); protected static final String RESOURCE_DUMMY_UGLY_OID = "10000000-0000-0000-0000-000000344104"; @@ -79,26 +81,36 @@ public class TestPassword extends AbstractInitializedModelIntegrationTest { protected static final File PASSWORD_POLICY_UGLY_FILE = new File(TEST_DIR, "password-policy-ugly.xml"); protected static final String PASSWORD_POLICY_UGLY_OID = "cfb3fa9e-027a-11e7-8e2c-dbebaacaf4ee"; - private static final String USER_JACK_EMPLOYEE_NUMBER_NEW_BAD = "No1"; - private static final String USER_JACK_EMPLOYEE_NUMBER_NEW_GOOD = "pir321"; + protected static final File SECURITY_POLICY_DEFAULT_STORAGE_HASHING_FILE = new File(TEST_DIR, "security-policy-default-storage-hashing.xml"); + protected static final String SECURITY_POLICY_DEFAULT_STORAGE_HASHING_OID = "0ea3b93c-0425-11e7-bbc1-73566dc53d59"; + + protected static final File SECURITY_POLICY_PASSWORD_STORAGE_NONE_FILE = new File(TEST_DIR, "security-policy-password-storage-none.xml"); + protected static final String SECURITY_POLICY_PASSWORD_STORAGE_NONE_OID = "2997a20a-0423-11e7-af65-a7ab7d19442c"; + + protected static final String USER_JACK_EMPLOYEE_NUMBER_NEW_BAD = "No1"; + protected static final String USER_JACK_EMPLOYEE_NUMBER_NEW_GOOD = "pir321"; protected DummyResource dummyResourceUgly; protected DummyResourceContoller dummyResourceCtlUgly; protected ResourceType resourceDummyUglyType; protected PrismObject resourceDummyUgly; - private String accountOid; - private String accountRedOid; - private String accountUglyOid; - private String accountYellowOid; - private XMLGregorianCalendar lastPasswordChangeStart; - private XMLGregorianCalendar lastPasswordChangeEnd; + protected String accountOid; + protected String accountRedOid; + protected String accountUglyOid; + protected String accountYellowOid; + protected XMLGregorianCalendar lastPasswordChangeStart; + protected XMLGregorianCalendar lastPasswordChangeEnd; @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { super.initSystem(initTask, initResult); importObjectFromFile(PASSWORD_POLICY_UGLY_FILE); + importObjectFromFile(SECURITY_POLICY_DEFAULT_STORAGE_HASHING_FILE); + importObjectFromFile(SECURITY_POLICY_PASSWORD_STORAGE_NONE_FILE); + + setGlobalSecurityPolicy(getSecurityPolicyOid(), initResult); dummyResourceCtlUgly = DummyResourceContoller.create(RESOURCE_DUMMY_UGLY_NAME, resourceDummyUgly); dummyResourceCtlUgly.extendSchemaPirate(); @@ -109,6 +121,8 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti login(USER_ADMINISTRATOR_USERNAME); } + + protected abstract String getSecurityPolicyOid(); @Test public void test010AddPasswordPolicy() throws Exception { @@ -116,7 +130,7 @@ public void test010AddPasswordPolicy() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.NONE); @@ -144,7 +158,7 @@ public void test050CheckJackPassword() throws Exception { // this happens during test initialization when user-jack.xml is added // THEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); PrismObject userJack = getUser(USER_JACK_OID); @@ -161,7 +175,7 @@ public void test051ModifyUserJackPassword() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = createTask(TestPassword.class.getName() + "." + TEST_NAME); + Task task = createTask(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -194,7 +208,7 @@ public void test060CheckJackPasswordModelInteraction() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = createTask(TestPassword.class.getName() + "." + TEST_NAME); + Task task = createTask(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -226,7 +240,7 @@ public void test100ModifyUserJackAssignAccount() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -265,7 +279,7 @@ public void test110ModifyUserJackPassword() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -298,7 +312,7 @@ public void test111ModifyAccountJackPassword() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -331,7 +345,7 @@ public void test112ModifyJackPasswordUserAndAccount() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -378,7 +392,7 @@ public void test120ModifyUserJackAssignAccountDummyRedAndUgly() throws Exception TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -421,7 +435,7 @@ public void test121ModifyJackPasswordUserAndAccountRed() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -553,7 +567,7 @@ public void test130ModifyUserJackAssignAccountDummyYellow() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -599,7 +613,7 @@ public void test132ModifyUserJackPasswordA() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -649,8 +663,9 @@ public void test200ApplyPasswordPolicy() throws Exception { PrismReferenceValue passPolicyRef = new PrismReferenceValue(PASSWORD_POLICY_GLOBAL_OID, ValuePolicyType.COMPLEX_TYPE); // WHEN - modifyObjectReplaceReference(SystemConfigurationType.class, SystemObjectsType.SYSTEM_CONFIGURATION.value(), - SystemConfigurationType.F_GLOBAL_PASSWORD_POLICY_REF, task, result, passPolicyRef); + modifyObjectReplaceReference(SecurityPolicyType.class, getSecurityPolicyOid(), + new ItemPath(SecurityPolicyType.F_CREDENTIALS, CredentialsPolicyType.F_PASSWORD, PasswordCredentialsPolicyType.F_PASSWORD_POLICY_REF), + task, result, passPolicyRef); // THEN result.computeStatus(); @@ -668,7 +683,7 @@ public void test202ReconcileUserJack() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -777,7 +792,7 @@ public void test222ModifyUserJackPasswordBadContainer() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -983,7 +998,7 @@ public void test300TwoParentOrgRefs() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -1048,7 +1063,7 @@ public void test310RemovePassword() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -1086,7 +1101,7 @@ public void test320ChangeEmployeeNumber() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); @@ -1115,7 +1130,7 @@ public void test330RemoveEmployeeNumber() throws Exception { TestUtil.displayTestTile(this, TEST_NAME); // GIVEN - Task task = taskManager.createTaskInstance(TestPassword.class.getName() + "." + TEST_NAME); + Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefault.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefault.java new file mode 100644 index 00000000000..53b183659aa --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefault.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010-2017 Evolveum + * + * Licensed 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 com.evolveum.midpoint.model.intest.password; + +import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static org.testng.AssertJUnit.*; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; + +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; + +/** + * Password test with DEFAULT configuration of password storage. + * + * @author semancik + * + */ +@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestPasswordDefault extends AbstractPasswordTest { + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + + } + + @Override + protected String getSecurityPolicyOid() { + return SECURITY_POLICY_OID; + } + +} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java new file mode 100644 index 00000000000..466200eb772 --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2017 Evolveum + * + * Licensed 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 com.evolveum.midpoint.model.intest.password; + +import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static org.testng.AssertJUnit.*; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; + +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; + +/** + * Password test with HASHING storage for all credential types. + * + * @author semancik + * + */ +@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestPasswordDefaultHashing extends AbstractPasswordTest { + + @Override + public void initSystem(Task initTask, OperationResult initResult) throws Exception { + super.initSystem(initTask, initResult); + } + + @Override + protected String getSecurityPolicyOid() { + return SECURITY_POLICY_DEFAULT_STORAGE_HASHING_OID; + } + +} diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index 0494e15dcc5..6e74a791037 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -47,14 +47,16 @@ - + + + - + @@ -90,7 +92,7 @@ - + diff --git a/model/model-intest/src/test/resources/password/security-policy-default-storage-hashing.xml b/model/model-intest/src/test/resources/password/security-policy-default-storage-hashing.xml new file mode 100644 index 00000000000..55dbc02dd45 --- /dev/null +++ b/model/model-intest/src/test/resources/password/security-policy-default-storage-hashing.xml @@ -0,0 +1,39 @@ + + + + + Security Policy: default storage hashing + + + + hashing + + + + + http://midpoint.evolveum.com/xml/ns/public/security/question-2#q001 + true + How much wood would a woodchuck chuck if woodchuck could chuck wood? + + + http://midpoint.evolveum.com/xml/ns/public/security/question-2#q002 + What is your mother's best friend's uncle's grandaughter's dog's mother maiden name? + + + + diff --git a/model/model-intest/src/test/resources/password/security-policy-password-storage-none.xml b/model/model-intest/src/test/resources/password/security-policy-password-storage-none.xml new file mode 100644 index 00000000000..38df37c8853 --- /dev/null +++ b/model/model-intest/src/test/resources/password/security-policy-password-storage-none.xml @@ -0,0 +1,39 @@ + + + + + Security Policy: password storage none + + + + none + + + + + http://midpoint.evolveum.com/xml/ns/public/security/question-2#q001 + true + How much wood would a woodchuck chuck if woodchuck could chuck wood? + + + http://midpoint.evolveum.com/xml/ns/public/security/question-2#q002 + What is your mother's best friend's uncle's grandaughter's dog's mother maiden name? + + + + diff --git a/model/model-intest/testng-integration.xml b/model/model-intest/testng-integration.xml index e63125f9593..a7c97b2a520 100644 --- a/model/model-intest/testng-integration.xml +++ b/model/model-intest/testng-integration.xml @@ -34,7 +34,6 @@ - @@ -92,6 +91,12 @@ + + + + + + @@ -11839,6 +11861,13 @@ MidPoitn will only work with credential in the memory while it is needed to complete current operation. The credential will be discarded after the operation. + + THIS IS ONLY PARTIALLY SUPPORTED + + MidPoint should be able not to store the credentials when + this setting is used. But there may be side effects. + This is not entirelly tests and not supported. + Use at your own risk. diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java index 400419bee74..b44c51a093d 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/CredentialsProcessor.java @@ -40,6 +40,7 @@ import com.evolveum.midpoint.schema.constants.ExpressionConstants; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.security.api.SecurityUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.SchemaFailableProcessor; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; @@ -433,12 +434,8 @@ private void transformFocusExectionDeltaCredential(LensCo return; } CredentialPolicyType defaltCredPolicyType = credsType.getDefault(); - CredentialsStorageMethodType storageMethod = null; - if (credPolicyType != null && credPolicyType.getStorageMethod() != null) { - storageMethod = credPolicyType.getStorageMethod(); - } else if (defaltCredPolicyType != null && defaltCredPolicyType.getStorageMethod() != null) { - storageMethod = defaltCredPolicyType.getStorageMethod(); - } + CredentialsStorageMethodType storageMethod = + SecurityUtil.getCredPolicyItem(defaltCredPolicyType, credPolicyType, pol -> pol.getStorageMethod()); if (storageMethod == null) { return; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java index 5ce5c208083..79d2137035a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/projector/PasswordPolicyProcessor.java @@ -59,6 +59,7 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.security.api.SecurityUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; @@ -70,6 +71,8 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.CharacterClassType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CheckExpressionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsStorageMethodType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsStorageTypeType; import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; @@ -709,7 +712,6 @@ private String determinePasswordValue(ProtectedStringType passValue) { return passwordStr; } - public void processPasswordHistoryDeltas(LensFocusContext focusContext, LensContext context, SecurityPolicyType securityPolicy, XMLGregorianCalendar now, Task task, OperationResult result) throws SchemaException { @@ -729,7 +731,7 @@ public void processPasswordHistoryDeltas(LensFocusContext< List historyEntryValues = getSortedHistoryList(historyEntries, true); int newHistoryEntries = 0; if (maxPasswordsToSave > 0) { - newHistoryEntries = createAddHistoryDelta(context, password, now); + newHistoryEntries = createAddHistoryDelta(context, password, securityPolicy, now); } createDeleteHistoryDeltasIfNeeded(historyEntryValues, maxPasswordsToSave, newHistoryEntries, context, task, result); } @@ -794,9 +796,24 @@ private int getMaxPasswordsToSave(LensFocusContext focu private int createAddHistoryDelta(LensContext context, - PrismContainer password, XMLGregorianCalendar now) throws SchemaException { + PrismContainer password, SecurityPolicyType securityPolicy, XMLGregorianCalendar now) throws SchemaException { PrismContainerValue passwordValue = password.getValue(); - PasswordType passwordRealValue = passwordValue.asContainerable(); + PasswordType passwordType = passwordValue.asContainerable(); + if (passwordType == null || passwordType.getValue() == null) { + return 0; + } + ProtectedStringType passwordPsForStorage = passwordType.getValue().clone(); + + CredentialsStorageTypeType storageType = CredentialsStorageTypeType.HASHING; + CredentialsPolicyType creds = securityPolicy.getCredentials(); + if (creds != null) { + CredentialsStorageMethodType storageMethod = + SecurityUtil.getCredPolicyItem(creds.getDefault(), creds.getPassword(), pol -> pol.getStorageMethod()); + if (storageMethod != null && storageMethod.getStorageType() != null) { + storageType = storageMethod.getStorageType(); + } + } + prepareProtectedStringForStorage(passwordPsForStorage, storageType); PrismContainerDefinition historyEntryDefinition = password.getDefinition().findContainerDefinition(PasswordType.F_HISTORY_ENTRY); PrismContainer historyEntry = historyEntryDefinition.instantiate(); @@ -804,8 +821,8 @@ private int createAddHistoryDelta(LensContext context, PrismContainerValue hisotryEntryValue = historyEntry.createNewValue(); PasswordHistoryEntryType entryType = hisotryEntryValue.asContainerable(); - entryType.setValue(passwordRealValue.getValue()); - entryType.setMetadata(passwordRealValue.getMetadata()); + entryType.setValue(passwordPsForStorage); + entryType.setMetadata(passwordType.getMetadata()); entryType.setChangeTimestamp(now); ContainerDelta addHisotryDelta = ContainerDelta @@ -815,6 +832,37 @@ private int createAddHistoryDelta(LensContext context, return 1; } + + private void prepareProtectedStringForStorage(ProtectedStringType ps, CredentialsStorageTypeType storageType) throws SchemaException { + try { + switch (storageType) { + case ENCRYPTION: + if (ps.isEncrypted()) { + break; + } + if (ps.isHashed()) { + throw new SchemaException("Cannot store hashed value in an encrypted form"); + } + protector.encrypt(ps); + break; + + case HASHING: + if (ps.isHashed()) { + break; + } + protector.hash(ps); + break; + + case NONE: + throw new SchemaException("Cannot store value on NONE storage form"); + + default: + throw new SchemaException("Unknown storage type: "+storageType); + } + } catch (EncryptionException e) { + throw new SystemException(e.getMessage(), e); + } + } private void createDeleteHistoryDeltasIfNeeded( List historyEntryValues, int maxPasswordsToSave, int newHistoryEntries, LensContext context, Task task, diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java index da60d69e58c..1e5994d4b67 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/AbstractPasswordTest.java @@ -721,7 +721,10 @@ public void test202ReconcileUserJack() throws Exception { // GIVEN Task task = taskManager.createTaskInstance(AbstractPasswordTest.class.getName() + "." + TEST_NAME); OperationResult result = task.getResult(); - assumeAssignmentPolicy(AssignmentPolicyEnforcementType.FULL); + + PrismObject userBefore = getUser(USER_JACK_OID); + display("User before", userBefore); + assertLinks(userBefore, 4); // WHEN reconcileUser(USER_JACK_OID, task, result); @@ -730,27 +733,27 @@ public void test202ReconcileUserJack() throws Exception { result.computeStatus(); TestUtil.assertSuccess(result); - PrismObject userJack = getUser(USER_JACK_OID); - display("User after change execution", userJack); - assertLinks(userJack, 4); - accountYellowOid = getLinkRefOid(userJack, RESOURCE_DUMMY_YELLOW_OID); + PrismObject userAfter = getUser(USER_JACK_OID); + display("User after", userAfter); + assertLinks(userAfter, 4); + accountYellowOid = getLinkRefOid(userAfter, RESOURCE_DUMMY_YELLOW_OID); // Check account in dummy resource (yellow): password is too short for this, original password should remain there assertDummyAccount(RESOURCE_DUMMY_YELLOW_NAME, ACCOUNT_JACK_DUMMY_USERNAME, ACCOUNT_JACK_DUMMY_FULLNAME, true); - assertDummyPassword(RESOURCE_DUMMY_YELLOW_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_1_CLEAR); + assertDummyPasswordConditional(RESOURCE_DUMMY_YELLOW_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_1_CLEAR); // Check account in dummy resource (red) assertDummyAccount(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME, ACCOUNT_JACK_DUMMY_FULLNAME, true); assertDummyPassword(RESOURCE_DUMMY_RED_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_A_CLEAR); // User and default dummy account should have unchanged passwords - assertUserPassword(userJack, USER_PASSWORD_A_CLEAR); + assertUserPassword(userAfter, USER_PASSWORD_A_CLEAR); assertDummyPassword(ACCOUNT_JACK_DUMMY_USERNAME, USER_PASSWORD_A_CLEAR); // this one is not changed assertDummyPassword(RESOURCE_DUMMY_UGLY_NAME, ACCOUNT_JACK_DUMMY_USERNAME, USER_JACK_EMPLOYEE_NUMBER_NEW_GOOD); - assertPasswordHistoryEntries(userJack); + assertPasswordHistoryEntries(userAfter); } /** diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java index 79af8155586..e1d7993f8b8 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordDefaultHashing.java @@ -49,6 +49,6 @@ protected String getSecurityPolicyOid() { @Override protected CredentialsStorageTypeType getPasswordStorageType() { return CredentialsStorageTypeType.HASHING; - } + } } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordNone.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordNone.java index 9263924396a..0173cf133ba 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordNone.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/password/TestPasswordNone.java @@ -29,6 +29,12 @@ /** * Password test with NONE password storage (default storage for other types) * + * This test is only partially working. + * IT IS NOT PART OF THE TEST SUITE. It is NOT executed automatically. + * + * E.g. new password will be generated on every recompute because the + * weak inbound mapping is activated. + * * @author semancik * */ @@ -50,5 +56,4 @@ protected String getSecurityPolicyOid() { protected CredentialsStorageTypeType getPasswordStorageType() { return CredentialsStorageTypeType.NONE; } - } diff --git a/model/model-intest/src/test/resources/logback-test.xml b/model/model-intest/src/test/resources/logback-test.xml index 6e74a791037..13228f205c1 100644 --- a/model/model-intest/src/test/resources/logback-test.xml +++ b/model/model-intest/src/test/resources/logback-test.xml @@ -46,7 +46,7 @@ if any of the following is set to "TRACE" then it was changed by mistake and should be changed back --> - + diff --git a/model/model-intest/testng-integration.xml b/model/model-intest/testng-integration.xml index f733ac50372..f4545204b2c 100644 --- a/model/model-intest/testng-integration.xml +++ b/model/model-intest/testng-integration.xml @@ -95,7 +95,8 @@ - + +