1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.persistence.jpa.entity;
20
21 import java.time.OffsetDateTime;
22 import java.util.Base64;
23 import java.util.Optional;
24 import java.util.regex.Pattern;
25 import javax.persistence.Lob;
26 import javax.persistence.MappedSuperclass;
27 import org.apache.commons.lang3.ArrayUtils;
28 import org.apache.commons.lang3.StringUtils;
29 import org.apache.commons.lang3.builder.ToStringBuilder;
30 import org.apache.syncope.common.lib.SyncopeConstants;
31 import org.apache.syncope.common.lib.types.AttrSchemaType;
32 import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
33 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
34 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
35 import org.apache.syncope.core.persistence.jpa.validation.entity.PlainAttrValueCheck;
36 import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
37 import org.apache.syncope.core.spring.ApplicationContextProvider;
38 import org.apache.syncope.core.spring.security.Encryptor;
39
40 @MappedSuperclass
41 @PlainAttrValueCheck
42 public abstract class AbstractPlainAttrValue extends AbstractGeneratedKeyEntity implements PlainAttrValue {
43
44 private static final long serialVersionUID = -9141923816611244785L;
45
46 private static final Pattern SPRING_ENV_PROPERTY = Pattern.compile("^\\$\\{.*\\}$");
47
48 private String stringValue;
49
50 private OffsetDateTime dateValue;
51
52 private Boolean booleanValue;
53
54 private Long longValue;
55
56 private Double doubleValue;
57
58 @Lob
59 private byte[] binaryValue;
60
61 @Override
62 public Boolean getBooleanValue() {
63 return booleanValue;
64 }
65
66 @Override
67 public void setBooleanValue(final Boolean booleanValue) {
68 this.booleanValue = booleanValue;
69 }
70
71 @Override
72 public OffsetDateTime getDateValue() {
73 return dateValue;
74 }
75
76 @Override
77 public void setDateValue(final OffsetDateTime dateValue) {
78 this.dateValue = dateValue;
79 }
80
81 @Override
82 public Double getDoubleValue() {
83 return doubleValue;
84 }
85
86 @Override
87 public void setDoubleValue(final Double doubleValue) {
88 this.doubleValue = doubleValue;
89 }
90
91 @Override
92 public Long getLongValue() {
93 return longValue;
94 }
95
96 @Override
97 public void setLongValue(final Long longValue) {
98 this.longValue = longValue;
99 }
100
101 @Override
102 public String getStringValue() {
103
104 return dateValue == null
105 && booleanValue == null
106 && longValue == null
107 && doubleValue == null
108 && binaryValue == null
109 && stringValue == null
110 ? StringUtils.EMPTY
111 : stringValue;
112 }
113
114 @Override
115 public void setStringValue(final String stringValue) {
116 this.stringValue = stringValue;
117 }
118
119 @Override
120 public byte[] getBinaryValue() {
121 return binaryValue;
122 }
123
124 @Override
125 public void setBinaryValue(final byte[] binaryValue) {
126 this.binaryValue = ArrayUtils.clone(binaryValue);
127 }
128
129 protected String getSecretKey(final PlainSchema schema) {
130 return SPRING_ENV_PROPERTY.matcher(schema.getSecretKey()).matches()
131 ? ApplicationContextProvider.getApplicationContext().getEnvironment().
132 getProperty(StringUtils.substringBetween(schema.getSecretKey(), "${", "}"))
133 : schema.getSecretKey();
134 }
135
136 @Override
137 public void parseValue(final PlainSchema schema, final String value) {
138 Exception exception = null;
139
140 switch (schema.getType()) {
141
142 case Boolean:
143 this.setBooleanValue(Boolean.valueOf(value));
144 break;
145
146 case Long:
147 try {
148 this.setLongValue(schema.getConversionPattern() == null
149 ? Long.valueOf(value)
150 : FormatUtils.parseNumber(value, schema.getConversionPattern()).longValue());
151 } catch (Exception pe) {
152 exception = pe;
153 }
154 break;
155
156 case Double:
157 try {
158 this.setDoubleValue(schema.getConversionPattern() == null
159 ? Double.valueOf(value)
160 : FormatUtils.parseNumber(value, schema.getConversionPattern()).doubleValue());
161 } catch (Exception pe) {
162 exception = pe;
163 }
164 break;
165
166 case Date:
167 try {
168 this.setDateValue(schema.getConversionPattern() == null
169 ? FormatUtils.parseDate(value)
170 : FormatUtils.parseDate(value, schema.getConversionPattern()));
171 } catch (Exception pe) {
172 exception = pe;
173 }
174 break;
175
176 case Encrypted:
177 try {
178 this.setStringValue(Encryptor.getInstance(getSecretKey(schema)).
179 encode(value, schema.getCipherAlgorithm()));
180 } catch (Exception pe) {
181 exception = pe;
182 }
183 break;
184
185 case Binary:
186 this.setBinaryValue(Base64.getDecoder().decode(value));
187 break;
188
189 case String:
190 case Enum:
191 default:
192 this.setStringValue(value);
193 }
194
195 if (exception != null) {
196 throw new ParsingValidationException(
197 "While trying to parse '" + value + "' as " + schema.getKey(), exception);
198 }
199 }
200
201 @SuppressWarnings("unchecked")
202 @Override
203 public <T> T getValue() {
204 return (T) (booleanValue != null
205 ? getBooleanValue()
206 : dateValue != null
207 ? getDateValue()
208 : doubleValue != null
209 ? getDoubleValue()
210 : longValue != null
211 ? getLongValue()
212 : binaryValue != null
213 ? getBinaryValue()
214 : getStringValue());
215 }
216
217 private Object getValue(final AttrSchemaType type) {
218 Object value;
219 switch (type) {
220
221 case Boolean:
222 value = getBooleanValue();
223 break;
224
225 case Long:
226 value = getLongValue();
227 break;
228
229 case Double:
230 value = getDoubleValue();
231 break;
232
233 case Date:
234 value = getDateValue();
235 break;
236
237 case Binary:
238 value = getBinaryValue();
239 break;
240
241 case String:
242 case Enum:
243 case Encrypted:
244 value = getStringValue();
245 break;
246
247 default:
248 value = null;
249 }
250
251 return value;
252 }
253
254 private String getValueAsString(final AttrSchemaType type, final PlainSchema schema) {
255 if (getValue(type) == null) {
256 LOG.warn("Could not find expected value for type {} in {}, reverting to getValue().toString()", type, this);
257
258 Object value = getValue();
259 return Optional.ofNullable(value).map(Object::toString).orElse(null);
260 }
261
262 String result;
263 switch (type) {
264
265 case Boolean:
266 result = getBooleanValue().toString();
267 break;
268
269 case Long:
270 result = schema == null || schema.getConversionPattern() == null
271 ? getLongValue().toString()
272 : FormatUtils.format(getLongValue(), schema.getConversionPattern());
273 break;
274
275 case Double:
276 result = schema == null || schema.getConversionPattern() == null
277 ? getDoubleValue().toString()
278 : FormatUtils.format(getDoubleValue(), schema.getConversionPattern());
279 break;
280
281 case Date:
282 result = schema == null || schema.getConversionPattern() == null
283 ? FormatUtils.format(getDateValue())
284 : FormatUtils.format(getDateValue(), schema.getConversionPattern());
285 break;
286
287 case Binary:
288 result = Base64.getEncoder().encodeToString(getBinaryValue());
289 break;
290
291 case Encrypted:
292 if (schema == null
293 || !SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(schema.getConversionPattern())
294 || !schema.getCipherAlgorithm().isInvertible()) {
295
296 result = getStringValue();
297 } else {
298 try {
299 result = Encryptor.getInstance(getSecretKey(schema)).
300 decode(getStringValue(), schema.getCipherAlgorithm());
301 } catch (Exception e) {
302 LOG.error("Could not decode encrypted value {} for schema {}", getStringValue(), schema, e);
303 result = getStringValue();
304 }
305 }
306 break;
307
308 case String:
309 case Enum:
310 default:
311 result = getStringValue();
312 }
313
314 return result;
315 }
316
317 @Override
318 public String getValueAsString() {
319 PlainSchema schema = getAttr() == null || getAttr().getSchema() == null
320 ? null
321 : getAttr().getSchema();
322 AttrSchemaType type = schema == null || schema.getType() == null
323 ? AttrSchemaType.String
324 : getAttr().getSchema().getType();
325
326 return getValueAsString(type, schema);
327 }
328
329 @Override
330 public String getValueAsString(final AttrSchemaType type) {
331 return getValueAsString(
332 type,
333 getAttr() == null || getAttr().getSchema() == null ? null : getAttr().getSchema());
334 }
335
336 @Override
337 public String getValueAsString(final PlainSchema schema) {
338 return getValueAsString(schema.getType(), schema);
339 }
340
341 @Override
342 public String toString() {
343 return new ToStringBuilder(this).
344 append(getKey()).
345 append(stringValue).
346 append(dateValue).
347 append(booleanValue).
348 append(longValue).
349 append(doubleValue).
350 append(binaryValue).
351 build();
352 }
353 }