Skip to content

Commit 31d7414

Browse files
committed
creates AbstractEnvironmentDecrypt to handle common decrypt functions
1 parent 7584d0a commit 31d7414

File tree

3 files changed

+169
-267
lines changed

3 files changed

+169
-267
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*
2+
* Copyright 2013-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.bootstrap.encrypt;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collections;
21+
import java.util.LinkedHashMap;
22+
import java.util.List;
23+
import java.util.Map;
24+
import java.util.regex.Pattern;
25+
26+
import org.apache.commons.logging.Log;
27+
import org.apache.commons.logging.LogFactory;
28+
29+
import org.springframework.core.env.CompositePropertySource;
30+
import org.springframework.core.env.EnumerablePropertySource;
31+
import org.springframework.core.env.PropertySource;
32+
import org.springframework.core.env.PropertySources;
33+
import org.springframework.security.crypto.encrypt.TextEncryptor;
34+
35+
/**
36+
* Abstract class that handles decrypting and merging of PropertySources.
37+
*/
38+
public class AbstractEnvironmentDecrypt {
39+
40+
private static final Pattern COLLECTION_PROPERTY = Pattern.compile("(\\S+)?\\[(\\d+)\\](\\.\\S+)?");
41+
42+
/**
43+
* Name of the decrypted property source.
44+
*/
45+
public static final String DECRYPTED_PROPERTY_SOURCE_NAME = "decrypted";
46+
47+
/**
48+
* Prefix indicating an encrypted value.
49+
*/
50+
public static final String ENCRYPTED_PROPERTY_PREFIX = "{cipher}";
51+
52+
protected Log logger = LogFactory.getLog(getClass());
53+
54+
private boolean failOnError = true;
55+
56+
/**
57+
* Strategy to determine how to handle exceptions during decryption.
58+
* @param failOnError the flag value (default true)
59+
*/
60+
public void setFailOnError(boolean failOnError) {
61+
this.failOnError = failOnError;
62+
}
63+
64+
public boolean isFailOnError() {
65+
return this.failOnError;
66+
}
67+
68+
protected Map<String, Object> decrypt(TextEncryptor encryptor, PropertySources propertySources) {
69+
Map<String, Object> properties = merge(propertySources);
70+
decrypt(encryptor, properties);
71+
return properties;
72+
}
73+
74+
protected Map<String, Object> merge(PropertySources propertySources) {
75+
Map<String, Object> properties = new LinkedHashMap<>();
76+
List<PropertySource<?>> sources = new ArrayList<>();
77+
for (PropertySource<?> source : propertySources) {
78+
sources.add(0, source);
79+
}
80+
for (PropertySource<?> source : sources) {
81+
merge(source, properties);
82+
}
83+
return properties;
84+
}
85+
86+
protected void merge(PropertySource<?> source, Map<String, Object> properties) {
87+
if (source instanceof CompositePropertySource) {
88+
89+
List<PropertySource<?>> sources = new ArrayList<>(((CompositePropertySource) source).getPropertySources());
90+
Collections.reverse(sources);
91+
92+
for (PropertySource<?> nested : sources) {
93+
merge(nested, properties);
94+
}
95+
96+
}
97+
else if (source instanceof EnumerablePropertySource) {
98+
Map<String, Object> otherCollectionProperties = new LinkedHashMap<>();
99+
boolean sourceHasDecryptedCollection = false;
100+
101+
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
102+
for (String key : enumerable.getPropertyNames()) {
103+
Object property = source.getProperty(key);
104+
if (property != null) {
105+
String value = property.toString();
106+
if (value.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
107+
properties.put(key, value);
108+
if (COLLECTION_PROPERTY.matcher(key).matches()) {
109+
sourceHasDecryptedCollection = true;
110+
}
111+
}
112+
else if (COLLECTION_PROPERTY.matcher(key).matches()) {
113+
// put non-encrypted properties so merging of index properties
114+
// happens correctly
115+
otherCollectionProperties.put(key, value);
116+
}
117+
else {
118+
// override previously encrypted with non-encrypted property
119+
properties.remove(key);
120+
}
121+
}
122+
}
123+
// copy all indexed properties even if not encrypted
124+
if (sourceHasDecryptedCollection && !otherCollectionProperties.isEmpty()) {
125+
properties.putAll(otherCollectionProperties);
126+
}
127+
128+
}
129+
}
130+
131+
protected void decrypt(TextEncryptor encryptor, Map<String, Object> properties) {
132+
properties.replaceAll((key, value) -> {
133+
String valueString = value.toString();
134+
if (!valueString.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
135+
return value;
136+
}
137+
return decrypt(encryptor, key, valueString);
138+
});
139+
}
140+
141+
protected String decrypt(TextEncryptor encryptor, String key, String original) {
142+
String value = original.substring(ENCRYPTED_PROPERTY_PREFIX.length());
143+
try {
144+
value = encryptor.decrypt(value);
145+
if (logger.isDebugEnabled()) {
146+
logger.debug("Decrypted: key=" + key);
147+
}
148+
return value;
149+
}
150+
catch (Exception e) {
151+
String message = "Cannot decrypt: key=" + key;
152+
if (logger.isDebugEnabled()) {
153+
logger.warn(message, e);
154+
}
155+
else {
156+
logger.warn(message);
157+
}
158+
if (this.failOnError) {
159+
throw new IllegalStateException(message, e);
160+
}
161+
return "";
162+
}
163+
}
164+
165+
}

spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/DecryptEnvironmentPostProcessor.java

Lines changed: 1 addition & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@
1616

1717
package org.springframework.cloud.bootstrap.encrypt;
1818

19-
import java.util.ArrayList;
20-
import java.util.Collections;
21-
import java.util.LinkedHashMap;
22-
import java.util.List;
2319
import java.util.Map;
24-
import java.util.regex.Pattern;
25-
26-
import org.apache.commons.logging.Log;
27-
import org.apache.commons.logging.LogFactory;
2820

2921
import org.springframework.boot.SpringApplication;
3022
import org.springframework.boot.context.properties.bind.Binder;
@@ -33,12 +25,8 @@
3325
import org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper.FailsafeTextEncryptor;
3426
import org.springframework.cloud.context.encrypt.EncryptorFactory;
3527
import org.springframework.core.Ordered;
36-
import org.springframework.core.env.CompositePropertySource;
3728
import org.springframework.core.env.ConfigurableEnvironment;
38-
import org.springframework.core.env.EnumerablePropertySource;
3929
import org.springframework.core.env.MutablePropertySources;
40-
import org.springframework.core.env.PropertySource;
41-
import org.springframework.core.env.PropertySources;
4230
import org.springframework.core.env.SystemEnvironmentPropertySource;
4331
import org.springframework.security.crypto.encrypt.TextEncryptor;
4432
import org.springframework.util.ClassUtils;
@@ -54,34 +42,10 @@
5442
* @author Dave Syer
5543
* @author Tim Ysewyn
5644
*/
57-
public class DecryptEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
58-
59-
/**
60-
* Name of the decrypted property source.
61-
*/
62-
public static final String DECRYPTED_PROPERTY_SOURCE_NAME = "decrypted";
63-
64-
/**
65-
* Prefix indicating an encrypted value.
66-
*/
67-
public static final String ENCRYPTED_PROPERTY_PREFIX = "{cipher}";
68-
69-
private static final Pattern COLLECTION_PROPERTY = Pattern.compile("(\\S+)?\\[(\\d+)\\](\\.\\S+)?");
70-
71-
private static Log logger = LogFactory.getLog(DecryptEnvironmentPostProcessor.class);
45+
public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt implements EnvironmentPostProcessor, Ordered {
7246

7347
private int order = Ordered.LOWEST_PRECEDENCE;
7448

75-
private boolean failOnError = true;
76-
77-
/**
78-
* Strategy to determine how to handle exceptions during decryption.
79-
* @param failOnError the flag value (default true)
80-
*/
81-
public void setFailOnError(boolean failOnError) {
82-
this.failOnError = failOnError;
83-
}
84-
8549
@Override
8650
public int getOrder() {
8751
return this.order;
@@ -128,101 +92,5 @@ protected TextEncryptor getTextEncryptor(ConfigurableEnvironment environment) {
12892
return new FailsafeTextEncryptor();
12993
}
13094

131-
public Map<String, Object> decrypt(TextEncryptor encryptor, PropertySources propertySources) {
132-
Map<String, Object> properties = merge(propertySources);
133-
decrypt(encryptor, properties);
134-
return properties;
135-
}
136-
137-
private Map<String, Object> merge(PropertySources propertySources) {
138-
Map<String, Object> properties = new LinkedHashMap<>();
139-
List<PropertySource<?>> sources = new ArrayList<>();
140-
for (PropertySource<?> source : propertySources) {
141-
sources.add(0, source);
142-
}
143-
for (PropertySource<?> source : sources) {
144-
merge(source, properties);
145-
}
146-
return properties;
147-
}
148-
149-
private void merge(PropertySource<?> source, Map<String, Object> properties) {
150-
if (source instanceof CompositePropertySource) {
151-
152-
List<PropertySource<?>> sources = new ArrayList<>(((CompositePropertySource) source).getPropertySources());
153-
Collections.reverse(sources);
154-
155-
for (PropertySource<?> nested : sources) {
156-
merge(nested, properties);
157-
}
158-
159-
}
160-
else if (source instanceof EnumerablePropertySource) {
161-
Map<String, Object> otherCollectionProperties = new LinkedHashMap<>();
162-
boolean sourceHasDecryptedCollection = false;
163-
164-
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
165-
for (String key : enumerable.getPropertyNames()) {
166-
Object property = source.getProperty(key);
167-
if (property != null) {
168-
String value = property.toString();
169-
if (value.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
170-
properties.put(key, value);
171-
if (COLLECTION_PROPERTY.matcher(key).matches()) {
172-
sourceHasDecryptedCollection = true;
173-
}
174-
}
175-
else if (COLLECTION_PROPERTY.matcher(key).matches()) {
176-
// put non-encrypted properties so merging of index properties
177-
// happens correctly
178-
otherCollectionProperties.put(key, value);
179-
}
180-
else {
181-
// override previously encrypted with non-encrypted property
182-
properties.remove(key);
183-
}
184-
}
185-
}
186-
// copy all indexed properties even if not encrypted
187-
if (sourceHasDecryptedCollection && !otherCollectionProperties.isEmpty()) {
188-
properties.putAll(otherCollectionProperties);
189-
}
190-
191-
}
192-
}
193-
194-
private void decrypt(TextEncryptor encryptor, Map<String, Object> properties) {
195-
properties.replaceAll((key, value) -> {
196-
String valueString = value.toString();
197-
if (!valueString.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
198-
return value;
199-
}
200-
return decrypt(encryptor, key, valueString);
201-
});
202-
}
203-
204-
private String decrypt(TextEncryptor encryptor, String key, String original) {
205-
String value = original.substring(ENCRYPTED_PROPERTY_PREFIX.length());
206-
try {
207-
value = encryptor.decrypt(value);
208-
if (logger.isDebugEnabled()) {
209-
logger.debug("Decrypted: key=" + key);
210-
}
211-
return value;
212-
}
213-
catch (Exception e) {
214-
String message = "Cannot decrypt: key=" + key;
215-
if (logger.isDebugEnabled()) {
216-
logger.warn(message, e);
217-
}
218-
else {
219-
logger.warn(message);
220-
}
221-
if (this.failOnError) {
222-
throw new IllegalStateException(message, e);
223-
}
224-
return "";
225-
}
226-
}
22795

22896
}

0 commit comments

Comments
 (0)