Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[vividus] Introduce decrypt expression #4610

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions docs/modules/ROOT/pages/secrets-management.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

== Encryption

It is allowed to store values of the properties in the encrypted form.
It is allowed to store property values and input test data in the encrypted form.
VIVIDUS uses Jasypt (Java Simplified Encryption) which provides two-way
encryption mechanism. While performing two-way encryption, apart from feeding
plain-text it's required to feed the secret text (i.e. password) and this secret
Expand Down Expand Up @@ -101,12 +101,22 @@ IMPORTANT: This password must be kept secret and must not be committed to versio
+
NOTE: `paSSw0rd` is a sample password and it should be never used, own strong password is required for the encryption.

. Use the case-sensitive wrapping `ENC(...)` for any encrypted property value. e.g.
. Use the encrypted value in the following ways:
+
.. Use the case-sensitive wrapping `ENC(...)` for any encrypted property value, e.g.:
+
[source,properties]
----
http.auth.password=ENC(7TQwyzsmuGSQ1GlUlPZVcrzY8BovTHWn1OuVaJ2PTdvOd3md25Y8szfXoHAq94Pw)
----
+
.. Decrypt the value with help of xref:commons:expressions.adoc#_decrypt[`decrypt` expression] and use the result in
test scenario, e.g.:
+
[source,gherkin]
----
When I enter `#{decrypt(ENC(7TQwyzsmuGSQ1GlUlPZVcrzY8BovTHWn1OuVaJ2PTdvOd3md25Y8szfXoHAq94Pw))}` in field located by `id(password)`
----

Please see xref:tests-configuration.adoc#_externalized_configuration[Externalized Configuration section] to get more
information on how encryptor password can be passed to the tests.
Expand Down
25 changes: 25 additions & 0 deletions docs/modules/commons/pages/expressions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,31 @@ Calculates the {resource-info}[resource] or file hash using the specified hashin
|`0a05ba6064ae7e5d6ee9818f85b666ad`
|===


== Decryption

=== `decrypt`

Decrypts an input string using the same algrotihm and password employed for
the xref:ROOT:secrets-management.adoc#_how_to_use_an_encrypted_string[decryption] of properties.

[source, subs="+quotes"]
----
#{decrypt(*$encryptedInput*)}
----

* *`$encryptedInput`* - any string to be decrypted.

.Examples of the expression doing decryption with encryption password equal to `12345`
[cols="2a,1a"]
|===
|Expression |Result

|`#{decrypt(7K9wRym/gaD8mFSqZ6KALLnrE6vPhsBaxKIcN9g4d9w=)}`
|`The Show Must Go On`
|===


== Resources
:resource-name-argument: pass:quotes[*`$resourceName`* - the name of the xref:ROOT:glossary.adoc#_resource[resource] to load]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ Then `${partial-encrypted-variable}` is equal to `required username="my-username

Scenario: Verify decrypted value is loaded from system variables
Then `${system-property}` is equal to `Encrypted message`

Scenario: Verify `decrypt` expression
Meta:
@requirementId 4596
Then `#{decrypt(7K9wRym/gaD8mFSqZ6KALLnrE6vPhsBaxKIcN9g4d9w=)}` is equal to `The Show Must Go On`
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,35 @@

package org.vividus.configuration;

import java.util.Optional;
import java.util.Properties;

import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
import org.vividus.encryption.DecryptionFailedException;
import org.vividus.encryption.Decryptor;
import org.vividus.encryption.NoDecryptionPasswordException;

public class EncryptedPropertiesProcessor extends AbstractPropertiesProcessor
{
private static final String ENC = "ENC";
private Properties properties;
private StringEncryptor stringEncryptor;

EncryptedPropertiesProcessor(StringEncryptor stringEncryptor)
{
super(ENC);
this.stringEncryptor = stringEncryptor;
}
private final Decryptor decryptor;

EncryptedPropertiesProcessor(Properties properties)
{
super(ENC);
this.properties = properties;
this.decryptor = new Decryptor(properties);
}

@Override
protected String processValue(String propertyName, String partOfPropertyValueToProcess)
{
try
{
return getStringEncryptor().decrypt(partOfPropertyValueToProcess);
return decryptor.decrypt(partOfPropertyValueToProcess);
}
catch (NoDecryptionPasswordException e)
{
throw new DecryptionFailedException(
"Encrypted properties are found, but no password for decryption is provided", e);
}
catch (EncryptionOperationNotPossibleException e)
{
Expand All @@ -55,23 +53,4 @@ protected String processValue(String propertyName, String partOfPropertyValueToP
throw new DecryptionFailedException(errorMessage, e);
}
}

private StringEncryptor getStringEncryptor()
{
if (stringEncryptor == null)
{
String password = Optional.ofNullable(System.getenv("VIVIDUS_ENCRYPTOR_PASSWORD"))
.or(() -> Optional.ofNullable(System.getProperty("vividus.encryptor.password")))
.or(() -> Optional.ofNullable(properties.getProperty("system.vividus.encryptor.password")))
.orElseThrow(() -> new IllegalStateException(
"Encrypted properties are found, but no password for decryption is provided"));

StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setAlgorithm("PBEWithMD5AndDES");
encryptor.setPassword(password);

stringEncryptor = encryptor;
}
return stringEncryptor;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 the original author or authors.
* Copyright 2019-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,11 +14,11 @@
* limitations under the License.
*/

package org.vividus.configuration;
package org.vividus.encryption;

public class DecryptionFailedException extends RuntimeException
{
private static final long serialVersionUID = -3712209243074523884L;

Check warning on line 21 in vividus/src/main/java/org/vividus/encryption/DecryptionFailedException.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

'@Serial' annotation could be used

`serialVersionUID` can be annotated with '@serial' annotation

public DecryptionFailedException(String message, Throwable cause)
{
Expand Down
66 changes: 66 additions & 0 deletions vividus/src/main/java/org/vividus/encryption/Decryptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.vividus.encryption;

import java.util.Optional;
import java.util.Properties;

import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.apache.commons.lang3.concurrent.LazyInitializer;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;

public class Decryptor
{
private final LazyInitializer<StringEncryptor> encryptorProvider;

public Decryptor(Properties properties)
{
this.encryptorProvider = LazyInitializer.<StringEncryptor>builder().setInitializer(() -> {
String password = Optional.ofNullable(System.getenv("VIVIDUS_ENCRYPTOR_PASSWORD"))
.or(() -> Optional.ofNullable(System.getProperty("vividus.encryptor.password")))
.or(() -> Optional.ofNullable(properties.getProperty("system.vividus.encryptor.password")))
.orElseThrow(() -> new NoDecryptionPasswordException("No password for decryption is provided"));

StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setAlgorithm("PBEWithMD5AndDES");
encryptor.setPassword(password);
return encryptor;
}).get();
}

/**
* Decrypts an encrypted string.
*
* @param encrypted the encrypted string to be decrypted
* @return the result of decryption
* @throws NoDecryptionPasswordException if no password for decryption is provided
* @throws EncryptionOperationNotPossibleException if decryption is failed
*/
public String decrypt(String encrypted)
{
try
{
return encryptorProvider.get().decrypt(encrypted);
}
catch (ConcurrentException e)

Check warning on line 61 in vividus/src/main/java/org/vividus/encryption/Decryptor.java

View check run for this annotation

Codecov / codecov/patch

vividus/src/main/java/org/vividus/encryption/Decryptor.java#L61

Added line #L61 was not covered by tests
{
throw new IllegalStateException(e);

Check warning on line 63 in vividus/src/main/java/org/vividus/encryption/Decryptor.java

View check run for this annotation

Codecov / codecov/patch

vividus/src/main/java/org/vividus/encryption/Decryptor.java#L63

Added line #L63 was not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.vividus.encryption;

public class NoDecryptionPasswordException extends RuntimeException
{
private static final long serialVersionUID = 8336981206596501721L;

Check warning on line 21 in vividus/src/main/java/org/vividus/encryption/NoDecryptionPasswordException.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

'@Serial' annotation could be used

`serialVersionUID` can be annotated with '@serial' annotation

public NoDecryptionPasswordException(String message)
{
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2019-2023 the original author or authors.
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.vividus.expression;

import org.jbehave.core.expressions.SingleArgExpressionProcessor;
import org.vividus.encryption.Decryptor;

public class DecryptExpressionProcessor extends SingleArgExpressionProcessor<String>
{
public DecryptExpressionProcessor(Decryptor decryptor)
{
super("decrypt", decryptor::decrypt);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@
<property name="eventBus" ref="eventBus" />
</bean>

<bean class="org.vividus.encryption.Decryptor">
<constructor-arg index="0" ref="properties" />
</bean>

</beans>
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
<bean id="SORTING" class="org.vividus.transformer.SortingTableTransformer" />

<bean class="org.vividus.expression.Base64ExpressionProcessors" />
<bean class="org.vividus.expression.DecryptExpressionProcessor" />
<bean class="org.vividus.expression.EvalExpressionProcessor" />
<bean class="org.vividus.expression.GroovyExpressionProcessor" />
<bean class="org.vividus.expression.HashExpressionProcessors" />
Expand Down
Loading
Loading