You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2019/11/13 14:26:30 UTC
[syncope] branch 2_1_X updated: [SYNCOPE-1510] Secret key can now
also be referenced as Spring property + option to store encrypted and read
cleartext
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/2_1_X by this push:
new 8fcf318 [SYNCOPE-1510] Secret key can now also be referenced as Spring property + option to store encrypted and read cleartext
8fcf318 is described below
commit 8fcf318829ece1f077424815c4c3687a38a01ab5
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Nov 13 15:26:15 2019 +0100
[SYNCOPE-1510] Secret key can now also be referenced as Spring property + option to store encrypted and read cleartext
---
.../panels/ParametersCreateWizardAttrStep.java | 33 ++++----
.../console/panels/ParametersDetailsPanel.java | 33 ++++----
.../client/console/panels/PlainSchemaDetails.java | 44 ++++++++---
.../wizards/any/AbstractAttrsWizardStep.java | 43 ++++++-----
.../client/console/panels/PlainSchemaDetails.html | 3 +
.../console/panels/PlainSchemaDetails.properties | 1 +
.../panels/PlainSchemaDetails_it.properties | 1 +
.../panels/PlainSchemaDetails_ja.properties | 1 +
.../panels/PlainSchemaDetails_pt_BR.properties | 1 +
.../panels/PlainSchemaDetails_ru.properties | 1 +
.../app/js/directives/dynamicPlainAttribute.js | 5 ++
.../resources/app/views/dynamicPlainAttribute.html | 7 +-
.../syncope/common/lib/SyncopeConstants.java | 2 +
.../jpa/entity/JPAJSONEntityListener.java | 4 +-
.../entity/anyobject/JPAJSONAnyObjectListener.java | 2 +-
.../jpa/entity/conf/JPAJSONConfListener.java | 2 +-
.../jpa/entity/group/JPAJSONGroupListener.java | 2 +-
.../entity/user/JPAJSONLinkedAccountListener.java | 2 +-
.../jpa/entity/user/JPAJSONUserListener.java | 2 +-
.../entity/JPAJSONAttributableValidator.java | 4 +-
.../jpa/entity/AbstractPlainAttrValue.java | 88 ++++++++++++++--------
.../core/persistence/jpa/inner/PlainAttrTest.java | 62 +++++++++++++++
.../java/data/AnyObjectDataBinderImpl.java | 26 ++++---
fit/core-reference/pom.xml | 11 ++-
.../org/apache/syncope/fit/core/GroupITCase.java | 54 +++++++++++++
.../reference-guide/concepts/typemanagement.adoc | 4 +-
26 files changed, 315 insertions(+), 123 deletions(-)
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardAttrStep.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardAttrStep.java
index 7fa1774..580e82c 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardAttrStep.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersCreateWizardAttrStep.java
@@ -62,7 +62,7 @@ public class ParametersCreateWizardAttrStep extends WizardStep {
schema.setRequired(true);
content.add(schema);
- final LoadableDetachableModel<List<PlainSchemaTO>> loadableDetachableModel =
+ LoadableDetachableModel<List<PlainSchemaTO>> loadableDetachableModel =
new LoadableDetachableModel<List<PlainSchemaTO>>() {
private static final long serialVersionUID = 7172461137064525667L;
@@ -74,14 +74,13 @@ public class ParametersCreateWizardAttrStep extends WizardStep {
};
- final ListView<PlainSchemaTO> listView = new ListView<PlainSchemaTO>("attrs", loadableDetachableModel) {
+ ListView<PlainSchemaTO> listView = new ListView<PlainSchemaTO>("attrs", loadableDetachableModel) {
private static final long serialVersionUID = 9101744072914090143L;
@Override
protected void populateItem(final ListItem<PlainSchemaTO> item) {
- final Panel panel = getFieldPanel("panel", modelObject.getAttrTO(), item.getModelObject());
- item.add(panel);
+ item.add(getFieldPanel("panel", modelObject.getAttrTO(), item.getModelObject()));
}
};
@@ -89,16 +88,15 @@ public class ParametersCreateWizardAttrStep extends WizardStep {
}
@SuppressWarnings({ "rawtypes", "unchecked" })
- private Panel getFieldPanel(final String id, final AttrTO attrTO, final PlainSchemaTO plainSchemaTO) {
+ private Panel getFieldPanel(final String id, final AttrTO attrTO, final PlainSchemaTO plainSchema) {
final String valueHeaderName = getString("values");
final FieldPanel panel;
- switch (plainSchemaTO.getType()) {
+ switch (plainSchema.getType()) {
case Date:
- final String dataPattern = plainSchemaTO.getConversionPattern() == null
+ String dataPattern = plainSchema.getConversionPattern() == null
? SyncopeConstants.DEFAULT_DATE_PATTERN
- : plainSchemaTO.getConversionPattern();
-
+ : plainSchema.getConversionPattern();
if (dataPattern.contains("H")) {
panel = new AjaxDateTimeFieldPanel(
id, valueHeaderName, new Model<>(), dataPattern);
@@ -139,7 +137,7 @@ public class ParametersCreateWizardAttrStep extends WizardStep {
case Enum:
panel = new AjaxDropDownChoicePanel<>(id, valueHeaderName, new Model<>(), false);
- ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(plainSchemaTO));
+ ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(plainSchema));
if (!attrTO.getValues().isEmpty()) {
((AjaxDropDownChoicePanel) panel).setChoiceRenderer(new IChoiceRenderer<String>() {
@@ -164,7 +162,7 @@ public class ParametersCreateWizardAttrStep extends WizardStep {
});
}
((AjaxDropDownChoicePanel<String>) panel).setNullValid(
- "true".equalsIgnoreCase(plainSchemaTO.getMandatoryCondition()));
+ "true".equalsIgnoreCase(plainSchema.getMandatoryCondition()));
break;
case Long:
@@ -178,25 +176,26 @@ public class ParametersCreateWizardAttrStep extends WizardStep {
break;
case Binary:
- panel = new BinaryFieldPanel(id, valueHeaderName, new Model<>(), plainSchemaTO.getMimeType(),
+ panel = new BinaryFieldPanel(id, valueHeaderName, new Model<>(), plainSchema.getMimeType(),
schema.getModelObject());
break;
case Encrypted:
- panel = new EncryptedFieldPanel(id, valueHeaderName, new Model<>(), true);
+ panel = SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(plainSchema.getConversionPattern())
+ ? new AjaxTextFieldPanel(id, valueHeaderName, new Model<>(), false)
+ : new EncryptedFieldPanel(id, valueHeaderName, new Model<>(), true);
break;
default:
panel = new AjaxTextFieldPanel(id, valueHeaderName, new Model<>(), false);
}
- if (plainSchemaTO.isMultivalue()) {
+ if (plainSchema.isMultivalue()) {
return new MultiFieldPanel.Builder<>(
new PropertyModel<>(attrTO, "values")).build(id, valueHeaderName, panel);
- } else {
- panel.setNewModel(attrTO.getValues());
}
- panel.setRequired("true".equalsIgnoreCase(plainSchemaTO.getMandatoryCondition()));
+ panel.setNewModel(attrTO.getValues());
+ panel.setRequired("true".equalsIgnoreCase(plainSchema.getMandatoryCondition()));
return panel;
}
}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersDetailsPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersDetailsPanel.java
index c7e01fb..427ed8d 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersDetailsPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ParametersDetailsPanel.java
@@ -78,21 +78,21 @@ public class ParametersDetailsPanel extends Panel {
@SuppressWarnings({ "rawtypes", "unchecked" })
private Panel getFieldPanel(final String id, final AttrTO attrTO) {
- final String valueHeaderName = getString("values");
+ String valueHeaderName = getString("values");
- final PlainSchemaTO schemaTO = schemaRestClient.read(SchemaType.PLAIN, attrTO.getSchema());
+ PlainSchemaTO plainSchema = schemaRestClient.read(SchemaType.PLAIN, attrTO.getSchema());
- final FieldPanel panel;
- switch (schemaTO.getType()) {
+ FieldPanel panel;
+ switch (plainSchema.getType()) {
case Date:
- final String datePattern = schemaTO.getConversionPattern() == null
+ final String datePattern = plainSchema.getConversionPattern() == null
? SyncopeConstants.DEFAULT_DATE_PATTERN
- : schemaTO.getConversionPattern();
+ : plainSchema.getConversionPattern();
if (StringUtils.containsIgnoreCase(datePattern, "H")) {
- panel = new AjaxDateTimeFieldPanel("panel", schemaTO.getKey(), new Model<>(), datePattern);
+ panel = new AjaxDateTimeFieldPanel("panel", plainSchema.getKey(), new Model<>(), datePattern);
} else {
- panel = new AjaxDateFieldPanel("panel", schemaTO.getKey(), new Model<>(), datePattern);
+ panel = new AjaxDateFieldPanel("panel", plainSchema.getKey(), new Model<>(), datePattern);
}
break;
case Boolean:
@@ -125,7 +125,7 @@ public class ParametersDetailsPanel extends Panel {
break;
case Enum:
panel = new AjaxDropDownChoicePanel<>(id, valueHeaderName, new Model<>(), false);
- ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(schemaTO));
+ ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(plainSchema));
if (!attrTO.getValues().isEmpty()) {
((AjaxDropDownChoicePanel) panel).setChoiceRenderer(new IChoiceRenderer<String>() {
@@ -150,7 +150,7 @@ public class ParametersDetailsPanel extends Panel {
});
}
((AjaxDropDownChoicePanel<String>) panel).setNullValid(
- "false".equalsIgnoreCase(schemaTO.getMandatoryCondition()));
+ "false".equalsIgnoreCase(plainSchema.getMandatoryCondition()));
break;
case Long:
@@ -164,25 +164,26 @@ public class ParametersDetailsPanel extends Panel {
break;
case Binary:
- panel = new BinaryFieldPanel(id, valueHeaderName, new Model<>(), schemaTO.getMimeType(),
+ panel = new BinaryFieldPanel(id, valueHeaderName, new Model<>(), plainSchema.getMimeType(),
schema.getModelObject());
break;
case Encrypted:
- panel = new EncryptedFieldPanel(id, valueHeaderName, new Model<>(), true);
+ panel = SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(plainSchema.getConversionPattern())
+ ? new AjaxTextFieldPanel(id, valueHeaderName, new Model<>(), false)
+ : new EncryptedFieldPanel(id, valueHeaderName, new Model<>(), true);
break;
default:
panel = new AjaxTextFieldPanel(id, valueHeaderName, new Model<>(), false);
}
- if (schemaTO.isMultivalue()) {
+ if (plainSchema.isMultivalue()) {
return new MultiFieldPanel.Builder<>(
new PropertyModel<List<String>>(attrTO, "values")).build(id, valueHeaderName, panel);
- } else {
- panel.setNewModel(attrTO.getValues());
}
- panel.setRequired("true".equalsIgnoreCase(schemaTO.getMandatoryCondition()));
+ panel.setNewModel(attrTO.getValues());
+ panel.setRequired("true".equalsIgnoreCase(plainSchema.getMandatoryCondition()));
return panel;
}
}
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
index c6e3c88..c873d40 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
@@ -25,6 +25,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
+import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.SyncopeConsoleApplication;
import org.apache.syncope.client.console.commons.Constants;
@@ -37,6 +38,7 @@ import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPan
import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
import org.apache.syncope.client.console.wicket.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.EntityTO;
import org.apache.syncope.common.lib.to.PlainSchemaTO;
import org.apache.syncope.common.lib.types.AttrSchemaType;
@@ -82,20 +84,20 @@ public class PlainSchemaDetails extends AbstractSchemaDetailsPanel {
add(type);
// long, double, date
- final AjaxTextFieldPanel conversionPattern = new AjaxTextFieldPanel("conversionPattern",
+ AjaxTextFieldPanel conversionPattern = new AjaxTextFieldPanel("conversionPattern",
getString("conversionPattern"), new PropertyModel<>(schemaTO, "conversionPattern"));
add(conversionPattern);
- final WebMarkupContainer conversionParams = new WebMarkupContainer("conversionParams");
+ WebMarkupContainer conversionParams = new WebMarkupContainer("conversionParams");
conversionParams.setOutputMarkupPlaceholderTag(true);
conversionParams.add(conversionPattern);
add(conversionParams);
- final WebMarkupContainer typeParams = new WebMarkupContainer("typeParams");
+ WebMarkupContainer typeParams = new WebMarkupContainer("typeParams");
typeParams.setOutputMarkupPlaceholderTag(true);
// enum
- final AjaxTextFieldPanel enumerationValuesPanel = new AjaxTextFieldPanel("panel",
+ AjaxTextFieldPanel enumerationValuesPanel = new AjaxTextFieldPanel("panel",
"enumerationValues", new Model<>(null));
enumerationValues = new MultiFieldPanel.Builder<String>(
@@ -176,34 +178,52 @@ public class PlainSchemaDetails extends AbstractSchemaDetailsPanel {
"enumerationKeys",
new AjaxTextFieldPanel("panel", "enumerationKeys", new Model<String>()));
- final WebMarkupContainer enumParams = new WebMarkupContainer("enumParams");
+ WebMarkupContainer enumParams = new WebMarkupContainer("enumParams");
enumParams.setOutputMarkupPlaceholderTag(true);
enumParams.add(enumerationValues);
enumParams.add(enumerationKeys);
typeParams.add(enumParams);
// encrypted
- final AjaxTextFieldPanel secretKey = new AjaxTextFieldPanel("secretKey",
+ AjaxTextFieldPanel secretKey = new AjaxTextFieldPanel("secretKey",
getString("secretKey"), new PropertyModel<>(schemaTO, "secretKey"));
- final AjaxDropDownChoicePanel<CipherAlgorithm> cipherAlgorithm = new AjaxDropDownChoicePanel<>(
+ AjaxDropDownChoicePanel<CipherAlgorithm> cipherAlgorithm = new AjaxDropDownChoicePanel<>(
"cipherAlgorithm", getString("cipherAlgorithm"),
new PropertyModel<>(schemaTO, "cipherAlgorithm"));
-
cipherAlgorithm.setChoices(Arrays.asList(CipherAlgorithm.values()));
- final WebMarkupContainer encryptedParams = new WebMarkupContainer("encryptedParams");
+ AjaxCheckBoxPanel transparentEncryption = new AjaxCheckBoxPanel(
+ "transparentEncryption", "transparentEncryption", new Model<Boolean>() {
+
+ private static final long serialVersionUID = 5636572627689425575L;
+
+ @Override
+ public Boolean getObject() {
+ return SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(schemaTO.getConversionPattern());
+ }
+
+ @Override
+ public void setObject(final Boolean object) {
+ schemaTO.setConversionPattern(BooleanUtils.isTrue(object)
+ ? SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN
+ : null);
+ }
+ }, true);
+
+ WebMarkupContainer encryptedParams = new WebMarkupContainer("encryptedParams");
encryptedParams.setOutputMarkupPlaceholderTag(true);
encryptedParams.add(secretKey);
encryptedParams.add(cipherAlgorithm);
+ encryptedParams.add(transparentEncryption);
typeParams.add(encryptedParams);
// binary
- final AjaxTextFieldPanel mimeType = new AjaxTextFieldPanel("mimeType",
+ AjaxTextFieldPanel mimeType = new AjaxTextFieldPanel("mimeType",
getString("mimeType"), new PropertyModel<>(schemaTO, "mimeType"));
- final WebMarkupContainer binaryParams = new WebMarkupContainer("binaryParams");
+ WebMarkupContainer binaryParams = new WebMarkupContainer("binaryParams");
binaryParams.setOutputMarkupPlaceholderTag(true);
binaryParams.add(mimeType);
typeParams.add(binaryParams);
@@ -301,7 +321,7 @@ public class PlainSchemaDetails extends AbstractSchemaDetailsPanel {
final AjaxTextFieldPanel secretKey, final AjaxDropDownChoicePanel<CipherAlgorithm> cipherAlgorithm,
final WebMarkupContainer binaryParams, final AjaxTextFieldPanel mimeType) {
- final int typeOrdinal = Integer.parseInt(type.getField().getValue());
+ int typeOrdinal = Integer.parseInt(type.getField().getValue());
if (AttrSchemaType.Long.ordinal() == typeOrdinal
|| AttrSchemaType.Double.ordinal() == typeOrdinal
|| AttrSchemaType.Date.ordinal() == typeOrdinal) {
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
index 5e894b5..c20df70 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
@@ -177,7 +177,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
}
@SuppressWarnings({ "rawtypes", "unchecked" })
- protected FieldPanel getFieldPanel(final PlainSchemaTO schemaTO) {
+ protected FieldPanel getFieldPanel(final PlainSchemaTO plainSchema) {
final boolean required;
final boolean readOnly;
final AttrSchemaType type;
@@ -189,9 +189,9 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
type = AttrSchemaType.String;
jexlHelp = true;
} else {
- required = schemaTO.getMandatoryCondition().equalsIgnoreCase("true");
- readOnly = schemaTO.isReadonly();
- type = schemaTO.getType();
+ required = plainSchema.getMandatoryCondition().equalsIgnoreCase("true");
+ readOnly = plainSchema.isReadonly();
+ type = plainSchema.getType();
jexlHelp = false;
}
@@ -200,27 +200,27 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
case Boolean:
panel = new AjaxCheckBoxPanel(
"panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()),
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()),
new Model<>(),
true);
panel.setRequired(required);
break;
case Date:
- String datePattern = schemaTO.getConversionPattern() == null
+ String datePattern = plainSchema.getConversionPattern() == null
? SyncopeConstants.DEFAULT_DATE_PATTERN
- : schemaTO.getConversionPattern();
+ : plainSchema.getConversionPattern();
if (datePattern.contains("H")) {
panel = new AjaxDateTimeFieldPanel(
"panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()),
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()),
new Model<>(),
datePattern);
} else {
panel = new AjaxDateFieldPanel(
"panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()),
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()),
new Model<>(),
datePattern);
}
@@ -233,15 +233,15 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
case Enum:
panel = new AjaxDropDownChoicePanel<>("panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()), new Model<>(), true);
- ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(schemaTO));
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()), new Model<>(), true);
+ ((AjaxDropDownChoicePanel<String>) panel).setChoices(SchemaUtils.getEnumeratedValues(plainSchema));
- if (StringUtils.isNotBlank(schemaTO.getEnumerationKeys())) {
+ if (StringUtils.isNotBlank(plainSchema.getEnumerationKeys())) {
((AjaxDropDownChoicePanel) panel).setChoiceRenderer(new IChoiceRenderer<String>() {
private static final long serialVersionUID = -3724971416312135885L;
- private final Map<String, String> valueMap = SchemaUtils.getEnumeratedKeyValues(schemaTO);
+ private final Map<String, String> valueMap = SchemaUtils.getEnumeratedKeyValues(plainSchema);
@Override
public String getDisplayValue(final String value) {
@@ -269,7 +269,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
case Long:
panel = new AjaxSpinnerFieldPanel.Builder<Long>().enableOnChange().build(
"panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()),
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()),
Long.class,
new Model<>());
@@ -281,7 +281,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
case Double:
panel = new AjaxSpinnerFieldPanel.Builder<Double>().enableOnChange().step(0.1).build(
"panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()),
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()),
Double.class,
new Model<>());
@@ -294,9 +294,9 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
final PageReference pageRef = getPageReference();
panel = new BinaryFieldPanel(
"panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()),
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()),
new Model<>(),
- schemaTO.getMimeType(),
+ plainSchema.getMimeType(),
fileKey) {
private static final long serialVersionUID = -3268213909514986831L;
@@ -313,8 +313,11 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
break;
case Encrypted:
- panel = new EncryptedFieldPanel("panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()), new Model<>(), true);
+ panel = SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN.equals(plainSchema.getConversionPattern())
+ ? new AjaxTextFieldPanel("panel",
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()), new Model<>(), true)
+ : new EncryptedFieldPanel("panel",
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()), new Model<>(), true);
if (required) {
panel.addRequiredLabel();
@@ -323,7 +326,7 @@ public abstract class AbstractAttrsWizardStep<S extends SchemaTO> extends Wizard
default:
panel = new AjaxTextFieldPanel("panel",
- schemaTO.getLabel(SyncopeConsoleSession.get().getLocale()), new Model<>(), true);
+ plainSchema.getLabel(SyncopeConsoleSession.get().getLocale()), new Model<>(), true);
if (jexlHelp) {
AjaxTextFieldPanel.class.cast(panel).enableJexlHelp();
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.html b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.html
index ac334de..6859c9d 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.html
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.html
@@ -45,6 +45,9 @@ under the License.
<div class="form-group">
<span wicket:id="cipherAlgorithm">[cipherAlgorithm]</span>
</div>
+ <div class="form-group">
+ <span wicket:id="transparentEncryption">[transparentEncryption]</span>
+ </div>
</div>
<div wicket:id="binaryParams">
<div class="form-group">
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.properties
index b3f99bb..9367a07 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails.properties
@@ -31,3 +31,4 @@ readonly=Read-only
secretKey=Secret key
cipherAlgorithm=Cipher algorithm
mimeType=MIME Type
+transparentEncryption=Transparent encryption?
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_it.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_it.properties
index bd4766b..e9f4449 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_it.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_it.properties
@@ -31,3 +31,4 @@ multivalueAndUniqueConstr.validation=Le opzioni 'Multivalore' e 'Vincolo unique'
secretKey=Chiave segreta
cipherAlgorithm=Algoritmo di cifratura
mimeType=MIME Type
+transparentEncryption=Cifratura trasparente?
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ja.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ja.properties
index eb9c986..6316527 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ja.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ja.properties
@@ -31,3 +31,4 @@ readonly=\u8aad\u307f\u53d6\u308a\u5c02\u7528
secretKey=\u79d8\u5bc6\u9375
cipherAlgorithm=\u6697\u53f7\u5316\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0
mimeType=MIME \u30bf\u30a4\u30d7
+transparentEncryption=Transparent encryption?
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_pt_BR.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_pt_BR.properties
index f607f99..88e664c 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_pt_BR.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_pt_BR.properties
@@ -31,3 +31,4 @@ readonly=Apenas leitura
secretKey=Chave secreta
cipherAlgorithm=Algoritmo de criptografia
mimeType=MIME Type
+transparentEncryption=Transparent encryption?
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ru.properties b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ru.properties
index b190c79..106c483 100644
--- a/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ru.properties
+++ b/client/console/src/main/resources/org/apache/syncope/client/console/panels/PlainSchemaDetails_ru.properties
@@ -49,3 +49,4 @@ secretKey=\u041a\u043b\u044e\u0447 \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u0
cipherAlgorithm=\u0410\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f
# mimeType=\u00d0\u00a2\u00d0\u00b8\u00d0\u00bf MIME
mimeType=\u0422\u0438\u043f MIME
+transparentEncryption=Transparent encryption?
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js
index 7ed07e1..3495bf6 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js
+++ b/client/enduser/src/main/resources/META-INF/resources/app/js/directives/dynamicPlainAttribute.js
@@ -29,6 +29,11 @@ angular.module('self')
user: "="
},
controller: function ($scope, $rootScope, $element, $window) {
+ $scope.schemaType = function (schema) {
+ return schema.type == "Encrypted" && schema.conversionPattern == 'ENCRYPTED_DECODE_CONVERSION_PATTERN'
+ ? "String"
+ : schema.type;
+ };
$scope.initAttribute = function (schema, index) {
switch (schema.type) {
case "Long":
diff --git a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
index c6b5042..7dcd782 100644
--- a/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
+++ b/client/enduser/src/main/resources/META-INF/resources/app/views/dynamicPlainAttribute.html
@@ -16,7 +16,7 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
-<div ng-switch="schema.type" class="schema-type">
+<div ng-switch="schemaType(schema)" class="schema-type">
<input ng-switch-when="String" class="form-control" type="text"
ng-model="user.plainAttrs[schema.key].values[index]"
ng-required="{{schema.mandatoryCondition}}" validate="true"
@@ -143,8 +143,3 @@ under the License.
ng-disabled="schema.readonly || customReadonly(schema.key)"
ng-init="initAttribute(schema, index)"/>
</div>
-
-
-
-
-
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java b/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
index b8a7ee4..fea103a 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
@@ -63,6 +63,8 @@ public final class SyncopeConstants {
public static final Pattern UUID_PATTERN = Pattern.compile(UUID_REGEX);
+ public static final String ENCRYPTED_DECODE_CONVERSION_PATTERN = "ENCRYPTED_DECODE_CONVERSION_PATTERN";
+
public static final String DOUBLE_DASH = "--";
public static final String CRLF = "\r\n";
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java
index 10b8e77..f89f6d6 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAJSONEntityListener.java
@@ -28,7 +28,7 @@ import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
public abstract class JPAJSONEntityListener<A extends Any<?>> {
- protected abstract List<? extends JSONPlainAttr<A>> getValues(String plainAttrsJSON);
+ protected abstract List<? extends JSONPlainAttr<A>> getAttrs(String plainAttrsJSON);
@SuppressWarnings("unchecked")
protected void json2list(final JSONAttributable<A> entity, final boolean clearFirst) {
@@ -36,7 +36,7 @@ public abstract class JPAJSONEntityListener<A extends Any<?>> {
entity.getPlainAttrList().clear();
}
if (entity.getPlainAttrsJSON() != null) {
- getValues(entity.getPlainAttrsJSON()).stream().filter(attr -> attr.getSchema() != null).map(attr -> {
+ getAttrs(entity.getPlainAttrsJSON()).stream().filter(attr -> attr.getSchema() != null).map(attr -> {
if (entity instanceof Any) {
attr.setOwner((A) entity);
} else if (entity instanceof LinkedAccount) {
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObjectListener.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObjectListener.java
index ad03fc5..f554332 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObjectListener.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAJSONAnyObjectListener.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
public class JPAJSONAnyObjectListener extends JPAJSONEntityListener<AnyObject> {
@Override
- protected List<? extends JSONPlainAttr<AnyObject>> getValues(final String plainAttrsJSON) {
+ protected List<? extends JSONPlainAttr<AnyObject>> getAttrs(final String plainAttrsJSON) {
return POJOHelper.deserialize(plainAttrsJSON, new TypeReference<List<JPAJSONAPlainAttr>>() {
});
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAJSONConfListener.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAJSONConfListener.java
index 7923c47..b10562d 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAJSONConfListener.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/conf/JPAJSONConfListener.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
public class JPAJSONConfListener extends JPAJSONEntityListener<Conf> {
@Override
- protected List<? extends JSONPlainAttr<Conf>> getValues(final String plainAttrsJSON) {
+ protected List<? extends JSONPlainAttr<Conf>> getAttrs(final String plainAttrsJSON) {
return POJOHelper.deserialize(plainAttrsJSON, new TypeReference<List<JPAJSONCPlainAttr>>() {
});
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroupListener.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroupListener.java
index a259238..0574898 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroupListener.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAJSONGroupListener.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
public class JPAJSONGroupListener extends JPAJSONEntityListener<Group> {
@Override
- protected List<? extends JSONPlainAttr<Group>> getValues(final String plainAttrsJSON) {
+ protected List<? extends JSONPlainAttr<Group>> getAttrs(final String plainAttrsJSON) {
return POJOHelper.deserialize(plainAttrsJSON, new TypeReference<List<JPAJSONGPlainAttr>>() {
});
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccountListener.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccountListener.java
index 3a71a95..26436a4 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccountListener.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONLinkedAccountListener.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
public class JPAJSONLinkedAccountListener extends JPAJSONEntityListener<User> {
@Override
- protected List<? extends JSONLAPlainAttr> getValues(final String plainAttrsJSON) {
+ protected List<? extends JSONLAPlainAttr> getAttrs(final String plainAttrsJSON) {
return POJOHelper.deserialize(plainAttrsJSON, new TypeReference<List<JPAJSONLAPlainAttr>>() {
});
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUserListener.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUserListener.java
index 66f8b08..1b924cc 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUserListener.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAJSONUserListener.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.entity.JSONPlainAttr;
public class JPAJSONUserListener extends JPAJSONEntityListener<User> {
@Override
- protected List<? extends JSONPlainAttr<User>> getValues(final String plainAttrsJSON) {
+ protected List<? extends JSONPlainAttr<User>> getAttrs(final String plainAttrsJSON) {
return POJOHelper.deserialize(plainAttrsJSON, new TypeReference<List<JPAJSONUPlainAttr>>() {
});
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableValidator.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableValidator.java
index 94fb108..b8d1a08 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableValidator.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/JPAJSONAttributableValidator.java
@@ -36,8 +36,8 @@ public class JPAJSONAttributableValidator extends AbstractValidator<JPAJSONAttri
entity.getPlainAttrList().forEach(attr -> {
PlainAttr<?> plainAttr = (PlainAttr<?>) attr;
isValid.getAndSet(isValid.get() && attrValidator.isValid(plainAttr, context));
- plainAttr.getValues().forEach(value
- -> isValid.getAndSet(isValid.get() && attrValueValidator.isValid(value, context)));
+ plainAttr.getValues().forEach(
+ value -> isValid.getAndSet(isValid.get() && attrValueValidator.isValid(value, context)));
});
return isValid.get();
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java
index 2ed3468..dcb9e54 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractPlainAttrValue.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.persistence.jpa.entity;
import java.util.Base64;
import java.util.Date;
+import java.util.regex.Pattern;
import javax.persistence.Lob;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
@@ -27,12 +28,14 @@ import javax.persistence.TemporalType;
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.provisioning.api.utils.FormatUtils;
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.spring.ApplicationContextProvider;
import org.apache.syncope.core.spring.security.Encryptor;
@MappedSuperclass
@@ -41,6 +44,8 @@ public abstract class AbstractPlainAttrValue extends AbstractGeneratedKeyEntity
private static final long serialVersionUID = -9141923816611244785L;
+ private static final Pattern SPRING_ENV_PROPERTY = Pattern.compile("^\\$\\{.*\\}$");
+
private String stringValue;
@Temporal(TemporalType.TIMESTAMP)
@@ -127,6 +132,13 @@ public abstract class AbstractPlainAttrValue extends AbstractGeneratedKeyEntity
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;
@@ -139,42 +151,42 @@ public abstract class AbstractPlainAttrValue extends AbstractGeneratedKeyEntity
case Long:
try {
- this.setLongValue(schema.getConversionPattern() == null
- ? Long.valueOf(value)
- : FormatUtils.parseNumber(value, schema.getConversionPattern()).longValue());
- } catch (Exception pe) {
- exception = pe;
- }
- break;
+ 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;
+ 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)
- : new Date(FormatUtils.parseDate(value, schema.getConversionPattern()).getTime()));
- } catch (Exception pe) {
- exception = pe;
- }
- break;
+ this.setDateValue(schema.getConversionPattern() == null
+ ? FormatUtils.parseDate(value)
+ : new Date(FormatUtils.parseDate(value, schema.getConversionPattern()).getTime()));
+ } catch (Exception pe) {
+ exception = pe;
+ }
+ break;
case Encrypted:
try {
- this.setStringValue(Encryptor.getInstance(schema.getSecretKey()).
- encode(value, schema.getCipherAlgorithm()));
- } catch (Exception pe) {
- exception = pe;
- }
- break;
+ this.setStringValue(Encryptor.getInstance(getSecretKey(schema)).
+ encode(value, schema.getCipherAlgorithm()));
+ } catch (Exception pe) {
+ exception = pe;
+ }
+ break;
case Binary:
this.setBinaryValue(Base64.getDecoder().decode(value));
@@ -187,8 +199,8 @@ public abstract class AbstractPlainAttrValue extends AbstractGeneratedKeyEntity
}
if (exception != null) {
- throw new ParsingValidationException("While trying to parse '" + value + "' as " + schema.getKey(),
- exception);
+ throw new ParsingValidationException(
+ "While trying to parse '" + value + "' as " + schema.getKey(), exception);
}
}
@@ -282,9 +294,25 @@ public abstract class AbstractPlainAttrValue extends AbstractGeneratedKeyEntity
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:
- case Encrypted:
default:
result = getStringValue();
}
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java
index 856e570..54c773f 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PlainAttrTest.java
@@ -19,19 +19,27 @@
package org.apache.syncope.core.persistence.jpa.inner;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Random;
import javax.validation.ValidationException;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
import org.apache.syncope.common.lib.types.EntityViolationType;
import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
@@ -41,6 +49,7 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.persistence.jpa.AbstractTest;
import org.apache.syncope.core.spring.security.Encryptor;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.spring.security.SecureRandomUtils;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -58,6 +67,9 @@ public class PlainAttrTest extends AbstractTest {
@Autowired
private PlainSchemaDAO plainSchemaDAO;
+ @Autowired
+ private AnyTypeClassDAO anyTypeClassDAO;
+
@Tag("plainAttrTable")
@Test
public void findByKey() {
@@ -229,6 +241,56 @@ public class PlainAttrTest extends AbstractTest {
}
@Test
+ public void encryptedWithKeyAsSysProp() throws Exception {
+ PlainSchema obscureSchema = plainSchemaDAO.find("obscure");
+ assertNotNull(obscureSchema);
+
+ PlainSchema obscureWithKeyAsSysprop = entityFactory.newEntity(PlainSchema.class);
+ obscureWithKeyAsSysprop.setKey("obscureWithKeyAsSysprop");
+ obscureWithKeyAsSysprop.setAnyTypeClass(obscureSchema.getAnyTypeClass());
+ obscureWithKeyAsSysprop.setType(AttrSchemaType.Encrypted);
+ obscureWithKeyAsSysprop.setCipherAlgorithm(obscureSchema.getCipherAlgorithm());
+ obscureWithKeyAsSysprop.setSecretKey("${obscureSecretKey}");
+
+ obscureWithKeyAsSysprop = plainSchemaDAO.save(obscureWithKeyAsSysprop);
+
+ System.setProperty("obscureSecretKey", obscureSchema.getSecretKey());
+
+ UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class);
+ attr.setSchema(obscureWithKeyAsSysprop);
+ attr.add("testvalue", anyUtilsFactory.getInstance(AnyTypeKind.USER));
+
+ assertEquals(Encryptor.getInstance(obscureSchema.getSecretKey()).
+ encode("testvalue", obscureSchema.getCipherAlgorithm()), attr.getValues().get(0).getStringValue());
+ }
+
+ @Test
+ public void encryptedWithDecodeConversionPattern() throws Exception {
+ PlainSchema obscureWithDecodeConversionPattern = entityFactory.newEntity(PlainSchema.class);
+ obscureWithDecodeConversionPattern.setKey("obscureWithDecodeConversionPattern");
+ obscureWithDecodeConversionPattern.setAnyTypeClass(anyTypeClassDAO.find("other"));
+ obscureWithDecodeConversionPattern.setType(AttrSchemaType.Encrypted);
+ obscureWithDecodeConversionPattern.setCipherAlgorithm(CipherAlgorithm.AES);
+ obscureWithDecodeConversionPattern.setSecretKey(SecureRandomUtils.generateRandomUUID().toString());
+
+ obscureWithDecodeConversionPattern = plainSchemaDAO.save(obscureWithDecodeConversionPattern);
+
+ UPlainAttr attr = entityFactory.newEntity(UPlainAttr.class);
+ attr.setSchema(obscureWithDecodeConversionPattern);
+ attr.add("testvalue", anyUtilsFactory.getInstance(AnyTypeKind.USER));
+
+ assertEquals(Encryptor.getInstance(obscureWithDecodeConversionPattern.getSecretKey()).
+ encode("testvalue", obscureWithDecodeConversionPattern.getCipherAlgorithm()),
+ attr.getValues().get(0).getStringValue());
+
+ obscureWithDecodeConversionPattern.setConversionPattern(SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN);
+ plainSchemaDAO.save(obscureWithDecodeConversionPattern);
+
+ assertNotEquals("testvalue", attr.getValues().get(0).getStringValue());
+ assertEquals("testvalue", attr.getValuesAsStrings().get(0));
+ }
+
+ @Test
public void saveWithBinary() throws UnsupportedEncodingException {
User user = userDAO.find("1417acbe-cbf6-4277-9372-e75e04f97000");
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index abb122b..ba46f9b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -337,9 +337,8 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
// memberships
- anyObjectPatch.getMemberships().stream().
- filter((membPatch) -> (membPatch.getGroup() != null)).forEachOrdered(membPatch -> {
- anyObject.getMembership(membPatch.getGroup()).ifPresent(membership -> {
+ anyObjectPatch.getMemberships().stream().filter(patch -> patch.getGroup() != null).forEach(patch -> {
+ anyObject.getMembership(patch.getGroup()).ifPresent(membership -> {
anyObject.remove(membership);
membership.setLeftEnd(null);
anyObject.getPlainAttrs(membership).forEach(attr -> {
@@ -349,7 +348,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
plainAttrValueDAO.deleteAll(attr, anyUtils);
});
- if (membPatch.getOperation() == PatchOperation.DELETE) {
+ if (patch.getOperation() == PatchOperation.DELETE) {
groupDAO.findAllResourceKeys(membership.getRightEnd().getKey()).stream().
filter(resource -> reasons.containsKey(resource)).
forEach(resource -> {
@@ -358,10 +357,10 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
});
}
});
- if (membPatch.getOperation() == PatchOperation.ADD_REPLACE) {
- Group group = groupDAO.find(membPatch.getGroup());
+ if (patch.getOperation() == PatchOperation.ADD_REPLACE) {
+ Group group = groupDAO.find(patch.getGroup());
if (group == null) {
- LOG.debug("Ignoring invalid group {}", membPatch.getGroup());
+ LOG.debug("Ignoring invalid group {}", patch.getGroup());
} else if (anyObject.getRealm().getFullPath().startsWith(group.getRealm().getFullPath())) {
AMembership newMembership = entityFactory.newEntity(AMembership.class);
newMembership.setRightEnd(group);
@@ -369,7 +368,7 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
anyObject.add(newMembership);
- membPatch.getPlainAttrs().forEach(attrTO -> {
+ patch.getPlainAttrs().forEach(attrTO -> {
PlainSchema schema = getPlainSchema(attrTO.getSchema());
if (schema == null) {
LOG.debug("Invalid " + PlainSchema.class.getSimpleName()
@@ -387,10 +386,15 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
newAttr.setSchema(schema);
anyObject.add(newAttr);
- AttrPatch patch = new AttrPatch.Builder().attrTO(attrTO).build();
processAttrPatch(
- anyObject, patch, schema, newAttr, anyUtils,
- resources, propByRes, invalidValues);
+ anyObject,
+ new AttrPatch.Builder().attrTO(attrTO).build(),
+ schema,
+ newAttr,
+ anyUtils,
+ resources,
+ propByRes,
+ invalidValues);
}
}
});
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 429c38d..517a63d 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -36,7 +36,9 @@ under the License.
<properties>
<jdbcdriver.groupId>com.h2database</jdbcdriver.groupId>
<jdbcdriver.artifactId>h2</jdbcdriver.artifactId>
-
+
+ <obscureSecretKey>obscureSecretKeyValue</obscureSecretKey>
+
<rootpom.basedir>${basedir}/../..</rootpom.basedir>
<cli-test.dir>${project.build.directory}/cli-test</cli-test.dir>
</properties>
@@ -294,6 +296,12 @@ under the License.
<inherited>true</inherited>
<configuration>
<container>
+ <systemProperties>
+ <java.security.egd>file:/dev/./urandom</java.security.egd>
+ <java.util.secureRandomSeed>true</java.util.secureRandomSeed>
+
+ <obscureSecretKey>${obscureSecretKey}</obscureSecretKey>
+ </systemProperties>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
@@ -451,6 +459,7 @@ under the License.
<configuration>
<systemPropertyVariables>
<jaxrsContentType>${jaxrs.content.type}</jaxrsContentType>
+ <obscureSecretKey>${obscureSecretKey}</obscureSecretKey>
</systemPropertyVariables>
<includes>
<include>**/*ITCase.java</include>
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
index d8a9894..3a20570 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java
@@ -20,6 +20,7 @@ package org.apache.syncope.fit.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -76,6 +77,7 @@ import org.apache.syncope.common.lib.to.TypeExtensionTO;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.ConnectorCapability;
import org.apache.syncope.common.lib.types.MappingPurpose;
@@ -90,6 +92,7 @@ import org.apache.syncope.common.rest.api.beans.AnyQuery;
import org.apache.syncope.common.rest.api.service.GroupService;
import org.apache.syncope.common.rest.api.service.SyncopeService;
import org.apache.syncope.core.provisioning.java.job.TaskJob;
+import org.apache.syncope.core.spring.security.Encryptor;
import org.apache.syncope.fit.AbstractITCase;
import org.junit.jupiter.api.Test;
@@ -595,6 +598,57 @@ public class GroupITCase extends AbstractITCase {
}
@Test
+ public void encrypted() throws Exception {
+ // 1. create encrypted schema with secret key as system property
+ PlainSchemaTO encrypted = new PlainSchemaTO();
+ encrypted.setKey("encrypted" + getUUIDString());
+ encrypted.setType(AttrSchemaType.Encrypted);
+ encrypted.setCipherAlgorithm(CipherAlgorithm.SHA512);
+ encrypted.setSecretKey("${obscureSecretKey}");
+ schemaService.create(SchemaType.PLAIN, encrypted);
+
+ // 2. add the new schema to the default group type
+ AnyTypeTO type = anyTypeService.read(AnyTypeKind.GROUP.name());
+ String typeClassName = type.getClasses().get(0);
+ AnyTypeClassTO typeClass = anyTypeClassService.read(typeClassName);
+ typeClass.getPlainSchemas().add(encrypted.getKey());
+ anyTypeClassService.update(typeClass);
+ typeClass = anyTypeClassService.read(typeClassName);
+ assertTrue(typeClass.getPlainSchemas().contains(encrypted.getKey()));
+
+ // 3. create group, verify that the correct encrypted value is returned
+ GroupTO group = getSampleTO("encrypted");
+ group.getPlainAttrs().add(new AttrTO.Builder().schema(encrypted.getKey()).value("testvalue").build());
+ group = createGroup(group).getEntity();
+
+ assertEquals(Encryptor.getInstance(System.getProperty("obscureSecretKey")).
+ encode("testvalue", encrypted.getCipherAlgorithm()),
+ group.getPlainAttr(encrypted.getKey()).get().getValues().get(0));
+
+ // 4. update schema to return cleartext values
+ encrypted.setAnyTypeClass(typeClassName);
+ encrypted.setCipherAlgorithm(CipherAlgorithm.AES);
+ encrypted.setConversionPattern(SyncopeConstants.ENCRYPTED_DECODE_CONVERSION_PATTERN);
+ schemaService.update(SchemaType.PLAIN, encrypted);
+
+ // 5. update group, verify that the cleartext value is returned
+ GroupPatch patch = new GroupPatch();
+ patch.setKey(group.getKey());
+ patch.getPlainAttrs().add(new AttrPatch.Builder().
+ attrTO(new AttrTO.Builder().schema(encrypted.getKey()).value("testvalue").build()).build());
+ group = updateGroup(patch).getEntity();
+
+ assertEquals("testvalue", group.getPlainAttr(encrypted.getKey()).get().getValues().get(0));
+
+ // 6. update schema again to disallow cleartext values
+ encrypted.setConversionPattern(null);
+ schemaService.update(SchemaType.PLAIN, encrypted);
+
+ group = groupService.read(group.getKey());
+ assertNotEquals("testvalue", group.getPlainAttr(encrypted.getKey()).get().getValues().get(0));
+ }
+
+ @Test
public void anonymous() {
GroupService unauthenticated = clientFactory.create().getService(GroupService.class);
try {
diff --git a/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc b/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc
index 0e77562..07e1c7f 100644
--- a/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc
@@ -50,8 +50,10 @@ http://docs.oracle.com/javase/8/docs/api/java/text/DateFormat.html[DateFormat^]
*** enumeration values (mandatory)
*** enumeration labels (optional, values will be used alternatively)
** `Encrypted`
-*** secret key
+*** secret key (stored or referenced as https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-property-source-abstraction[Spring property^])
*** cipher algorithm
+*** whether transparent encryption is to be enabled, e.g. attribute values are stored as encrypted but available as
+cleartext on-demand (requires AES ciphering)
** `Binary` - it is required to provide the declared mime type
* Validator class - (optional) Java class validating the value(s) provided for attributes, see
ifeval::["{snapshotOrRelease}" == "release"]