| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.syncope.core.persistence.jpa.entity; |
| |
| import java.time.OffsetDateTime; |
| import java.util.Base64; |
| import java.util.Optional; |
| import java.util.regex.Pattern; |
| import javax.persistence.Lob; |
| import javax.persistence.MappedSuperclass; |
| import org.apache.commons.lang3.ArrayUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.lang3.builder.ToStringBuilder; |
| import org.apache.syncope.common.lib.SyncopeConstants; |
| import org.apache.syncope.common.lib.types.AttrSchemaType; |
| import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException; |
| import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; |
| import org.apache.syncope.core.persistence.api.entity.PlainSchema; |
| import org.apache.syncope.core.persistence.jpa.validation.entity.PlainAttrValueCheck; |
| import org.apache.syncope.core.provisioning.api.utils.FormatUtils; |
| import org.apache.syncope.core.spring.ApplicationContextProvider; |
| import org.apache.syncope.core.spring.security.Encryptor; |
| |
| @MappedSuperclass |
| @PlainAttrValueCheck |
| public abstract class AbstractPlainAttrValue extends AbstractGeneratedKeyEntity implements PlainAttrValue { |
| |
| private static final long serialVersionUID = -9141923816611244785L; |
| |
| private static final Pattern SPRING_ENV_PROPERTY = Pattern.compile("^\\$\\{.*\\}$"); |
| |
| private String stringValue; |
| |
| private OffsetDateTime dateValue; |
| |
| private Boolean booleanValue; |
| |
| private Long longValue; |
| |
| private Double doubleValue; |
| |
| @Lob |
| private byte[] binaryValue; |
| |
| @Override |
| public Boolean getBooleanValue() { |
| return booleanValue; |
| } |
| |
| @Override |
| public void setBooleanValue(final Boolean booleanValue) { |
| this.booleanValue = booleanValue; |
| } |
| |
| @Override |
| public OffsetDateTime getDateValue() { |
| return dateValue; |
| } |
| |
| @Override |
| public void setDateValue(final OffsetDateTime dateValue) { |
| this.dateValue = dateValue; |
| } |
| |
| @Override |
| public Double getDoubleValue() { |
| return doubleValue; |
| } |
| |
| @Override |
| public void setDoubleValue(final Double doubleValue) { |
| this.doubleValue = doubleValue; |
| } |
| |
| @Override |
| public Long getLongValue() { |
| return longValue; |
| } |
| |
| @Override |
| public void setLongValue(final Long longValue) { |
| this.longValue = longValue; |
| } |
| |
| @Override |
| public String getStringValue() { |
| // workaround for Oracle DB considering empty string values as NULL (SYNCOPE-664) |
| return dateValue == null |
| && booleanValue == null |
| && longValue == null |
| && doubleValue == null |
| && binaryValue == null |
| && stringValue == null |
| ? StringUtils.EMPTY |
| : stringValue; |
| } |
| |
| @Override |
| public void setStringValue(final String stringValue) { |
| this.stringValue = stringValue; |
| } |
| |
| @Override |
| public byte[] getBinaryValue() { |
| return binaryValue; |
| } |
| |
| @Override |
| public void setBinaryValue(final byte[] binaryValue) { |
| this.binaryValue = ArrayUtils.clone(binaryValue); |
| } |
| |
| protected String getSecretKey(final PlainSchema schema) { |
| return SPRING_ENV_PROPERTY.matcher(schema.getSecretKey()).matches() |
| ? ApplicationContextProvider.getApplicationContext().getEnvironment(). |
| getProperty(StringUtils.substringBetween(schema.getSecretKey(), "${", "}")) |
| : schema.getSecretKey(); |
| } |
| |
| @Override |
| public void parseValue(final PlainSchema schema, final String value) { |
| Exception exception = null; |
| |
| switch (schema.getType()) { |
| |
| case Boolean: |
| this.setBooleanValue(Boolean.parseBoolean(value)); |
| break; |
| |
| case Long: |
| try { |
| this.setLongValue(schema.getConversionPattern() == null |
| ? Long.valueOf(value) |
| : FormatUtils.parseNumber(value, schema.getConversionPattern()).longValue()); |
| } catch (Exception pe) { |
| exception = pe; |
| } |
| break; |
| |
| case Double: |
| try { |
| this.setDoubleValue(schema.getConversionPattern() == null |
| ? Double.valueOf(value) |
| : FormatUtils.parseNumber(value, schema.getConversionPattern()).doubleValue()); |
| } catch (Exception pe) { |
| exception = pe; |
| } |
| break; |
| |
| case Date: |
| try { |
| this.setDateValue(schema.getConversionPattern() == null |
| ? FormatUtils.parseDate(value) |
| : FormatUtils.parseDate(value, schema.getConversionPattern())); |
| } catch (Exception pe) { |
| exception = pe; |
| } |
| break; |
| |
| case Encrypted: |
| try { |
| this.setStringValue(Encryptor.getInstance(getSecretKey(schema)). |
| encode(value, schema.getCipherAlgorithm())); |
| } catch (Exception pe) { |
| exception = pe; |
| } |
| break; |
| |
| case Binary: |
| this.setBinaryValue(Base64.getDecoder().decode(value)); |
| break; |
| |
| case String: |
| case Enum: |
| default: |
| this.setStringValue(value); |
| } |
| |
| if (exception != null) { |
| throw new ParsingValidationException( |
| "While trying to parse '" + value + "' as " + schema.getKey(), exception); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public <T> T getValue() { |
| return (T) (booleanValue != null |
| ? getBooleanValue() |
| : dateValue != null |
| ? getDateValue() |
| : doubleValue != null |
| ? getDoubleValue() |
| : longValue != null |
| ? getLongValue() |
| : binaryValue != null |
| ? getBinaryValue() |
| : getStringValue()); |
| } |
| |
| private Object getValue(final AttrSchemaType type) { |
| Object value; |
| switch (type) { |
| |
| case Boolean: |
| value = getBooleanValue(); |
| break; |
| |
| case Long: |
| value = getLongValue(); |
| break; |
| |
| case Double: |
| value = getDoubleValue(); |
| break; |
| |
| case Date: |
| value = getDateValue(); |
| break; |
| |
| case Binary: |
| value = getBinaryValue(); |
| break; |
| |
| case String: |
| case Enum: |
| case Encrypted: |
| value = getStringValue(); |
| break; |
| |
| default: |
| value = null; |
| } |
| |
| return value; |
| } |
| |
| private String getValueAsString(final AttrSchemaType type, final PlainSchema schema) { |
| if (getValue(type) == null) { |
| LOG.warn("Could not find expected value for type {} in {}, reverting to getValue().toString()", type, this); |
| |
| Object value = getValue(); |
| return Optional.ofNullable(value).map(Object::toString).orElse(null); |
| } |
| |
| String result; |
| switch (type) { |
| |
| case Boolean: |
| result = getBooleanValue().toString(); |
| break; |
| |
| case Long: |
| result = schema == null || schema.getConversionPattern() == null |
| ? getLongValue().toString() |
| : FormatUtils.format(getLongValue(), schema.getConversionPattern()); |
| break; |
| |
| case Double: |
| result = schema == null || schema.getConversionPattern() == null |
| ? getDoubleValue().toString() |
| : FormatUtils.format(getDoubleValue(), schema.getConversionPattern()); |
| break; |
| |
| case Date: |
| result = schema == null || schema.getConversionPattern() == null |
| ? FormatUtils.format(getDateValue()) |
| : FormatUtils.format(getDateValue(), schema.getConversionPattern()); |
| break; |
| |
| case Binary: |
| result = Base64.getEncoder().encodeToString(getBinaryValue()); |
| break; |
| |
| case Encrypted: |
| if (schema == null |
| || !SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(schema.getConversionPattern()) |
| || !schema.getCipherAlgorithm().isInvertible()) { |
| |
| result = getStringValue(); |
| } else { |
| try { |
| result = Encryptor.getInstance(getSecretKey(schema)). |
| decode(getStringValue(), schema.getCipherAlgorithm()); |
| } catch (Exception e) { |
| LOG.error("Could not decode encrypted value {} for schema {}", getStringValue(), schema, e); |
| result = getStringValue(); |
| } |
| } |
| break; |
| |
| case String: |
| case Enum: |
| default: |
| result = getStringValue(); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public String getValueAsString() { |
| PlainSchema schema = getAttr() == null || getAttr().getSchema() == null |
| ? null |
| : getAttr().getSchema(); |
| AttrSchemaType type = schema == null || schema.getType() == null |
| ? AttrSchemaType.String |
| : getAttr().getSchema().getType(); |
| |
| return getValueAsString(type, schema); |
| } |
| |
| @Override |
| public String getValueAsString(final AttrSchemaType type) { |
| return getValueAsString( |
| type, |
| getAttr() == null || getAttr().getSchema() == null ? null : getAttr().getSchema()); |
| } |
| |
| @Override |
| public String getValueAsString(final PlainSchema schema) { |
| return getValueAsString(schema.getType(), schema); |
| } |
| |
| @Override |
| public String toString() { |
| return new ToStringBuilder(this). |
| append(getKey()). |
| append(stringValue). |
| append(dateValue). |
| append(booleanValue). |
| append(longValue). |
| append(doubleValue). |
| append(binaryValue). |
| build(); |
| } |
| } |