[SYNCOPE-1815] Macro improvements (#696) (#709)
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java
index 8de1443..d685da5 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMAnyDirectoryPanelAdditionalActionLinksProvider.java
@@ -192,8 +192,7 @@
@Override
public void onClick(final AjaxRequestTarget target, final GroupTO ignore) {
- IModel<AnyWrapper<GroupTO>> formModel = new CompoundPropertyModel<>(
- new GroupWrapper(modelObject));
+ IModel<AnyWrapper<GroupTO>> formModel = new CompoundPropertyModel<>(new GroupWrapper(modelObject));
modal.setFormModel(formModel);
target.add(modal.setContent(new AnyStatusModal<>(
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java
index 003a65c..cfc8493 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java
@@ -87,7 +87,6 @@
protected void onComponentTag(final ComponentTag tag) {
tag.append("class", "scrollable-tab-content", " ");
}
-
};
if (createFlag && resourceDetailsPanel.getConnector() != null) {
@@ -100,8 +99,7 @@
protected void onUpdate(final AjaxRequestTarget target) {
resourceTO.setConnector(resourceDetailsPanel.getConnector().getModelObject());
- LoadableDetachableModel<List<ConnConfProperty>> model =
- new LoadableDetachableModel<>() {
+ LoadableDetachableModel<List<ConnConfProperty>> model = new LoadableDetachableModel<>() {
private static final long serialVersionUID = -2965284931860212687L;
diff --git a/ext/flowable/client-common-ui/src/main/java/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
similarity index 74%
rename from ext/flowable/client-common-ui/src/main/java/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.java
rename to client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
index 1231099..2cfc3cb 100644
--- a/ext/flowable/client-common-ui/src/main/java/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.ext.client.common.ui.panels;
+package org.apache.syncope.client.ui.commons.panels;
import java.text.ParseException;
import java.util.Date;
@@ -32,13 +32,9 @@
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxSpinnerFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
-import org.apache.syncope.common.lib.to.UserRequestForm;
-import org.apache.syncope.common.lib.to.UserRequestFormProperty;
-import org.apache.syncope.common.lib.to.UserRequestFormPropertyValue;
-import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+import org.apache.syncope.common.lib.form.FormProperty;
+import org.apache.syncope.common.lib.form.FormPropertyValue;
+import org.apache.syncope.common.lib.form.SyncopeForm;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Panel;
@@ -48,37 +44,33 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public abstract class UserRequestFormPanel extends Panel {
+public class SyncopeFormPanel<F extends SyncopeForm> extends Panel {
private static final long serialVersionUID = -8847854414429745216L;
- protected static final Logger LOG = LoggerFactory.getLogger(UserRequestFormPanel.class);
+ protected static final Logger LOG = LoggerFactory.getLogger(SyncopeFormPanel.class);
- public UserRequestFormPanel(final String id, final UserRequestForm form) {
- this(id, form, true);
- }
-
- public UserRequestFormPanel(final String id, final UserRequestForm form, final boolean showDetails) {
+ public SyncopeFormPanel(final String id, final F form) {
super(id);
- IModel<List<UserRequestFormProperty>> formProps = new LoadableDetachableModel<>() {
+ IModel<List<FormProperty>> formProps = new LoadableDetachableModel<>() {
private static final long serialVersionUID = 3169142472626817508L;
@Override
- protected List<UserRequestFormProperty> load() {
+ protected List<FormProperty> load() {
return form.getProperties();
}
};
- ListView<UserRequestFormProperty> propView = new ListView<>("propView", formProps) {
+ ListView<FormProperty> propView = new ListView<>("propView", formProps) {
private static final long serialVersionUID = 9101744072914090143L;
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
- protected void populateItem(final ListItem<UserRequestFormProperty> item) {
- final UserRequestFormProperty prop = item.getModelObject();
+ protected void populateItem(final ListItem<FormProperty> item) {
+ FormProperty prop = item.getModelObject();
String label = StringUtils.isBlank(prop.getName()) ? prop.getId() : prop.getName();
@@ -134,10 +126,10 @@
"value", label, new PropertyModel<String>(prop, "value"), false).
setChoiceRenderer(new MapChoiceRenderer(prop.getEnumValues().stream().
collect(Collectors.toMap(
- UserRequestFormPropertyValue::getKey,
- UserRequestFormPropertyValue::getValue)))).
+ FormPropertyValue::getKey,
+ FormPropertyValue::getValue)))).
setChoices(prop.getEnumValues().stream().
- map(UserRequestFormPropertyValue::getKey).collect(Collectors.toList()));
+ map(FormPropertyValue::getKey).collect(Collectors.toList()));
break;
case Dropdown:
@@ -145,10 +137,10 @@
"value", label, new PropertyModel<String>(prop, "value"), false).
setChoiceRenderer(new MapChoiceRenderer(prop.getDropdownValues().stream().
collect(Collectors.toMap(
- UserRequestFormPropertyValue::getKey,
- UserRequestFormPropertyValue::getValue)))).
+ FormPropertyValue::getKey,
+ FormPropertyValue::getValue)))).
setChoices(prop.getDropdownValues().stream().
- map(UserRequestFormPropertyValue::getKey).collect(Collectors.toList()));
+ map(FormPropertyValue::getKey).collect(Collectors.toList()));
break;
case Long:
@@ -193,23 +185,6 @@
}
};
- AjaxLink<String> userDetails = new AjaxLink<>("userDetails") {
-
- private static final long serialVersionUID = -4804368561204623354L;
-
- @Override
- public void onClick(final AjaxRequestTarget target) {
- viewDetails(target);
- }
- };
- MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, IdRepoEntitlement.USER_READ);
-
- boolean enabled = form.getUserTO() != null;
- userDetails.setVisible(enabled && showDetails).setEnabled(enabled);
-
add(propView);
- add(userDetails);
}
-
- protected abstract void viewDetails(AjaxRequestTarget target);
}
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.html
similarity index 80%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
copy to client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.html
index aef81e5..127037b 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
+++ b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.html
@@ -22,10 +22,6 @@
<span wicket:id="value">[value]</span>
</div>
- <div style="margin: 20px 0">
- <a href="#" alt="user details" class="btn btn-success btn-circle btn-lg" wicket:id="userDetails" wicket:message="title:userDetails">
- <i class="fas fa-eye"></i>
- </a>
- </div>
+ <wicket:child/>
</wicket:panel>
</html>
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
index 0284b4d..7b39d99 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
@@ -21,6 +21,7 @@
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.StreamReadFeature;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.json.JsonMapper;
@@ -214,21 +215,21 @@
@Override
protected void onEvent(final AjaxRequestTarget target) {
- AuditEventTO beforeEntry = beforeVersionsPanel.getModelObject() == null
+ AuditEventTO beforeEvent = beforeVersionsPanel.getModelObject() == null
? latestAuditEventTO
: beforeVersionsPanel.getModelObject();
- AuditEventTO afterEntry = afterVersionsPanel.getModelObject() == null
+ AuditEventTO afterEvent = afterVersionsPanel.getModelObject() == null
? after
- : buildAfterAuditEventTO(beforeEntry);
+ : buildAfterAuditEventTO(beforeEvent);
AuditHistoryDetails.this.addOrReplace(
- new JsonDiffPanel(toJSON(beforeEntry, reference), toJSON(afterEntry, reference)));
+ new JsonDiffPanel(toJSON(beforeEvent, reference), toJSON(afterEvent, reference)));
// change after audit entries in order to match only the ones newer than the current after one
afterVersionsPanel.setChoices(auditEntries.stream().
- filter(ae -> ae.getWhen().isAfter(beforeEntry.getWhen())
- || ae.getWhen().isEqual(beforeEntry.getWhen())).
+ filter(ae -> ae.getWhen().isAfter(beforeEvent.getWhen())
+ || ae.getWhen().isEqual(beforeEvent.getWhen())).
collect(Collectors.toList()));
// set the new after entry
- afterVersionsPanel.setModelObject(afterEntry);
+ afterVersionsPanel.setModelObject(afterEvent);
target.add(AuditHistoryDetails.this);
}
});
@@ -323,30 +324,36 @@
return output;
}
- protected Model<String> toJSON(final AuditEventTO auditEntry, final Class<T> reference) {
+ protected Model<String> toJSON(final AuditEventTO auditEvent, final Class<T> reference) {
+ if (auditEvent == null) {
+ return Model.of();
+ }
+
try {
- if (auditEntry == null) {
- return Model.of();
+ String content;
+ if (auditEvent.getBefore() == null) {
+ JsonNode output = MAPPER.readTree(auditEvent.getOutput());
+ if (output.has("entity")) {
+ content = output.get("entity").toPrettyString();
+ } else {
+ content = output.toPrettyString();
+ }
+ } else {
+ content = auditEvent.getBefore();
}
- String content = auditEntry.getBefore() == null
- ? MAPPER.readTree(auditEntry.getOutput()).get("entity") == null
- ? MAPPER.readTree(auditEntry.getOutput()).toPrettyString()
- : MAPPER.readTree(auditEntry.getOutput()).get("entity").toPrettyString()
- : auditEntry.getBefore();
T entity = MAPPER.reader().
with(StreamReadFeature.STRICT_DUPLICATE_DETECTION).
readValue(content, reference);
- if (entity instanceof UserTO) {
- UserTO userTO = (UserTO) entity;
+ if (entity instanceof UserTO userTO) {
userTO.setPassword(null);
userTO.setSecurityAnswer(null);
}
return Model.of(MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(entity));
} catch (Exception e) {
- LOG.error("While (de)serializing entity {}", auditEntry, e);
- throw new WicketRuntimeException(e);
+ LOG.error("While (de)serializing entity {}", auditEvent, e);
+ return Model.of();
}
}
}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java
index 83507a8..4da290d 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java
@@ -119,8 +119,12 @@
templateClassName = "MyLogicActions";
break;
- case IdRepoImplementationType.VALIDATOR:
- templateClassName = "MyValidator";
+ case IdRepoImplementationType.MACRO_ACTIONS:
+ templateClassName = "MyMacroActions";
+ break;
+
+ case IdRepoImplementationType.ATTR_VALUE_VALIDATOR:
+ templateClassName = "MyAttrValueValidator";
break;
case IdRepoImplementationType.RECIPIENTS_PROVIDER:
@@ -208,6 +212,20 @@
}
@Override
+ public IModel<List<String>> getMacroActions() {
+ return new LoadableDetachableModel<>() {
+
+ private static final long serialVersionUID = 5275935387613157437L;
+
+ @Override
+ protected List<String> load() {
+ return implementationRestClient.list(IdRepoImplementationType.MACRO_ACTIONS).stream().
+ map(ImplementationTO::getKey).sorted().collect(Collectors.toList());
+ }
+ };
+ }
+
+ @Override
public IModel<List<String>> getPullActions() {
return new LoadableDetachableModel<>() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java
index 51638e4..d01cde7 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java
@@ -46,6 +46,8 @@
IModel<List<String>> getReconFilterBuilders();
+ IModel<List<String>> getMacroActions();
+
IModel<List<String>> getPullActions();
IModel<List<String>> getPushActions();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
index cfec7ab..9960f1f 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
@@ -115,17 +115,18 @@
this.excluded.add("serialVersionUID");
this.excluded.add("class");
- LoadableDetachableModel<List<String>> model = new LoadableDetachableModel<>() {
+ LoadableDetachableModel<List<Field>> model = new LoadableDetachableModel<>() {
private static final long serialVersionUID = 5275935387613157437L;
@Override
- protected List<String> load() {
- List<String> result = new ArrayList<>();
+ protected List<Field> load() {
+ List<Field> result = new ArrayList<>();
if (BeanPanel.this.getDefaultModelObject() != null) {
- ReflectionUtils.doWithFields(BeanPanel.this.getDefaultModelObject().getClass(),
- field -> result.add(field.getName()),
+ ReflectionUtils.doWithFields(
+ BeanPanel.this.getDefaultModelObject().getClass(),
+ result::add,
field -> !field.isSynthetic() && !BeanPanel.this.excluded.contains(field.getName()));
}
@@ -137,7 +138,7 @@
private static final long serialVersionUID = 9101744072914090143L;
- private void setRequired(final ListItem<String> item, final boolean required) {
+ private void setRequired(final ListItem<Field> item, final boolean required) {
if (required) {
Fragment fragment = new Fragment("required", "requiredFragment", this);
fragment.add(new Label("requiredLabel", "*"));
@@ -145,7 +146,7 @@
}
}
- private void setDescription(final ListItem<String> item, final String description) {
+ private void setDescription(final ListItem<Field> item, final String description) {
Fragment fragment = new Fragment("description", "descriptionFragment", this);
fragment.add(new Label("descriptionLabel", Model.of()).add(new PopoverBehavior(
Model.<String>of(),
@@ -164,25 +165,20 @@
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
- protected void populateItem(final ListItem<String> item) {
+ protected void populateItem(final ListItem<Field> item) {
item.add(new Fragment("required", "emptyFragment", this));
item.add(new Fragment("description", "emptyFragment", this));
- String fieldName = item.getModelObject();
+ Field field = item.getModelObject();
- item.add(new Label("fieldName", new ResourceModel(fieldName, fieldName)));
-
- Field field = ReflectionUtils.findField(bean.getObject().getClass(), fieldName);
- if (field == null) {
- return;
- }
+ item.add(new Label("fieldName", new ResourceModel(field.getName(), field.getName())));
Panel panel;
SearchCondition scondAnnot = field.getAnnotation(SearchCondition.class);
if (scondAnnot != null) {
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean.getObject());
- String fiql = (String) wrapper.getPropertyValue(fieldName);
+ String fiql = (String) wrapper.getPropertyValue(field.getName());
List<SearchClause> clauses = Optional.ofNullable(fiql).
map(f -> SearchUtils.getSearchClauses(f.replaceAll(
@@ -211,7 +207,7 @@
}
Optional.ofNullable(BeanPanel.this.sCondWrapper).
- ifPresent(scw -> scw.put(fieldName, Pair.of(builder, clauses)));
+ ifPresent(scw -> scw.put(field.getName(), Pair.of(builder, clauses)));
} else if (List.class.equals(field.getType())) {
Class<?> listItemType = field.getGenericType() instanceof ParameterizedType
? (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]
@@ -243,15 +239,15 @@
}
}
- panel = new AjaxPalettePanel.Builder<>().setName(fieldName).build(
+ panel = new AjaxPalettePanel.Builder<>().setName(field.getName()).build(
"value",
- new PropertyModel<>(bean.getObject(), fieldName),
+ new PropertyModel<>(bean.getObject(), field.getName()),
new ListModel<>(choices.stream().map(SchemaTO::getKey).collect(Collectors.toList()))).
hideLabel();
} else if (listItemType.isEnum()) {
- panel = new AjaxPalettePanel.Builder<>().setName(fieldName).build(
+ panel = new AjaxPalettePanel.Builder<>().setName(field.getName()).build(
"value",
- new PropertyModel<>(bean.getObject(), fieldName),
+ new PropertyModel<>(bean.getObject(), field.getName()),
new ListModel(List.of(listItemType.getEnumConstants()))).hideLabel();
} else {
Triple<FieldPanel, Boolean, Optional<String>> single =
@@ -262,14 +258,14 @@
single.getRight().ifPresent(description -> setDescription(item, description));
panel = new MultiFieldPanel.Builder<>(
- new PropertyModel<>(bean.getObject(), fieldName)).build(
+ new PropertyModel<>(bean.getObject(), field.getName())).build(
"value",
- fieldName,
+ field.getName(),
single.getLeft()).hideLabel();
}
} else if (Map.class.equals(field.getType())) {
panel = new AjaxGridFieldPanel(
- "value", fieldName, new PropertyModel<>(bean, fieldName)).hideLabel();
+ "value", field.getName(), new PropertyModel<>(bean, field.getName())).hideLabel();
Optional.ofNullable(field.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class)).
ifPresent(annot -> setDescription(item, annot.description()));
} else {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardPanel.java
index fa19891..01cf3d6 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardPanel.java
@@ -47,11 +47,6 @@
}
@Override
- protected void onCancelInternal(final ParametersForm modelObject) {
- //do nothing
- }
-
- @Override
protected Serializable onApplyInternal(final ParametersForm modelObject) {
modelObject.getParam().setMultivalue(modelObject.getSchema().isMultivalue());
try {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
index c3470c7..44ea3c7 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/PlainSchemaDetails.java
@@ -257,7 +257,7 @@
@Override
protected List<String> load() {
- return implementationRestClient.list(IdRepoImplementationType.VALIDATOR).stream().
+ return implementationRestClient.list(IdRepoImplementationType.ATTR_VALUE_VALIDATOR).stream().
map(ImplementationTO::getKey).sorted().collect(Collectors.toList());
}
};
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/StartAtTogglePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/StartAtTogglePanel.java
index 35acde0..16a0dda 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/StartAtTogglePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/StartAtTogglePanel.java
@@ -53,18 +53,13 @@
form = new Form<>("startAtForm");
addInnerObject(form);
- final AjaxDateTimeFieldPanel startAtDate = new AjaxDateTimeFieldPanel(
+ AjaxDateTimeFieldPanel startAtDate = new AjaxDateTimeFieldPanel(
"startAtDate", "startAtDate", startAtDateModel,
FastDateFormat.getInstance(SyncopeConstants.DATE_PATTERNS[3]));
+ form.add(startAtDate.setReadOnly(true).hideLabel());
- startAtDate.setReadOnly(true).hideLabel();
- form.add(startAtDate);
-
- final AjaxCheckBoxPanel startAtCheck = new AjaxCheckBoxPanel(
+ AjaxCheckBoxPanel startAtCheck = new AjaxCheckBoxPanel(
"startAtCheck", "startAtCheck", new Model<>(false), false);
-
- form.add(startAtCheck);
-
startAtCheck.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
private static final long serialVersionUID = -1107858522700306810L;
@@ -74,6 +69,7 @@
target.add(startAtDate.setModelObject(null).setReadOnly(!startAtCheck.getModelObject()));
}
});
+ form.add(startAtCheck);
form.add(new AjaxSubmitLink("startAt", form) {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java
index 1fe177a..053aeda 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportWizardBuilder.java
@@ -138,7 +138,7 @@
return wizardModel;
}
- public class Profile extends WizardStep {
+ protected class Profile extends WizardStep {
private static final long serialVersionUID = -3043839139187792810L;
@@ -192,7 +192,7 @@
}
}
- public class Configuration extends WizardStep implements WizardModel.ICondition {
+ protected class Configuration extends WizardStep implements WizardModel.ICondition {
private static final long serialVersionUID = -785981096328637758L;
@@ -219,7 +219,7 @@
}
}
- public class Schedule extends WizardStep {
+ protected class Schedule extends WizardStep {
private static final long serialVersionUID = -785981096328637758L;
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
index 33b5639..f7c5713 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/TaskRestClient.java
@@ -27,6 +27,7 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.lib.batch.BatchRequest;
import org.apache.syncope.client.ui.commons.DateOps;
+import org.apache.syncope.common.lib.form.SyncopeForm;
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.NotificationTaskTO;
@@ -192,6 +193,10 @@
return getService(TaskService.class).read(type, taskKey, false);
}
+ public SyncopeForm getMacroTaskForm(final String taskKey) {
+ return getService(TaskService.class).getMacroTaskForm(taskKey);
+ }
+
public void delete(final TaskType type, final String taskKey) {
getService(TaskService.class).delete(type, taskKey);
}
@@ -202,8 +207,19 @@
}
public void startExecution(final String taskKey, final Date startAt, final boolean dryRun) {
- getService(TaskService.class).execute(new ExecSpecs.Builder().key(taskKey).
- startAt(DateOps.toOffsetDateTime(startAt)).dryRun(dryRun).build());
+ getService(TaskService.class).execute(
+ new ExecSpecs.Builder().key(taskKey).startAt(DateOps.toOffsetDateTime(startAt)).dryRun(dryRun).build());
+ }
+
+ public void startExecution(
+ final String taskKey,
+ final Date startAt,
+ final boolean dryRun,
+ final SyncopeForm macroTaskForm) {
+
+ getService(TaskService.class).execute(
+ new ExecSpecs.Builder().key(taskKey).startAt(DateOps.toOffsetDateTime(startAt)).dryRun(dryRun).build(),
+ macroTaskForm);
}
@Override
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java
index 56befe8..aa31489 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeDirectoryPanel.java
@@ -72,22 +72,22 @@
@SpringBean
protected CommandRestClient commandRestClient;
- protected final BaseModal<MacroTaskTO> baseModal;
-
protected final String task;
+ protected final BaseModal<MacroTaskTO> baseModal;
+
public CommandComposeDirectoryPanel(
+ final String task,
final CommandRestClient restClient,
final BaseModal<MacroTaskTO> baseModal,
- final String task,
final PageReference pageRef) {
super(BaseModal.CONTENT_ID, restClient, pageRef, false);
disableCheckBoxes();
- this.baseModal = baseModal;
this.task = task;
+ this.baseModal = baseModal;
enableUtilityButton();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder.java
index 00f9829..1d25bed 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder.java
@@ -83,6 +83,10 @@
@Override
protected Serializable onApplyInternal(final CommandWrapper modelObject) {
+ if (modelObject.getCommand().getArgs() == null) {
+ throw new IllegalArgumentException("Incorrect Command definition");
+ }
+
MacroTaskTO taskTO = taskRestClient.readTask(TaskType.MACRO, task);
if (modelObject.isNew()) {
taskTO.getCommands().add(modelObject.getCommand());
@@ -112,7 +116,6 @@
public Profile(final CommandWrapper command) {
this.command = command;
- MacroTaskTO taskTO = taskRestClient.readTask(TaskType.MACRO, task);
AutoCompleteSettings settings = new AutoCompleteSettings();
settings.setShowCompleteListOnFocusGain(false);
@@ -127,8 +130,7 @@
protected Iterator<String> getChoices(final String input) {
return commands.getObject().stream().
map(ImplementationTO::getKey).
- filter(cmd -> cmd.contains(input)
- && taskTO.getCommands().stream().noneMatch(c -> c.getKey().equals(cmd))).
+ filter(cmd -> cmd.contains(input)).
sorted().iterator();
}
};
@@ -140,8 +142,12 @@
@Override
protected void onUpdate(final AjaxRequestTarget target) {
- CommandTO cmd = commandRestClient.read(command.getCommand().getKey());
- command.getCommand().setArgs(cmd.getArgs());
+ try {
+ CommandTO cmd = commandRestClient.read(command.getCommand().getKey());
+ command.getCommand().setArgs(cmd.getArgs());
+ } catch (Exception e) {
+ LOG.error("While attempting to read Command {}", command.getCommand().getKey(), e);
+ }
}
});
add(args);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java
new file mode 100644
index 0000000..3d6210f
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java
@@ -0,0 +1,222 @@
+/*
+ * 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.client.console.tasks;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.panels.AbstractModalPanel;
+import org.apache.syncope.client.console.rest.TaskRestClient;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
+import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxGridFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.common.lib.to.FormPropertyDefTO;
+import org.apache.syncope.common.lib.to.MacroTaskTO;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxButton;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.model.util.ListModel;
+import org.apache.wicket.spring.injection.annot.SpringBean;
+
+public class FormPropertyDefsPanel extends AbstractModalPanel<MacroTaskTO> {
+
+ private static final long serialVersionUID = 6991001927367507753L;
+
+ @SpringBean
+ protected TaskRestClient taskRestClient;
+
+ protected final MacroTaskTO task;
+
+ protected final IModel<List<FormPropertyDefTO>> model;
+
+ public FormPropertyDefsPanel(
+ final MacroTaskTO task,
+ final BaseModal<MacroTaskTO> modal,
+ final PageReference pageRef) {
+
+ super(modal, pageRef);
+ this.task = task;
+
+ WebMarkupContainer propertyDefContainer = new WebMarkupContainer("propertyDefContainer");
+ propertyDefContainer.setOutputMarkupId(true);
+ add(propertyDefContainer);
+
+ model = new ListModel<>(new ArrayList<>());
+ model.getObject().addAll(task.getFormPropertyDefs());
+
+ ListView<FormPropertyDefTO> propertyDefs = new ListView<>("propertyDefs", model) {
+
+ private static final long serialVersionUID = 1814616131938968887L;
+
+ @Override
+ protected void populateItem(final ListItem<FormPropertyDefTO> item) {
+ FormPropertyDefTO fpd = item.getModelObject();
+
+ AjaxTextFieldPanel key = new AjaxTextFieldPanel(
+ "key",
+ "key",
+ new PropertyModel<>(fpd, "key"),
+ true);
+ item.add(key.setRequired(true).hideLabel());
+
+ AjaxTextFieldPanel name = new AjaxTextFieldPanel(
+ "name",
+ "name",
+ new PropertyModel<>(fpd, "name"),
+ true);
+ item.add(name.setRequired(true).hideLabel());
+
+ AjaxCheckBoxPanel readable = new AjaxCheckBoxPanel(
+ "readable",
+ "readable",
+ new PropertyModel<>(fpd, "readable"),
+ true);
+ item.add(readable.hideLabel());
+
+ AjaxCheckBoxPanel writable = new AjaxCheckBoxPanel(
+ "writable",
+ "writable",
+ new PropertyModel<>(fpd, "writable"),
+ true);
+ item.add(writable.hideLabel());
+
+ AjaxCheckBoxPanel required = new AjaxCheckBoxPanel(
+ "required",
+ "required",
+ new PropertyModel<>(fpd, "required"),
+ true);
+ item.add(required.hideLabel());
+
+ AjaxDropDownChoicePanel<FormPropertyType> type = new AjaxDropDownChoicePanel<>(
+ "type",
+ "type",
+ new PropertyModel<>(fpd, "type"),
+ true);
+ type.setChoices(List.of(FormPropertyType.values())).setNullValid(false);
+ item.add(type.setRequired(true).hideLabel());
+
+ AjaxTextFieldPanel datePattern = new AjaxTextFieldPanel(
+ "datePattern",
+ "datePattern",
+ new PropertyModel<>(fpd, "datePattern"),
+ true);
+ datePattern.setEnabled(fpd.getType() == FormPropertyType.Date);
+ item.add(datePattern.hideLabel().setOutputMarkupId(true));
+
+ AjaxGridFieldPanel<String, String> enumValues = new AjaxGridFieldPanel<>(
+ "enumValues",
+ "enumValues",
+ new PropertyModel<>(fpd, "enumValues"));
+ enumValues.setEnabled(fpd.getType() == FormPropertyType.Enum);
+ item.add(enumValues.hideLabel().setOutputMarkupId(true));
+
+ type.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ switch (type.getModelObject()) {
+ case Date -> {
+ datePattern.setEnabled(true);
+ enumValues.setEnabled(false);
+ fpd.getEnumValues().clear();
+ }
+
+ case Enum -> {
+ datePattern.setEnabled(false);
+ enumValues.setEnabled(true);
+ }
+
+ default -> {
+ datePattern.setEnabled(false);
+ enumValues.setEnabled(false);
+ fpd.getEnumValues().clear();
+ }
+ }
+
+ target.add(datePattern);
+ target.add(enumValues);
+ }
+ });
+
+ ActionsPanel<Serializable> actions = new ActionsPanel<>("toRemove", null);
+ actions.add(new ActionLink<>() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+ model.getObject().remove(item.getIndex());
+ item.getParent().removeAll();
+ target.add(propertyDefContainer);
+ }
+ }, ActionLink.ActionType.DELETE, StringUtils.EMPTY, true).hideLabel();
+ item.add(actions);
+ }
+ };
+ propertyDefs.setReuseItems(true);
+ propertyDefContainer.add(propertyDefs);
+
+ IndicatingAjaxButton addPropertyDef = new IndicatingAjaxButton("addPropertyDef") {
+
+ private static final long serialVersionUID = -4804368561204623354L;
+
+ @Override
+ protected void onSubmit(final AjaxRequestTarget target) {
+ model.getObject().add(new FormPropertyDefTO());
+ target.add(propertyDefContainer);
+ }
+ };
+ addPropertyDef.setDefaultFormProcessing(false);
+ propertyDefContainer.add(addPropertyDef);
+ }
+
+ @Override
+ public void onSubmit(final AjaxRequestTarget target) {
+ task.getFormPropertyDefs().clear();
+ task.getFormPropertyDefs().addAll(model.getObject());
+ try {
+ taskRestClient.update(TaskType.MACRO, task);
+
+ SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+ modal.close(target);
+ } catch (Exception e) {
+ LOG.error("While updating Macro Task {}", task.getKey(), e);
+ SyncopeConsoleSession.get().onException(e);
+ }
+ ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java
index c314562..345fda7 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.java
@@ -18,6 +18,8 @@
*/
package org.apache.syncope.client.console.tasks;
+import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
@@ -25,13 +27,19 @@
import org.apache.syncope.client.console.rest.CommandRestClient;
import org.apache.syncope.client.console.rest.TaskRestClient;
import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.BooleanPropertyColumn;
+import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.Action;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
import org.apache.syncope.common.lib.to.MacroTaskTO;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.event.IEvent;
+import org.apache.wicket.event.IEventSink;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
@@ -43,15 +51,55 @@
private static final long serialVersionUID = -6247673131495530094L;
+ protected class ExecModalEventSink implements IEventSink, Serializable {
+
+ private static final long serialVersionUID = -5961049309874978659L;
+
+ @Override
+ public void onEvent(final IEvent<?> event) {
+ if (event.getPayload() instanceof AjaxWizard.NewItemCancelEvent
+ || event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
+
+ AjaxWizard.NewItemEvent<?> nie = AjaxWizard.NewItemEvent.class.cast(event.getPayload());
+ nie.getTarget().ifPresent(execModal::close);
+ }
+
+ MacroTaskDirectoryPanel.this.onEvent(event);
+ }
+ }
+
@SpringBean
protected CommandRestClient commandRestClient;
+ protected final BaseModal<MacroTaskTO> formPropertyDefModal = new BaseModal<>(Constants.OUTER);
+
+ protected final BaseModal<MacroTaskTO> execModal;
+
public MacroTaskDirectoryPanel(
final TaskRestClient restClient,
final MultilevelPanel mlp,
final PageReference pageRef) {
super(MultilevelPanel.FIRST_LEVEL_ID, restClient, null, mlp, TaskType.MACRO, new MacroTaskTO(), pageRef, true);
+
+ formPropertyDefModal.size(Modal.Size.Extra_large);
+ formPropertyDefModal.addSubmitButton();
+ setWindowClosedReloadCallback(formPropertyDefModal);
+ addOuterObject(formPropertyDefModal);
+
+ execModal = new BaseModal<>(Constants.OUTER) {
+
+ private static final long serialVersionUID = 389935548143327858L;
+
+ @Override
+ protected void onConfigure() {
+ super.onConfigure();
+ setFooterVisible(false);
+ }
+ };
+ execModal.size(Modal.Size.Large);
+ setWindowClosedReloadCallback(execModal);
+ addOuterObject(execModal);
}
@Override
@@ -79,6 +127,36 @@
}
@Override
+ public ActionsPanel<MacroTaskTO> getActions(final IModel<MacroTaskTO> model) {
+ ActionsPanel<MacroTaskTO> panel = super.getActions(model);
+
+ panel.getActions().removeIf(action -> action.getType() == ActionLink.ActionType.EXECUTE);
+
+ Action<MacroTaskTO> execute = new Action<>(new ActionLink<>() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target, final MacroTaskTO ignore) {
+ MacroTaskExecWizardBuilder wb = new MacroTaskExecWizardBuilder(model.getObject(), restClient, pageRef);
+ wb.setEventSink(new ExecModalEventSink());
+
+ target.add(execModal.setContent(wb.build(BaseModal.CONTENT_ID, AjaxWizard.Mode.EDIT)));
+
+ execModal.header(new StringResourceModel(
+ "exec", MacroTaskDirectoryPanel.this, Model.of(model.getObject())));
+ execModal.show(true);
+ }
+ }, ActionLink.ActionType.EXECUTE);
+ execute.setEntitlements(IdRepoEntitlement.TASK_EXECUTE);
+ execute.setOnConfirm(false);
+
+ panel.add(panel.getActions().size() - 1, execute);
+
+ return panel;
+ }
+
+ @Override
protected void addFurtherActions(final ActionsPanel<MacroTaskTO> panel, final IModel<MacroTaskTO> model) {
panel.add(new ActionLink<>() {
@@ -87,12 +165,28 @@
@Override
public void onClick(final AjaxRequestTarget target, final MacroTaskTO ignore) {
target.add(modal.setContent(new CommandComposeDirectoryPanel(
- commandRestClient, modal, model.getObject().getKey(), pageRef)));
+ model.getObject().getKey(), commandRestClient, modal, pageRef)));
modal.header(new StringResourceModel(
"command.conf", MacroTaskDirectoryPanel.this, Model.of(model.getObject())));
modal.show(true);
}
}, ActionLink.ActionType.COMPOSE, IdRepoEntitlement.TASK_UPDATE);
+
+ panel.add(new ActionLink<>() {
+
+ private static final long serialVersionUID = -3722207913631435501L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target, final MacroTaskTO ignore) {
+ model.setObject(restClient.readTask(TaskType.MACRO, model.getObject().getKey()));
+ target.add(formPropertyDefModal.setContent(
+ new FormPropertyDefsPanel(model.getObject(), formPropertyDefModal, pageRef)));
+
+ formPropertyDefModal.header(new StringResourceModel(
+ "form.def", MacroTaskDirectoryPanel.this, Model.of(model.getObject())));
+ formPropertyDefModal.show(true);
+ }
+ }, ActionLink.ActionType.MAPPING, IdRepoEntitlement.TASK_UPDATE);
}
}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder.java
new file mode 100644
index 0000000..47c980b
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder.java
@@ -0,0 +1,123 @@
+/*
+ * 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.client.console.tasks;
+
+import java.io.Serializable;
+import java.util.Date;
+import org.apache.commons.lang3.time.FastDateFormat;
+import org.apache.syncope.client.console.rest.TaskRestClient;
+import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder;
+import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
+import org.apache.syncope.client.ui.commons.panels.SyncopeFormPanel;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.form.SyncopeForm;
+import org.apache.syncope.common.lib.to.MacroTaskTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.wizard.WizardModel;
+import org.apache.wicket.extensions.wizard.WizardStep;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+
+public class MacroTaskExecWizardBuilder extends BaseAjaxWizardBuilder<MacroTaskTO> {
+
+ private static final long serialVersionUID = 3318576575286024205L;
+
+ protected final TaskRestClient taskRestClient;
+
+ protected final IModel<SyncopeForm> formModel = Model.of();
+
+ protected final Model<Date> startAtDateModel = new Model<>();
+
+ protected final Model<Boolean> dryRunModel = new Model<>(false);
+
+ public MacroTaskExecWizardBuilder(
+ final MacroTaskTO defaultItem,
+ final TaskRestClient taskRestClient,
+ final PageReference pageRef) {
+
+ super(defaultItem, pageRef);
+ this.taskRestClient = taskRestClient;
+ }
+
+ @Override
+ protected Serializable onApplyInternal(final MacroTaskTO modelObject) {
+ if (formModel.getObject() == null) {
+ taskRestClient.startExecution(modelObject.getKey(),
+ startAtDateModel.getObject(),
+ dryRunModel.getObject());
+ } else {
+ taskRestClient.startExecution(modelObject.getKey(),
+ startAtDateModel.getObject(),
+ dryRunModel.getObject(),
+ formModel.getObject());
+ }
+
+ return null;
+ }
+
+ @Override
+ protected WizardModel buildModelSteps(final MacroTaskTO modelObject, final WizardModel wizardModel) {
+ if (!modelObject.getFormPropertyDefs().isEmpty()) {
+ formModel.setObject(taskRestClient.getMacroTaskForm(modelObject.getKey()));
+ wizardModel.add(new Form());
+ }
+ wizardModel.add(new StartAt());
+ return wizardModel;
+ }
+
+ protected class Form extends WizardStep {
+
+ private static final long serialVersionUID = 7352192594863229013L;
+
+ protected Form() {
+ add(new SyncopeFormPanel<>("form", formModel.getObject()));
+ }
+ }
+
+ protected class StartAt extends WizardStep {
+
+ private static final long serialVersionUID = -961082324376783538L;
+
+ protected StartAt() {
+ AjaxDateTimeFieldPanel startAtDate = new AjaxDateTimeFieldPanel(
+ "startAtDate", "startAtDate", startAtDateModel,
+ FastDateFormat.getInstance(SyncopeConstants.DATE_PATTERNS[3]));
+ add(startAtDate.setReadOnly(true).hideLabel());
+
+ AjaxCheckBoxPanel startAtCheck = new AjaxCheckBoxPanel(
+ "startAtCheck", "startAtCheck", new Model<>(false), false);
+ startAtCheck.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
+
+ private static final long serialVersionUID = -1107858522700306810L;
+
+ @Override
+ protected void onUpdate(final AjaxRequestTarget target) {
+ target.add(startAtDate.setModelObject(null).setReadOnly(!startAtCheck.getModelObject()));
+ }
+ });
+ add(startAtCheck);
+
+ add(new AjaxCheckBoxPanel("dryRun", "dryRun", dryRunModel, false));
+ }
+ }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
index 3f2fb17..e7d82c6 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
@@ -134,6 +134,9 @@
private final IModel<List<String>> reconFilterBuilders = SyncopeWebApplication.get().
getImplementationInfoProvider().getReconFilterBuilders();
+ private final IModel<List<String>> macroActions = SyncopeWebApplication.get().
+ getImplementationInfoProvider().getMacroActions();
+
private final IModel<List<String>> pullActions = SyncopeWebApplication.get().
getImplementationInfoProvider().getPullActions();
@@ -176,9 +179,8 @@
WebMarkupContainer macroTaskSpecifics = new WebMarkupContainer("macroTaskSpecifics");
add(macroTaskSpecifics.setRenderBodyOnly(true));
- AjaxSearchFieldPanel realm =
- new AjaxSearchFieldPanel("realm", "realm",
- new PropertyModel<>(taskTO, "realm"), settings) {
+ AjaxSearchFieldPanel realm = new AjaxSearchFieldPanel(
+ "realm", "realm", new PropertyModel<>(taskTO, "realm"), settings) {
private static final long serialVersionUID = -6390474600233486704L;
@@ -189,7 +191,6 @@
: List.<String>of()).iterator();
}
};
-
if (taskTO instanceof MacroTaskTO) {
realm.addRequiredLabel();
if (StringUtils.isBlank(MacroTaskTO.class.cast(taskTO).getRealm())) {
@@ -199,6 +200,10 @@
}
macroTaskSpecifics.add(realm);
+ macroTaskSpecifics.add(new AjaxDropDownChoicePanel<>(
+ "macroActions", "macroActions", new PropertyModel<>(taskTO, "macroActions"), false).
+ setChoices(macroActions));
+
AjaxCheckBoxPanel continueOnError = new AjaxCheckBoxPanel(
"continueOnError", "continueOnError", new PropertyModel<>(taskTO, "continueOnError"), false);
macroTaskSpecifics.add(continueOnError);
@@ -243,10 +248,8 @@
@Override
protected void onUpdate(final AjaxRequestTarget target) {
- reconFilterBuilder.setEnabled(
- pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
- reconFilterBuilder.setRequired(
- pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
+ reconFilterBuilder.setEnabled(pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
+ reconFilterBuilder.setRequired(pullMode.getModelObject() == PullMode.FILTERED_RECONCILIATION);
target.add(reconFilterBuilder);
}
});
@@ -337,8 +340,7 @@
provisioningTaskSpecifics.add(matchingRule);
AjaxDropDownChoicePanel<UnmatchingRule> unmatchingRule = new AjaxDropDownChoicePanel<>(
- "unmatchingRule", "unmatchingRule", new PropertyModel<>(taskTO, "unmatchingRule"),
- false);
+ "unmatchingRule", "unmatchingRule", new PropertyModel<>(taskTO, "unmatchingRule"), false);
unmatchingRule.setChoices(List.of(UnmatchingRule.values()));
provisioningTaskSpecifics.add(unmatchingRule);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
index e4a1bc2..3a9f51b 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java
@@ -59,8 +59,8 @@
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.ProvisioningTaskTO;
-import org.apache.syncope.common.lib.to.PullTaskTO;
import org.apache.syncope.common.lib.to.ReportTO;
+import org.apache.syncope.common.lib.to.TaskTO;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.common.lib.types.JobAction;
import org.apache.syncope.common.lib.types.JobType;
@@ -420,29 +420,48 @@
break;
case TASK:
- ProvisioningTaskTO schedTaskTO;
- try {
- schedTaskTO = taskRestClient.readTask(TaskType.PULL, jobTO.getRefKey());
- } catch (Exception e) {
- LOG.debug("Failed to read {} as {}, attempting {}",
- jobTO.getRefKey(), TaskType.PULL, TaskType.PUSH, e);
- schedTaskTO = taskRestClient.readTask(TaskType.PUSH, jobTO.getRefKey());
+ TaskType taskType = null;
+ if (jobTO.getRefDesc().startsWith("SCHEDULED")) {
+ taskType = TaskType.SCHEDULED;
+ } else if (jobTO.getRefDesc().startsWith("PULL")) {
+ taskType = TaskType.PULL;
+ } else if (jobTO.getRefDesc().startsWith("PUSH")) {
+ taskType = TaskType.PUSH;
+ } else if (jobTO.getRefDesc().startsWith("MACRO")) {
+ taskType = TaskType.MACRO;
+ }
+ if (taskType == null) {
+ break;
}
- SchedTaskWizardBuilder<ProvisioningTaskTO> swb =
- new SchedTaskWizardBuilder<>(schedTaskTO instanceof PullTaskTO
- ? TaskType.PULL : TaskType.PUSH, schedTaskTO,
- realmRestClient, taskRestClient, pageRef);
- swb.setEventSink(AvailableJobsPanel.this);
+ TaskTO taskTO = null;
+ try {
+ taskTO = taskRestClient.readTask(taskType, jobTO.getRefKey());
+ } catch (Exception e) {
+ LOG.debug("Failed to read {} as {}", jobTO.getRefKey(), taskType, e);
+ }
+ if (taskTO == null) {
+ break;
+ }
- target.add(jobModal.setContent(swb.build(BaseModal.CONTENT_ID, AjaxWizard.Mode.EDIT)));
+ if (taskTO instanceof ProvisioningTaskTO) {
+ SchedTaskWizardBuilder<ProvisioningTaskTO> swb =
+ new SchedTaskWizardBuilder<>(taskType, (ProvisioningTaskTO) taskTO,
+ realmRestClient, taskRestClient, pageRef);
+ swb.setEventSink(AvailableJobsPanel.this);
- jobModal.header(new StringResourceModel(
- "any.edit",
- AvailableJobsPanel.this,
- new Model<>(schedTaskTO)));
+ target.add(jobModal.setContent(swb.build(BaseModal.CONTENT_ID, AjaxWizard.Mode.EDIT)));
- jobModal.show(true);
+ jobModal.header(new StringResourceModel(
+ "any.edit",
+ AvailableJobsPanel.this,
+ new Model<>(taskTO)));
+
+ jobModal.show(true);
+ } else {
+ SyncopeConsoleSession.get().info("Unsupported task type: " + taskType.name());
+ ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ }
break;
default:
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AbstractMappingPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AbstractMappingPanel.java
index 60fd5d0..b555049 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AbstractMappingPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/mapping/AbstractMappingPanel.java
@@ -22,6 +22,7 @@
import de.agilecoders.wicket.core.markup.html.bootstrap.components.PopoverConfig;
import de.agilecoders.wicket.core.markup.html.bootstrap.components.TooltipConfig;
import java.io.Serializable;
+import java.util.Comparator;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
@@ -52,6 +53,45 @@
private static final long serialVersionUID = -8295587900937040104L;
+ protected static final Comparator<Item> ITEM_COMPARATOR = (left, right) -> {
+ int compared;
+ if (left == null && right == null) {
+ compared = 0;
+ } else if (left == null) {
+ compared = 1;
+ } else if (right == null) {
+ compared = -1;
+ } else if (left.isConnObjectKey()) {
+ compared = -1;
+ } else if (right.isConnObjectKey()) {
+ compared = 1;
+ } else if (left.isPassword()) {
+ compared = -1;
+ } else if (right.isPassword()) {
+ compared = 1;
+ } else if (left.getPurpose() == MappingPurpose.BOTH && right.getPurpose() != MappingPurpose.BOTH) {
+ compared = -1;
+ } else if (left.getPurpose() != MappingPurpose.BOTH && right.getPurpose() == MappingPurpose.BOTH) {
+ compared = 1;
+ } else if (left.getPurpose() == MappingPurpose.PROPAGATION
+ && (right.getPurpose() == MappingPurpose.PULL
+ || right.getPurpose() == MappingPurpose.NONE)) {
+ compared = -1;
+ } else if (left.getPurpose() == MappingPurpose.PULL
+ && right.getPurpose() == MappingPurpose.PROPAGATION) {
+ compared = 1;
+ } else if (left.getPurpose() == MappingPurpose.PULL
+ && right.getPurpose() == MappingPurpose.NONE) {
+ compared = -1;
+ } else if (left.getPurpose() == MappingPurpose.NONE
+ && right.getPurpose() != MappingPurpose.NONE) {
+ compared = 1;
+ } else {
+ compared = left.getIntAttrName().compareTo(right.getIntAttrName());
+ }
+ return compared;
+ };
+
protected final Label connObjectKeyLabel;
protected final Label passwordLabel;
@@ -131,44 +171,7 @@
mandatoryHeader.add(Constants.getJEXLPopover(this, TooltipConfig.Placement.bottom));
mappingContainer.add(mandatoryHeader);
- model.getObject().sort((left, right) -> {
- int compared;
- if (left == null && right == null) {
- compared = 0;
- } else if (left == null) {
- compared = 1;
- } else if (right == null) {
- compared = -1;
- } else if (left.isConnObjectKey()) {
- compared = -1;
- } else if (right.isConnObjectKey()) {
- compared = 1;
- } else if (left.isPassword()) {
- compared = -1;
- } else if (right.isPassword()) {
- compared = 1;
- } else if (left.getPurpose() == MappingPurpose.BOTH && right.getPurpose() != MappingPurpose.BOTH) {
- compared = -1;
- } else if (left.getPurpose() != MappingPurpose.BOTH && right.getPurpose() == MappingPurpose.BOTH) {
- compared = 1;
- } else if (left.getPurpose() == MappingPurpose.PROPAGATION
- && (right.getPurpose() == MappingPurpose.PULL
- || right.getPurpose() == MappingPurpose.NONE)) {
- compared = -1;
- } else if (left.getPurpose() == MappingPurpose.PULL
- && right.getPurpose() == MappingPurpose.PROPAGATION) {
- compared = 1;
- } else if (left.getPurpose() == MappingPurpose.PULL
- && right.getPurpose() == MappingPurpose.NONE) {
- compared = -1;
- } else if (left.getPurpose() == MappingPurpose.NONE
- && right.getPurpose() != MappingPurpose.NONE) {
- compared = 1;
- } else {
- compared = left.getIntAttrName().compareTo(right.getIntAttrName());
- }
- return compared;
- });
+ model.getObject().sort(ITEM_COMPARATOR);
mappings = new ListView<>("mappings", model) {
@@ -289,7 +292,6 @@
@Override
public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
model.getObject().remove(item.getIndex());
-
item.getParent().removeAll();
target.add(AbstractMappingPanel.this);
}
@@ -424,7 +426,7 @@
* @param connObjectKey connObjectKey checkbox.
* @param password password checkbox.
*/
- private static void setConnObjectKey(final AjaxCheckBoxPanel connObjectKey, final AjaxCheckBoxPanel password) {
+ protected void setConnObjectKey(final AjaxCheckBoxPanel connObjectKey, final AjaxCheckBoxPanel password) {
if (password.getModelObject()) {
connObjectKey.setReadOnly(true);
connObjectKey.setModelObject(false);
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyValidator.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyAttrValueValidator.groovy
similarity index 92%
rename from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyValidator.groovy
rename to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyAttrValueValidator.groovy
index b0c3e37..9e9a79d 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyValidator.groovy
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyAttrValueValidator.groovy
@@ -17,12 +17,12 @@
* under the License.
*/
import groovy.transform.CompileStatic
-import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator
+import org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValueValidator
import org.apache.syncope.core.persistence.api.entity.PlainAttrValue
import org.apache.syncope.core.persistence.api.entity.PlainSchema
@CompileStatic
-class MyValidator implements Validator {
+class MyAttrValueValidator implements PlainAttrValueValidator {
@Override
void setSchema(PlainSchema schema) {
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy
index 3b59310..3dd7c7f 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy
@@ -1,4 +1,3 @@
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -19,7 +18,7 @@
*/
import groovy.transform.CompileStatic
import org.apache.syncope.common.lib.command.CommandArgs
-import org.apache.syncope.core.logic.api.Command
+import org.apache.syncope.core.provisioning.api.macro.Command
@CompileStatic
class MyCommand implements Command<CommandArgs> {
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy
new file mode 100644
index 0000000..c920b03
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+import groovy.transform.CompileStatic
+import java.util.Map
+import javax.xml.bind.ValidationException
+import org.apache.syncope.common.lib.command.CommandArgs
+import org.apache.syncope.common.lib.form.MacroTaskForm
+import org.apache.syncope.core.provisioning.api.macro.Command
+import org.apache.syncope.core.provisioning.api.macro.MacroActions
+
+@CompileStatic
+class MyMacroActions implements MacroActions {
+
+ @Override
+ void validate(MacroTaskForm macroTaskForm) throws ValidationException {
+ }
+
+ @Override
+ Map<String, String> getDropdownValues(String formProperty) {
+ return Map.of();
+ }
+
+ @Override
+ void beforeAll() {
+ }
+
+ @Override
+ void beforeCommand(Command<CommandArgs> command, CommandArgs args) {
+ }
+
+ @Override
+ void afterCommand(Command<CommandArgs> command, CommandArgs args, String output) {
+ }
+
+ @Override
+ StringBuilder afterAll(StringBuilder output) {
+ return output;
+ }
+}
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy
index 88a297f..9ad6281 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPropagationActions.groovy
@@ -1,4 +1,3 @@
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyProvisionSorter.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyProvisionSorter.groovy
index f9d9bb7..6605e7e 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyProvisionSorter.groovy
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyProvisionSorter.groovy
@@ -1,4 +1,3 @@
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPushActions.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPushActions.groovy
index 9d4cbeb..63ed115 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPushActions.groovy
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPushActions.groovy
@@ -1,4 +1,3 @@
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder$Profile.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder$Profile.html
index e76dc6a..ba21e2f 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder$Profile.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/CommandComposeWizardBuilder$Profile.html
@@ -20,7 +20,7 @@
<wicket:panel>
<div class="form-group">
<label class="form-label" for="command"><wicket:message key="command"/></label>
- <input type="text" class="form-control col-xs-4" wicket:id="command"/>
+ <input type="text" class="form-control col-xs-4" wicket:id="command"/>
</div>
</wicket:panel>
</html>
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html
new file mode 100644
index 0000000..f40c2b5
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html
@@ -0,0 +1,84 @@
+<!--
+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.
+-->
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+ <wicket:extend>
+ <div class="table-responsive no-padding">
+ <table id="mappings"
+ class="table table-hover"
+ style="font-size: 1em;margin-top:2px;"
+ wicket:id="propertyDefContainer">
+ <tbody>
+ <tr>
+ <th><wicket:message key="key"/></th>
+ <th><wicket:message key="name"/></th>
+ <th><wicket:message key="type"/></th>
+ <th><wicket:message key="readable"/></th>
+ <th><wicket:message key="writable"/></th>
+ <th><wicket:message key="required"/></th>
+ <th><wicket:message key="datePattern"/></th>
+ <th><wicket:message key="enumValues"/></th>
+ <th></th>
+ </tr>
+
+ <tr wicket:id="propertyDefs">
+ <td>
+ <span wicket:id="key"/>
+ </td>
+ <td>
+ <span wicket:id="name"/>
+ </td>
+ <td>
+ <span wicket:id="type"/>
+ </td>
+ <td>
+ <span wicket:id="readable"/>
+ </td>
+ <td>
+ <span wicket:id="writable"/>
+ </td>
+ <td>
+ <span wicket:id="required"/>
+ </td>
+ <td>
+ <span wicket:id="datePattern"/>
+ </td>
+ <td>
+ <span wicket:id="enumValues"/>
+ </td>
+ <td>
+ <div id="inline-actions">
+ <span wicket:id="toRemove"/>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+
+ <tfoot>
+ <tr>
+ <td colspan="10" style="padding: 5px; text-align: right">
+ <button type="submit" class="btn btn-success btn-circle btn-lg" wicket:id="addPropertyDef">
+ <i class="fa fa-plus"></i>
+ </button>
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+ </div>
+ </wicket:extend>
+</html>
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties
similarity index 86%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties
index 5a9cc2d..8ba3a3a 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties
@@ -14,5 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
+type=Type
+readable=Readable
+writable=Writable
+required=Required
+datePattern=Date pattern
+enumValues=Enum values
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties
similarity index 86%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties
index 5a9cc2d..8ba3a3a 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties
@@ -14,5 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
+type=Type
+readable=Readable
+writable=Writable
+required=Required
+datePattern=Date pattern
+enumValues=Enum values
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties
similarity index 86%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties
index 5a9cc2d..62a3a2d 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties
@@ -14,5 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
+type=Tipo
+readable=Lettura
+writable=Scrittura
+required=Obbligatorio
+datePattern=Modello data
+enumValues=Valori enum
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties
similarity index 86%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties
index 5a9cc2d..8ba3a3a 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties
@@ -14,5 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
+type=Type
+readable=Readable
+writable=Writable
+required=Required
+datePattern=Date pattern
+enumValues=Enum values
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties
similarity index 86%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties
index 5a9cc2d..8ba3a3a 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties
@@ -14,5 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
+type=Type
+readable=Readable
+writable=Writable
+required=Required
+datePattern=Date pattern
+enumValues=Enum values
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties
similarity index 86%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties
index 5a9cc2d..8ba3a3a 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties
@@ -14,5 +14,9 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
+type=Type
+readable=Readable
+writable=Writable
+required=Required
+datePattern=Date pattern
+enumValues=Enum values
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.properties
index ed52486..0595325 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel.properties
@@ -19,3 +19,7 @@
continueOnError=Continue on error
saveExecs=Save executions
any.new=New Macro
+macroActions=Macro Actions
+form.def=Form definition
+mapping.title=form properties
+exec=Exec Macro ${name}
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_fr_CA.properties
index ed52486..0595325 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_fr_CA.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_fr_CA.properties
@@ -19,3 +19,7 @@
continueOnError=Continue on error
saveExecs=Save executions
any.new=New Macro
+macroActions=Macro Actions
+form.def=Form definition
+mapping.title=form properties
+exec=Exec Macro ${name}
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_it.properties
index 006bf2d..442c5f3 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_it.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_it.properties
@@ -19,3 +19,7 @@
continueOnError=Continuare in caso di errore
saveExecs=Salvare esecuzioni
any.new=Nuova Macro
+macroActions=Azioni macro
+form.def=Definizione della form
+mapping.title=propriet\u00e0 della form
+exec=Esegui Macro ${name}
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ja.properties
index ed52486..0595325 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ja.properties
@@ -19,3 +19,7 @@
continueOnError=Continue on error
saveExecs=Save executions
any.new=New Macro
+macroActions=Macro Actions
+form.def=Form definition
+mapping.title=form properties
+exec=Exec Macro ${name}
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_pt_BR.properties
index ed52486..0595325 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_pt_BR.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_pt_BR.properties
@@ -19,3 +19,7 @@
continueOnError=Continue on error
saveExecs=Save executions
any.new=New Macro
+macroActions=Macro Actions
+form.def=Form definition
+mapping.title=form properties
+exec=Exec Macro ${name}
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ru.properties
index ed52486..0595325 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ru.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskDirectoryPanel_ru.properties
@@ -19,3 +19,7 @@
continueOnError=Continue on error
saveExecs=Save executions
any.new=New Macro
+macroActions=Macro Actions
+form.def=Form definition
+mapping.title=form properties
+exec=Exec Macro ${name}
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$Form.html
similarity index 73%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$Form.html
index aef81e5..d684b5e 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$Form.html
@@ -18,14 +18,6 @@
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
- <div wicket:id="propView">
- <span wicket:id="value">[value]</span>
- </div>
-
- <div style="margin: 20px 0">
- <a href="#" alt="user details" class="btn btn-success btn-circle btn-lg" wicket:id="userDetails" wicket:message="title:userDetails">
- <i class="fas fa-eye"></i>
- </a>
- </div>
+ <span wicket:id="form"/>
</wicket:panel>
</html>
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt.html
similarity index 74%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt.html
index aef81e5..ad9b884 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt.html
@@ -18,14 +18,12 @@
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<wicket:panel>
- <div wicket:id="propView">
- <span wicket:id="value">[value]</span>
+ <div class="form-group">
+ <span wicket:id="startAtCheck"/>
+ <span wicket:id="startAtDate"/>
</div>
-
- <div style="margin: 20px 0">
- <a href="#" alt="user details" class="btn btn-success btn-circle btn-lg" wicket:id="userDetails" wicket:message="title:userDetails">
- <i class="fas fa-eye"></i>
- </a>
+ <div class="input-group">
+ <span wicket:id="dryRun"/>
</div>
</wicket:panel>
</html>
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt.properties
similarity index 92%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt.properties
index 450ff50..65e6c54 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt.properties
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=User details
-userForm=Edit User
+startAtCheck=Specify a starting date
+dryRun=Dry Run
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_fr_CA.properties
similarity index 91%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_fr_CA.properties
index 450ff50..e7e198d 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_fr_CA.properties
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=User details
-userForm=Edit User
+startAtCheck=Pr\u00e9ciser date de d\u00e9but
+dryRun=Dry Run
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_it.properties
similarity index 90%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_it.properties
index 450ff50..32e399e 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_it.properties
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=User details
-userForm=Edit User
+startAtCheck=Specifica una data di partenza
+dryRun=Esecuzione di prova
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_ja.properties
similarity index 91%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_ja.properties
index 450ff50..d6ed0d3 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_ja.properties
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=User details
-userForm=Edit User
+startAtCheck=\u958b\u59cb\u65e5\u3092\u6307\u5b9a
+dryRun=Dry Run
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_pt_BR.properties
similarity index 92%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_pt_BR.properties
index 450ff50..65e6c54 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_pt_BR.properties
@@ -14,5 +14,5 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=User details
-userForm=Edit User
+startAtCheck=Specify a starting date
+dryRun=Dry Run
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_ru.properties
similarity index 68%
copy from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_ru.properties
index 5a9cc2d..167d378 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/MacroTaskExecWizardBuilder$StartAt_ru.properties
@@ -14,5 +14,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
+#
+# startAtCheck=\u00d0\u00a3\u00d0\u00ba\u00d0\u00b0\u00d0\u00b6\u00d0\u00b8\u00d1\u0082\u00d0\u00b5 \u00d0\u00b4\u00d0\u00b0\u00d1\u0082\u00d1\u0083 \u00d0\u00bd\u00d0\u00b0\u00d1\u0087\u00d0\u00b0\u00d0\u00bb\u00d0\u00b0
+startAtCheck=\u0423\u043a\u0430\u0436\u0438\u0442\u0435 \u0434\u0430\u0442\u0443 \u043d\u0430\u0447\u0430\u043b\u0430
+dryRun=Dry Run
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html
index 8d5a2e1..7c4f21b 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html
@@ -26,6 +26,7 @@
<span wicket:id="macroTaskSpecifics">
<div class="form-group"><span wicket:id="realm">[realm]</span></div>
+ <div class="form-group"><span wicket:id="macroActions">[macroActions]</span></div>
<div class="form-group"><span wicket:id="continueOnError">[continueOnError]</span></div>
<div class="form-group"><span wicket:id="saveExecs">[saveExecs]</span></div>
</span>
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties
index 1cb70c4..0e53e9e 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties
@@ -141,7 +141,7 @@
dryrun.title=dry-run
dryrun.alt=dry-run icon
-claim.class=fa fa-ticket
+claim.class=fas fa-ticket-alt
claim.title=claim
claim.alt=claim icon
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties
index 24ddf1d..48d13be 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties
@@ -113,7 +113,7 @@
dryrun.class=fas fa-cogs
dryrun.title=test \u00e0 blanc
dryrun.alt=ic\u00f4ne test \u00e0 blanc
-claim.class=fa fa-ticket
+claim.class=fas fa-ticket-alt
claim.title=obtenir
claim.alt=ic\u00f4ne obtenir
unclaim.class=fa fa-undo
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
index d9c56eb..dbbab26 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties
@@ -142,7 +142,7 @@
dryrun.title=dry-run
dryrun.alt=dry-run icon
-claim.class=fa fa-ticket
+claim.class=fas fa-ticket-alt
claim.title=claim
claim.alt=claim icon
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties
index 4c11ce1..12f5a73 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties
@@ -142,7 +142,7 @@
dryrun.title=\u4e88\u884c\u6f14\u7fd2
dryrun.alt=\u4e88\u884c\u6f14\u7fd2
-claim.class=fa fa-ticket
+claim.class=fas fa-ticket-alt
claim.title=\u7533\u8acb
claim.alt=\u7533\u8acb
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties
index 8426802..368df37 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties
@@ -141,7 +141,7 @@
dryrun.title=dry-run
dryrun.alt=dry-run icon
-claim.class=fa fa-ticket
+claim.class=fas fa-ticket-alt
claim.title=claim
claim.alt=claim icon
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties
index 53bba87..8228495 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties
@@ -142,7 +142,7 @@
dryrun.title=dry-run
dryrun.alt=dry-run icon
-claim.class=fa fa-ticket
+claim.class=fas fa-ticket-alt
claim.title=claim
claim.alt=claim icon
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java
similarity index 84%
rename from ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
rename to common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java
index 603e7fc..471f072 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.lib.to;
+package org.apache.syncope.common.lib.form;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
@@ -25,9 +25,8 @@
import java.util.List;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.syncope.common.lib.types.UserRequestFormPropertyType;
-public class UserRequestFormProperty implements Serializable {
+public class FormProperty implements Serializable {
private static final long serialVersionUID = 9139969592634304261L;
@@ -35,7 +34,7 @@
private String name;
- private UserRequestFormPropertyType type;
+ private FormPropertyType type;
private boolean readable;
@@ -45,9 +44,9 @@
private String datePattern;
- private final List<UserRequestFormPropertyValue> enumValues = new ArrayList<>();
+ private final List<FormPropertyValue> enumValues = new ArrayList<>();
- private final List<UserRequestFormPropertyValue> dropdownValues = new ArrayList<>();
+ private final List<FormPropertyValue> dropdownValues = new ArrayList<>();
private String value;
@@ -83,11 +82,11 @@
this.required = required;
}
- public UserRequestFormPropertyType getType() {
+ public FormPropertyType getType() {
return type;
}
- public void setType(final UserRequestFormPropertyType type) {
+ public void setType(final FormPropertyType type) {
this.type = type;
}
@@ -109,13 +108,13 @@
@JacksonXmlElementWrapper(localName = "enumValues")
@JacksonXmlProperty(localName = "enumValue")
- public List<UserRequestFormPropertyValue> getEnumValues() {
+ public List<FormPropertyValue> getEnumValues() {
return enumValues;
}
@JacksonXmlElementWrapper(localName = "dropdownValues")
@JacksonXmlProperty(localName = "dropdownValue")
- public List<UserRequestFormPropertyValue> getDropdownValues() {
+ public List<FormPropertyValue> getDropdownValues() {
return dropdownValues;
}
@@ -154,7 +153,7 @@
if (getClass() != obj.getClass()) {
return false;
}
- UserRequestFormProperty other = (UserRequestFormProperty) obj;
+ FormProperty other = (FormProperty) obj;
return new EqualsBuilder().
append(id, other.id).
append(name, other.name).
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyType.java
similarity index 90%
rename from ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
rename to common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyType.java
index 5cd31c7..a2ca54f 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyType.java
@@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.lib.types;
+package org.apache.syncope.common.lib.form;
-public enum UserRequestFormPropertyType {
+public enum FormPropertyType {
String,
Long,
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormPropertyValue.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyValue.java
similarity index 89%
rename from ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormPropertyValue.java
rename to common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyValue.java
index 3a166bc..2d9135c 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormPropertyValue.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyValue.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.lib.to;
+package org.apache.syncope.common.lib.form;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -24,7 +24,7 @@
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-public class UserRequestFormPropertyValue implements Serializable {
+public class FormPropertyValue implements Serializable {
private static final long serialVersionUID = 9139969597634304261L;
@@ -33,7 +33,7 @@
private final String value;
@JsonCreator
- public UserRequestFormPropertyValue(
+ public FormPropertyValue(
@JsonProperty("key") final String key,
@JsonProperty("value") final String value) {
@@ -68,7 +68,7 @@
if (getClass() != obj.getClass()) {
return false;
}
- UserRequestFormPropertyValue other = (UserRequestFormPropertyValue) obj;
+ FormPropertyValue other = (FormPropertyValue) obj;
return new EqualsBuilder().
append(key, other.key).
append(value, other.value).
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/SyncopeForm.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/SyncopeForm.java
new file mode 100644
index 0000000..7ea4a1e
--- /dev/null
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/SyncopeForm.java
@@ -0,0 +1,71 @@
+/*
+ * 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.common.lib.form;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.common.lib.BaseBean;
+
+public class SyncopeForm implements BaseBean {
+
+ private static final long serialVersionUID = 8388697351958834257L;
+
+ private final List<FormProperty> properties = new ArrayList<>();
+
+ @JsonIgnore
+ public Optional<FormProperty> getProperty(final String id) {
+ return properties.stream().filter(property -> id.equals(property.getId())).findFirst();
+ }
+
+ @JacksonXmlElementWrapper(localName = "properties")
+ @JacksonXmlProperty(localName = "property")
+ public List<FormProperty> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().
+ append(properties).
+ build();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ SyncopeForm other = (SyncopeForm) obj;
+ return new EqualsBuilder().
+ append(properties, other.properties).
+ build();
+ }
+}
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java
similarity index 61%
copy from ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
copy to common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java
index 603e7fc..5357b1d 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java
@@ -18,55 +18,60 @@
*/
package org.apache.syncope.common.lib.to;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.LinkedHashMap;
+import java.util.Map;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.syncope.common.lib.types.UserRequestFormPropertyType;
+import org.apache.syncope.common.lib.form.FormPropertyType;
-public class UserRequestFormProperty implements Serializable {
+public class FormPropertyDefTO implements NamedEntityTO {
- private static final long serialVersionUID = 9139969592634304261L;
+ private static final long serialVersionUID = -6862109424380661428L;
- private String id;
+ private String key;
private String name;
- private UserRequestFormPropertyType type;
+ private FormPropertyType type;
- private boolean readable;
+ private boolean readable = true;
- private boolean writable;
+ private boolean writable = true;
private boolean required;
private String datePattern;
- private final List<UserRequestFormPropertyValue> enumValues = new ArrayList<>();
+ private final Map<String, String> enumValues = new LinkedHashMap<>();
- private final List<UserRequestFormPropertyValue> dropdownValues = new ArrayList<>();
-
- private String value;
-
- public String getId() {
- return id;
+ @Override
+ public String getKey() {
+ return key;
}
- public void setId(final String id) {
- this.id = id;
+ @Override
+ public void setKey(final String key) {
+ this.key = key;
}
+ @Override
public String getName() {
return name;
}
+ @Override
public void setName(final String name) {
this.name = name;
}
+ public FormPropertyType getType() {
+ return type;
+ }
+
+ public void setType(final FormPropertyType type) {
+ this.type = type;
+ }
+
public boolean isReadable() {
return readable;
}
@@ -75,22 +80,6 @@
this.readable = readable;
}
- public boolean isRequired() {
- return required;
- }
-
- public void setRequired(final boolean required) {
- this.required = required;
- }
-
- public UserRequestFormPropertyType getType() {
- return type;
- }
-
- public void setType(final UserRequestFormPropertyType type) {
- this.type = type;
- }
-
public boolean isWritable() {
return writable;
}
@@ -99,6 +88,14 @@
this.writable = writable;
}
+ public boolean isRequired() {
+ return required;
+ }
+
+ public void setRequired(final boolean required) {
+ this.required = required;
+ }
+
public String getDatePattern() {
return datePattern;
}
@@ -107,30 +104,14 @@
this.datePattern = datePattern;
}
- @JacksonXmlElementWrapper(localName = "enumValues")
- @JacksonXmlProperty(localName = "enumValue")
- public List<UserRequestFormPropertyValue> getEnumValues() {
+ public Map<String, String> getEnumValues() {
return enumValues;
}
- @JacksonXmlElementWrapper(localName = "dropdownValues")
- @JacksonXmlProperty(localName = "dropdownValue")
- public List<UserRequestFormPropertyValue> getDropdownValues() {
- return dropdownValues;
- }
-
- public String getValue() {
- return value;
- }
-
- public void setValue(final String value) {
- this.value = value;
- }
-
@Override
public int hashCode() {
return new HashCodeBuilder().
- append(id).
+ append(key).
append(name).
append(type).
append(readable).
@@ -138,8 +119,6 @@
append(required).
append(datePattern).
append(enumValues).
- append(dropdownValues).
- append(value).
build();
}
@@ -154,9 +133,9 @@
if (getClass() != obj.getClass()) {
return false;
}
- UserRequestFormProperty other = (UserRequestFormProperty) obj;
+ FormPropertyDefTO other = (FormPropertyDefTO) obj;
return new EqualsBuilder().
- append(id, other.id).
+ append(key, other.key).
append(name, other.name).
append(type, other.type).
append(readable, other.readable).
@@ -164,8 +143,6 @@
append(required, other.required).
append(datePattern, other.datePattern).
append(enumValues, other.enumValues).
- append(dropdownValues, other.dropdownValues).
- append(value, other.value).
build();
}
}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MacroTaskTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MacroTaskTO.java
index 8f92778..1685d72 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MacroTaskTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/MacroTaskTO.java
@@ -41,6 +41,10 @@
private boolean saveExecs = true;
+ private final List<FormPropertyDefTO> formPropertyDefs = new ArrayList<>();
+
+ private String macroActions;
+
@JacksonXmlProperty(localName = "_class", isAttribute = true)
@JsonProperty("_class")
@Schema(name = "_class", requiredMode = Schema.RequiredMode.REQUIRED,
@@ -80,6 +84,20 @@
this.saveExecs = saveExecs;
}
+ @JacksonXmlElementWrapper(localName = "formPropertyDefs")
+ @JacksonXmlProperty(localName = "formPropertyDef")
+ public List<FormPropertyDefTO> getFormPropertyDefs() {
+ return formPropertyDefs;
+ }
+
+ public String getMacroActions() {
+ return macroActions;
+ }
+
+ public void setMacroActions(final String macroActions) {
+ this.macroActions = macroActions;
+ }
+
@Override
public int hashCode() {
return new HashCodeBuilder().
@@ -88,6 +106,8 @@
append(commands).
append(continueOnError).
append(saveExecs).
+ append(formPropertyDefs).
+ append(macroActions).
build();
}
@@ -109,6 +129,8 @@
append(commands, other.commands).
append(continueOnError, other.continueOnError).
append(saveExecs, other.saveExecs).
+ append(formPropertyDefs, other.formPropertyDefs).
+ append(macroActions, other.macroActions).
build();
}
}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
index 513a0a1..5891f4a 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
@@ -25,6 +25,7 @@
InvalidADynMemberships,
InvalidConnInstanceLocation,
InvalidConnPoolConf,
+ InvalidFormPropertyDef,
InvalidMapping,
InvalidKey,
InvalidName,
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
index 64870f1..581d1d2 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoImplementationType.java
@@ -33,7 +33,9 @@
public static final String LOGIC_ACTIONS = "LOGIC_ACTIONS";
- public static final String VALIDATOR = "VALIDATOR";
+ public static final String MACRO_ACTIONS = "MACRO_ACTIONS";
+
+ public static final String ATTR_VALUE_VALIDATOR = "ATTR_VALUE_VALIDATOR";
public static final String COMMAND = "COMMAND";
@@ -47,8 +49,10 @@
Pair.of(TASKJOB_DELEGATE, "org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate"),
Pair.of(REPORT_DELEGATE, "org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate"),
Pair.of(LOGIC_ACTIONS, "org.apache.syncope.core.logic.api.LogicActions"),
- Pair.of(VALIDATOR, "org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator"),
- Pair.of(COMMAND, "org.apache.syncope.core.logic.api.Command"),
+ Pair.of(MACRO_ACTIONS, "org.apache.syncope.core.provisioning.api.macro.MacroActions"),
+ Pair.of(ATTR_VALUE_VALIDATOR,
+ "org.apache.syncope.core.persistence.api.attrvalue.validation.PlainAttrValueValidator"),
+ Pair.of(COMMAND, "org.apache.syncope.core.provisioning.api.macro.Command"),
Pair.of(RECIPIENTS_PROVIDER, "org.apache.syncope.core.provisioning.api.notification.RecipientsProvider"),
Pair.of(ITEM_TRANSFORMER, "org.apache.syncope.core.provisioning.api.data.ItemTransformer"));
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
index f1b873b..8b2ceb8 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/TaskService.java
@@ -44,12 +44,15 @@
import jakarta.ws.rs.core.Response;
import java.time.OffsetDateTime;
import java.util.List;
+import org.apache.syncope.common.lib.form.SyncopeForm;
+import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.PagedResult;
import org.apache.syncope.common.lib.to.SchedTaskTO;
import org.apache.syncope.common.lib.to.TaskTO;
import org.apache.syncope.common.lib.types.ExecStatus;
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
import org.apache.syncope.common.rest.api.beans.TaskQuery;
/**
@@ -161,4 +164,28 @@
@QueryParam("since") OffsetDateTime since,
@QueryParam("statuses") List<ExecStatus> statuses,
@QueryParam("resources") List<String> resources);
+
+ /**
+ * Fetches the form to fill and submit for execution, for the given macro task (if defined).
+ *
+ * @param key macro task key
+ * @return the form to fill and submit for execution, for the given macro task (if defined)
+ */
+ @GET
+ @Path("MACRO/{key}/form")
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ SyncopeForm getMacroTaskForm(@NotNull @PathParam("key") String key);
+
+ /**
+ * Executes the macro task matching the given specs, with the provided form as input.
+ *
+ * @param specs conditions to exec
+ * @param macroTaskForm macro task form
+ * @return execution report for the macro task matching the given specs
+ */
+ @POST
+ @Path("MACRO/{key}/execute")
+ @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ ExecTO execute(@BeanParam ExecSpecs specs, SyncopeForm macroTaskForm);
}
diff --git a/common/keymaster/client-api/src/main/resources/defaultContent.jpa.xml b/common/keymaster/client-api/src/main/resources/defaultContent.jpa.xml
index a9dd041..fcb7013 100644
--- a/common/keymaster/client-api/src/main/resources/defaultContent.jpa.xml
+++ b/common/keymaster/client-api/src/main/resources/defaultContent.jpa.xml
@@ -26,18 +26,18 @@
<AnyType id="GROUP" kind="GROUP"/>
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<SyncopeSchema id="email"/>
<PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
validator_id="EmailAddressValidator"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
index 48f7f85..dd64fe0 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
@@ -27,6 +27,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
+import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
@@ -71,6 +72,7 @@
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
+import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.persistence.api.utils.RealmUtils;
import org.apache.syncope.core.provisioning.api.ConnectorManager;
import org.apache.syncope.core.provisioning.api.MappingManager;
@@ -98,6 +100,7 @@
import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.framework.common.objects.SearchResult;
import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
import org.identityconnectors.framework.common.objects.SyncDeltaType;
@@ -190,6 +193,7 @@
protected ConnObject getOnSyncope(
final Item connObjectKeyItem,
final String connObjectKeyValue,
+ final Boolean suspended,
final Set<Attribute> attrs) {
ConnObject connObjectTO = ConnObjectUtils.getConnObjectTO(null, attrs);
@@ -197,6 +201,11 @@
value(connObjectKeyValue).build());
connObjectTO.getAttrs().add(new Attr.Builder(Uid.NAME).
value(connObjectKeyValue).build());
+ Optional.ofNullable(suspended).ifPresent(s -> {
+ connObjectTO.getAttrs().removeIf(a -> OperationalAttributes.ENABLE_NAME.equals(a.getSchema()));
+ connObjectTO.getAttrs().add(new Attr.Builder(OperationalAttributes.ENABLE_NAME).
+ value(BooleanUtils.negate(s).toString()).build());
+ });
return connObjectTO;
}
@@ -209,7 +218,11 @@
Pair<String, Set<Attribute>> prepared = mappingManager.prepareAttrsFromAny(
any, null, false, true, resource, provision);
- return getOnSyncope(connObjectKeyItem, prepared.getLeft(), prepared.getRight());
+ return getOnSyncope(
+ connObjectKeyItem,
+ prepared.getLeft(),
+ any instanceof User ? ((User) any).isSuspended() : null,
+ prepared.getRight());
}
protected ConnObject getOnSyncope(
@@ -219,7 +232,11 @@
Set<Attribute> attrs = mappingManager.prepareAttrsFromLinkedAccount(
account.getOwner(), account, null, false, provision);
- return getOnSyncope(connObjectKeyItem, account.getConnObjectKeyValue(), attrs);
+ return getOnSyncope(
+ connObjectKeyItem,
+ account.getConnObjectKeyValue(),
+ account.isSuspended(),
+ attrs);
}
protected Any<?> getAny(final Provision provision, final AnyTypeKind anyTypeKind, final String anyKey) {
@@ -234,11 +251,9 @@
: ((AnyObjectDAO) dao).findKey(provision.getAnyType(), anyKey)).
orElse(null);
}
- Any<?> any = dao.authFind(actualKey);
- if (any == null) {
- throw new NotFoundException(provision.getAnyType() + " '" + anyKey + "'");
- }
- return any;
+
+ return Optional.ofNullable(dao.authFind(actualKey)).
+ orElseThrow(() -> new NotFoundException(provision.getAnyType() + " '" + anyKey + "'"));
}
@PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
index 5ca851a..f7987fa 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractExecutableLogic.java
@@ -25,6 +25,7 @@
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.types.JobAction;
import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
import org.apache.syncope.core.persistence.api.dao.JobStatusDAO;
import org.apache.syncope.core.provisioning.api.job.JobManager;
import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler;
@@ -41,7 +42,7 @@
super(jobManager, scheduler, jobStatusDAO);
}
- public abstract ExecTO execute(String key, OffsetDateTime startAt, boolean dryRun);
+ public abstract ExecTO execute(ExecSpecs specs);
public abstract Page<ExecTO> listExecutions(
String key,
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java
index 23d2a2c..89c9718 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java
@@ -26,12 +26,14 @@
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.AuditConfTO;
import org.apache.syncope.common.lib.to.AuditEventTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoImplementationType;
import org.apache.syncope.common.lib.types.MatchingRule;
import org.apache.syncope.common.lib.types.OpEvent;
import org.apache.syncope.common.lib.types.ResourceOperation;
@@ -44,9 +46,8 @@
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.search.SyncopePage;
import org.apache.syncope.core.provisioning.api.AuditManager;
+import org.apache.syncope.core.provisioning.api.ImplementationLookup;
import org.apache.syncope.core.provisioning.api.data.AuditDataBinder;
-import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
-import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@@ -84,6 +85,8 @@
protected final EntityFactory entityFactory;
+ protected final ImplementationLookup implementationLookup;
+
protected final AuditDataBinder binder;
protected final AuditManager auditManager;
@@ -93,6 +96,7 @@
final AuditEventDAO auditEventDAO,
final ExternalResourceDAO resourceDAO,
final EntityFactory entityFactory,
+ final ImplementationLookup implementationLookup,
final AuditDataBinder binder,
final AuditManager auditManager) {
@@ -100,6 +104,7 @@
this.auditEventDAO = auditEventDAO;
this.resourceDAO = resourceDAO;
this.entityFactory = entityFactory;
+ this.implementationLookup = implementationLookup;
this.binder = binder;
this.auditManager = auditManager;
}
@@ -153,19 +158,13 @@
null,
OpEvent.LOGIN_OP);
- addForOutcomes(
+ implementationLookup.getClassNames(IdRepoImplementationType.TASKJOB_DELEGATE).
+ forEach(clazz -> addForOutcomes(
events,
OpEvent.CategoryType.TASK,
- PullJobDelegate.class.getSimpleName(),
+ StringUtils.substringAfterLast(clazz, '.'),
null,
- null);
-
- addForOutcomes(
- events,
- OpEvent.CategoryType.TASK,
- PushJobDelegate.class.getSimpleName(),
- null,
- null);
+ null));
addForOutcomes(
events,
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
index 80e8786..0056113 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
@@ -34,12 +34,12 @@
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.common.lib.types.IdRepoImplementationType;
-import org.apache.syncope.core.logic.api.Command;
import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException;
import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.search.SyncopePage;
+import org.apache.syncope.core.provisioning.api.macro.Command;
import org.apache.syncope.core.spring.implementation.ImplementationManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@@ -134,7 +134,7 @@
}
try {
- return runnable.run(command.getArgs());
+ return runnable.run(command.getArgs() == null ? ImplementationManager.emptyArgs(impl) : command.getArgs());
} catch (Exception e) {
LOG.error("While running {} on {}", command.getKey(), command.getArgs(), e);
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
index c67542c..c8013bd 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
@@ -209,6 +209,7 @@
final AuditEventDAO auditEventDAO,
final ExternalResourceDAO resourceDAO,
final EntityFactory entityFactory,
+ final ImplementationLookup implementationLookup,
final AuditDataBinder binder,
final AuditManager auditManager) {
@@ -217,6 +218,7 @@
auditEventDAO,
resourceDAO,
entityFactory,
+ implementationLookup,
binder,
auditManager);
}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java
index db2d83a..617bac9 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java
@@ -219,7 +219,7 @@
inUse = !policyDAO.findByPushCorrelationRule(implementation).isEmpty();
break;
- case IdRepoImplementationType.VALIDATOR:
+ case IdRepoImplementationType.ATTR_VALUE_VALIDATOR:
inUse = !plainSchemaDAO.findByValidator(implementation).isEmpty();
break;
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index edd1837..a1a9966 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -40,6 +40,7 @@
import org.apache.syncope.common.lib.types.JobType;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
import org.apache.syncope.core.persistence.api.dao.JobStatusDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.ReportDAO;
@@ -149,22 +150,28 @@
@PreAuthorize("hasRole('" + IdRepoEntitlement.REPORT_EXECUTE + "')")
@Override
- public ExecTO execute(final String key, final OffsetDateTime startAt, final boolean dryRun) {
- Report report = reportDAO.findById(key).
- orElseThrow(() -> new NotFoundException("Report " + key));
+ public ExecTO execute(final ExecSpecs specs) {
+ Report report = reportDAO.findById(specs.getKey()).
+ orElseThrow(() -> new NotFoundException("Report " + specs.getKey()));
if (!report.isActive()) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
- sce.getElements().add("Report " + key + " is not active");
+ sce.getElements().add("Report " + specs.getKey() + " is not active");
+ throw sce;
+ }
+
+ if (specs.getStartAt() != null && specs.getStartAt().isBefore(OffsetDateTime.now())) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+ sce.getElements().add("Cannot schedule in the past");
throw sce;
}
try {
jobManager.register(
report,
- Optional.ofNullable(startAt).orElseGet(() -> OffsetDateTime.now()),
+ Optional.ofNullable(specs.getStartAt()).orElseGet(() -> OffsetDateTime.now()),
AuthContextUtils.getUsername(),
- dryRun);
+ specs.getDryRun());
} catch (Exception e) {
LOG.error("While executing report {}", report, e);
@@ -213,8 +220,8 @@
}
// streaming output from a compressed byte array stream
- try (ByteArrayInputStream bais = new ByteArrayInputStream(reportExec.getExecResult()); ZipInputStream zis =
- new ZipInputStream(bais)) {
+ try (ByteArrayInputStream bais = new ByteArrayInputStream(reportExec.getExecResult());
+ ZipInputStream zis = new ZipInputStream(bais)) {
// a single ZipEntry in the ZipInputStream
zis.getNextEntry();
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index e2a5c89..3030630 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -30,6 +30,7 @@
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.form.SyncopeForm;
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.JobTO;
import org.apache.syncope.common.lib.to.MacroTaskTO;
@@ -45,6 +46,7 @@
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.JobStatusDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
@@ -69,6 +71,7 @@
import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
+import org.apache.syncope.core.provisioning.java.job.MacroJobDelegate;
import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler;
import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
import org.apache.syncope.core.spring.security.AuthContextUtils;
@@ -272,10 +275,24 @@
return binder.getTaskTO(task, taskUtilsFactory.getInstance(task), details);
}
- @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
- @Override
- public ExecTO execute(final String key, final OffsetDateTime startAt, final boolean dryRun) {
- Task<?> task = taskDAO.findById(key).orElseThrow(() -> new NotFoundException("Task " + key));
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_READ + "')")
+ @Transactional(readOnly = true)
+ public SyncopeForm getMacroTaskForm(final String key) {
+ MacroTask task = taskDAO.findById(TaskType.MACRO, key).
+ filter(MacroTask.class::isInstance).map(MacroTask.class::cast).
+ orElseThrow(() -> new NotFoundException("MacroTask " + key));
+
+ securityChecks(IdRepoEntitlement.TASK_READ, task.getRealm().getFullPath());
+
+ return binder.getMacroTaskForm(task);
+ }
+
+ protected ExecTO doExecute(
+ final Task<?> task,
+ final OffsetDateTime startAt,
+ final boolean dryRun,
+ final Map<String, Object> additionalDataMap) {
+
if (startAt != null && startAt.isBefore(OffsetDateTime.now())) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
sce.getElements().add("Cannot schedule in the past");
@@ -316,23 +333,23 @@
case PULL:
case PUSH:
case MACRO:
- if (taskUtils.getType() == TaskType.MACRO) {
- securityChecks(IdRepoEntitlement.TASK_EXECUTE, ((MacroTask) task).getRealm().getFullPath());
- }
-
if (!((SchedTask) task).isActive()) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
- sce.getElements().add("Task " + key + " is not active");
+ sce.getElements().add("Task " + task.getKey() + " is not active");
throw sce;
}
+ if (taskUtils.getType() == TaskType.MACRO) {
+ securityChecks(IdRepoEntitlement.TASK_EXECUTE, ((MacroTask) task).getRealm().getFullPath());
+ }
+
try {
jobManager.register(
(SchedTask) task,
Optional.ofNullable(startAt).orElseGet(() -> OffsetDateTime.now()),
executor,
dryRun,
- Map.of());
+ additionalDataMap);
} catch (Exception e) {
LOG.error("While executing task {}", task, e);
@@ -357,6 +374,32 @@
return result;
}
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+ @Override
+ public ExecTO execute(final ExecSpecs specs) {
+ Task<?> task = taskDAO.findById(specs.getKey()).
+ orElseThrow(() -> new NotFoundException("Task " + specs.getKey()));
+
+ return doExecute(
+ task,
+ specs.getStartAt(),
+ specs.getDryRun(),
+ Map.of());
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+ public ExecTO execute(final ExecSpecs specs, final SyncopeForm macroTaskForm) {
+ MacroTask task = taskDAO.findById(specs.getKey()).
+ filter(MacroTask.class::isInstance).map(MacroTask.class::cast).
+ orElseThrow(() -> new NotFoundException("MacroTask " + specs.getKey()));
+
+ return doExecute(
+ task,
+ specs.getStartAt(),
+ specs.getDryRun(),
+ Map.of(MacroJobDelegate.MACRO_TASK_FORM_JOBDETAIL_KEY, macroTaskForm));
+ }
+
@PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_DELETE + "')")
public <T extends TaskTO> T delete(final TaskType type, final String key) {
Task<?> task = taskDAO.findById(type, key).
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index f594233..5e3d2e1 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -35,7 +35,6 @@
import org.apache.syncope.common.lib.types.IdMImplementationType;
import org.apache.syncope.common.lib.types.IdRepoImplementationType;
import org.apache.syncope.common.lib.types.ImplementationTypesHolder;
-import org.apache.syncope.core.logic.api.Command;
import org.apache.syncope.core.logic.api.LogicActions;
import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator;
import org.apache.syncope.core.provisioning.api.ImplementationLookup;
@@ -44,6 +43,7 @@
import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
import org.apache.syncope.core.provisioning.api.job.report.ReportConfClass;
import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate;
+import org.apache.syncope.core.provisioning.api.macro.Command;
import org.apache.syncope.core.provisioning.api.notification.RecipientsProvider;
import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
@@ -199,7 +199,7 @@
} else if (PushActions.class.isAssignableFrom(clazz)) {
classNames.get(IdMImplementationType.PUSH_ACTIONS).add(bd.getBeanClassName());
} else if (PlainAttrValueValidator.class.isAssignableFrom(clazz)) {
- classNames.get(IdRepoImplementationType.VALIDATOR).add(bd.getBeanClassName());
+ classNames.get(IdRepoImplementationType.ATTR_VALUE_VALIDATOR).add(bd.getBeanClassName());
} else if (RecipientsProvider.class.isAssignableFrom(clazz)) {
classNames.get(IdRepoImplementationType.RECIPIENTS_PROVIDER).add(bd.getBeanClassName());
} else if (ProvisionSorter.class.isAssignableFrom(clazz)) {
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/job/MacroRunJobDelegate.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/job/MacroRunJobDelegate.java
deleted file mode 100644
index 38e5587..0000000
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/job/MacroRunJobDelegate.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.logic.job;
-
-import jakarta.validation.ConstraintViolation;
-import jakarta.validation.Validator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import org.apache.syncope.common.lib.command.CommandArgs;
-import org.apache.syncope.core.logic.api.Command;
-import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.provisioning.api.job.JobExecutionContext;
-import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.provisioning.java.job.AbstractSchedTaskJobDelegate;
-import org.apache.syncope.core.spring.implementation.ImplementationManager;
-import org.springframework.beans.factory.annotation.Autowired;
-
-public class MacroRunJobDelegate extends AbstractSchedTaskJobDelegate<MacroTask> {
-
- @Autowired
- protected ImplementationDAO implementationDAO;
-
- @Autowired
- protected Validator validator;
-
- protected final Map<String, Command<?>> perContextCommands = new ConcurrentHashMap<>();
-
- @SuppressWarnings("unchecked")
- @Override
- protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context)
- throws JobExecutionException {
-
- StringBuilder output = new StringBuilder();
- for (int i = 0; i < task.getCommands().size(); i++) {
- Implementation command = task.getCommands().get(i);
-
- Command<CommandArgs> runnable;
- try {
- runnable = (Command<CommandArgs>) ImplementationManager.build(
- command,
- () -> perContextCommands.get(command.getKey()),
- instance -> perContextCommands.put(command.getKey(), instance));
- } catch (Exception e) {
- throw new JobExecutionException("Could not build " + command.getKey(), e);
- }
-
- String args = POJOHelper.serialize(task.getCommandArgs().get(i));
-
- output.append("Command[").append(i).append("]: ").
- append(command.getKey()).append(" ").append(args).append("\n");
- if (dryRun) {
- output.append(command).append(' ').append(args);
- } else {
- try {
- if (task.getCommandArgs().get(i) != null) {
- Set<ConstraintViolation<Object>> violations = validator.validate(task.getCommandArgs().get(i));
- if (!violations.isEmpty()) {
- LOG.error("Errors while validating {}: {}", task.getCommandArgs().get(i), violations);
- throw new IllegalArgumentException(task.getCommandArgs().get(i).getClass().getName());
- }
- }
-
- output.append(runnable.run(task.getCommandArgs().get(i)));
- } catch (Exception e) {
- if (task.isContinueOnError()) {
- output.append("Continuing on error: <").append(e.getMessage()).append('>');
- LOG.error("While running {} with args {}, continuing on error", command.getKey(), args, e);
- } else {
- throw new RuntimeException("While running " + command.getKey(), e);
- }
- }
- }
- output.append("\n\n");
- }
-
- output.append("COMPLETED");
- return output.toString();
- }
-
- @Override
- protected boolean hasToBeRegistered(final TaskExec<?> execution) {
- return task.isSaveExecs();
- }
-}
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
index b784e75..3c07df4 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractExecutableService.java
@@ -74,8 +74,8 @@
}
@Override
- public ExecTO execute(final ExecSpecs query) {
- return getExecutableLogic().execute(query.getKey(), query.getStartAt(), query.getDryRun());
+ public ExecTO execute(final ExecSpecs execSpecs) {
+ return getExecutableLogic().execute(execSpecs);
}
@Override
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java
index 6aa590a..f116eb2 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/TaskServiceImpl.java
@@ -23,12 +23,15 @@
import java.net.URI;
import java.time.OffsetDateTime;
import java.util.List;
+import org.apache.syncope.common.lib.form.SyncopeForm;
+import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.PagedResult;
import org.apache.syncope.common.lib.to.SchedTaskTO;
import org.apache.syncope.common.lib.to.TaskTO;
import org.apache.syncope.common.lib.types.ExecStatus;
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
import org.apache.syncope.common.rest.api.beans.TaskQuery;
import org.apache.syncope.common.rest.api.service.TaskService;
import org.apache.syncope.core.logic.AbstractExecutableLogic;
@@ -107,4 +110,14 @@
return Response.ok(logic.purgePropagations(since, statuses, resources)).build();
}
+
+ @Override
+ public SyncopeForm getMacroTaskForm(final String key) {
+ return logic.getMacroTaskForm(key);
+ }
+
+ @Override
+ public ExecTO execute(final ExecSpecs specs, final SyncopeForm macroTaskForm) {
+ return logic.execute(specs, macroTaskForm);
+ }
}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java
new file mode 100644
index 0000000..dbfcadd
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java
@@ -0,0 +1,58 @@
+/*
+ * 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.api.entity.task;
+
+import java.util.Map;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
+
+public interface FormPropertyDef extends ProvidedKeyEntity {
+
+ MacroTask getMacroTask();
+
+ void setMacroTask(MacroTask macroTask);
+
+ String getName();
+
+ void setName(String name);
+
+ FormPropertyType getType();
+
+ void setType(FormPropertyType type);
+
+ boolean isReadable();
+
+ void setReadable(boolean readable);
+
+ boolean isWritable();
+
+ void setWritable(boolean writable);
+
+ boolean isRequired();
+
+ void setRequired(boolean required);
+
+ String getDatePattern();
+
+ void setDatePattern(String datePattern);
+
+ Map<String, String> getEnumValues();
+
+ void setEnumValues(Map<String, String> enumValues);
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/MacroTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/MacroTask.java
index 29c68e0..e9a6846 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/MacroTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/MacroTask.java
@@ -19,7 +19,6 @@
package org.apache.syncope.core.persistence.api.entity.task;
import java.util.List;
-import org.apache.syncope.common.lib.command.CommandArgs;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.Realm;
@@ -29,11 +28,17 @@
void setRealm(Realm realm);
- void add(Implementation command, CommandArgs args);
+ void add(MacroTaskCommand macroTaskCommand);
- List<? extends Implementation> getCommands();
+ List<? extends MacroTaskCommand> getCommands();
- List<CommandArgs> getCommandArgs();
+ void add(FormPropertyDef formPropertyDef);
+
+ List<? extends FormPropertyDef> getFormPropertyDefs();
+
+ Implementation getMacroActions();
+
+ void setMacroAction(Implementation macroActions);
boolean isContinueOnError();
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/Command.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/MacroTaskCommand.java
similarity index 64%
copy from core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/Command.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/MacroTaskCommand.java
index 88047c8..ed0cb4f 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/Command.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/MacroTaskCommand.java
@@ -16,12 +16,23 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.logic.api;
+package org.apache.syncope.core.persistence.api.entity.task;
import org.apache.syncope.common.lib.command.CommandArgs;
+import org.apache.syncope.core.persistence.api.entity.Entity;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
-@FunctionalInterface
-public interface Command<A extends CommandArgs> {
+public interface MacroTaskCommand extends Entity {
- String run(A args);
+ MacroTask getMacroTask();
+
+ void setMacroTask(MacroTask macroTask);
+
+ Implementation getCommand();
+
+ void setCommand(Implementation command);
+
+ CommandArgs getArgs();
+
+ void setArgs(CommandArgs args);
}
diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/FormPropertyDefCheck.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/FormPropertyDefCheck.java
new file mode 100644
index 0000000..42d615e
--- /dev/null
+++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/FormPropertyDefCheck.java
@@ -0,0 +1,40 @@
+/*
+ * 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.common.validation;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = FormPropertyDefValidator.class)
+@Documented
+public @interface FormPropertyDefCheck {
+
+ String message() default "{org.apache.syncope.core.persistence.validation.formpropertydef}";
+
+ Class<?>[] groups() default {};
+
+ Class<? extends Payload>[] payload() default {};
+}
diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/FormPropertyDefValidator.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/FormPropertyDefValidator.java
new file mode 100644
index 0000000..34c7fd0
--- /dev/null
+++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/validation/FormPropertyDefValidator.java
@@ -0,0 +1,61 @@
+/*
+ * 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.common.validation;
+
+import jakarta.validation.ConstraintValidatorContext;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
+
+public class FormPropertyDefValidator extends AbstractValidator<FormPropertyDefCheck, FormPropertyDef> {
+
+ @Override
+ public boolean isValid(final FormPropertyDef formPropertyDef, final ConstraintValidatorContext context) {
+ context.disableDefaultConstraintViolation();
+
+ if (formPropertyDef.getDatePattern() != null
+ && formPropertyDef.getType() != FormPropertyType.Date) {
+
+ context.buildConstraintViolationWithTemplate(getTemplate(
+ EntityViolationType.InvalidFormPropertyDef, "Date pattern found but type not set to Date")).
+ addPropertyNode("datePattern").addConstraintViolation();
+ return false;
+ }
+
+ if (!formPropertyDef.getEnumValues().isEmpty()
+ && formPropertyDef.getType() != FormPropertyType.Enum) {
+
+ context.buildConstraintViolationWithTemplate(getTemplate(
+ EntityViolationType.InvalidFormPropertyDef, "Enum values found but type not set to Enum")).
+ addPropertyNode("enumValues").addConstraintViolation();
+ return false;
+ }
+
+ if (formPropertyDef.getEnumValues().isEmpty()
+ && formPropertyDef.getType() == FormPropertyType.Enum) {
+
+ context.buildConstraintViolationWithTemplate(getTemplate(
+ EntityViolationType.InvalidFormPropertyDef, "No enum values provided")).
+ addPropertyNode("enumValues").addConstraintViolation();
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
index adff541..256e121 100644
--- a/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
+++ b/core/persistence-jpa-json/src/main/resources/domains/jpa-json/MasterContent.xml
@@ -29,18 +29,18 @@
<AnyType_AnyTypeClass anyType_id="GROUP" anyTypeClass_id="BaseGroup"/>
<!-- Actual plain schemas -->
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<SyncopeSchema id="email"/>
<PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
validator_id="EmailAddressValidator"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
index a2702c8..8817203 100644
--- a/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/test/resources/domains/MasterContent.xml
@@ -124,7 +124,7 @@
<PlainSchema id="fullname" type="String" anyTypeClass_id="minimal user"
mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"/>
<SyncopeSchema id="userId"/>
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<PlainSchema id="userId" type="String" anyTypeClass_id="minimal user"
mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"
@@ -175,7 +175,7 @@
mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
mimeType="image/jpeg"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
<SyncopeSchema id="csvuserid"/>
@@ -680,8 +680,8 @@
<VirSchema id="virtualdata" READONLY="0" anyTypeClass_id="minimal user"
resource_id="resource-db-virattr" anyType_id="USER" extAttrName="USERNAME"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
index a6aaf13..bae0170 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
@@ -504,8 +504,6 @@
jpaNotificationTask.list2json();
case JPAPushTask jpaPushTask ->
jpaPushTask.map2json();
- case JPAMacroTask macroTask ->
- macroTask.list2json();
default -> {
}
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index a3aac9e..6b376e9 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -88,7 +88,9 @@
import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.PullTask;
@@ -143,7 +145,9 @@
import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy;
import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy;
import org.apache.syncope.core.persistence.jpa.entity.task.JPAAnyTemplatePullTask;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAFormPropertyDef;
import org.apache.syncope.core.persistence.jpa.entity.task.JPAMacroTask;
+import org.apache.syncope.core.persistence.jpa.entity.task.JPAMacroTaskCommand;
import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTask;
import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTask;
import org.apache.syncope.core.persistence.jpa.entity.task.JPAPullTask;
@@ -280,6 +284,10 @@
result = (E) new JPASchedTask();
} else if (reference.equals(AnyTemplatePullTask.class)) {
result = (E) new JPAAnyTemplatePullTask();
+ } else if (reference.equals(MacroTaskCommand.class)) {
+ result = (E) new JPAMacroTaskCommand();
+ } else if (reference.equals(FormPropertyDef.class)) {
+ result = (E) new JPAFormPropertyDef();
} else if (reference.equals(SecurityQuestion.class)) {
result = (E) new JPASecurityQuestion();
} else if (reference.equals(AuditConf.class)) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java
index 3bb3c6f..af76cc7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPlainSchema.java
@@ -155,7 +155,7 @@
@Override
public void setValidator(final Implementation validator) {
checkType(validator, JPAImplementation.class);
- checkImplementationType(validator, IdRepoImplementationType.VALIDATOR);
+ checkImplementationType(validator, IdRepoImplementationType.ATTR_VALUE_VALIDATOR);
this.validator = (JPAImplementation) validator;
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java
new file mode 100644
index 0000000..0d86db3
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java
@@ -0,0 +1,153 @@
+/*
+ * 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.task;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.Lob;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+import jakarta.validation.constraints.NotNull;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.common.validation.FormPropertyDefCheck;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@Entity
+@Table(name = JPAFormPropertyDef.TABLE)
+@FormPropertyDefCheck
+public class JPAFormPropertyDef extends AbstractProvidedKeyEntity implements FormPropertyDef {
+
+ private static final long serialVersionUID = -5839990371546587373L;
+
+ public static final String TABLE = "FormPropertyDef";
+
+ @ManyToOne(optional = false)
+ private JPAMacroTask macroTask;
+
+ @NotNull
+ private String name;
+
+ @NotNull
+ @Enumerated(EnumType.STRING)
+ private FormPropertyType type;
+
+ @NotNull
+ private Boolean readable = Boolean.TRUE;
+
+ @NotNull
+ private Boolean writable = Boolean.TRUE;
+
+ @NotNull
+ private Boolean required = Boolean.FALSE;
+
+ private String datePattern;
+
+ @Lob
+ private String enumValues;
+
+ @Override
+ public JPAMacroTask getMacroTask() {
+ return macroTask;
+ }
+
+ @Override
+ public void setMacroTask(final MacroTask macroTask) {
+ checkType(macroTask, JPAMacroTask.class);
+ this.macroTask = (JPAMacroTask) macroTask;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public FormPropertyType getType() {
+ return type;
+ }
+
+ @Override
+ public void setType(final FormPropertyType type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean isReadable() {
+ return readable == null ? true : readable;
+ }
+
+ @Override
+ public void setReadable(final boolean readable) {
+ this.readable = readable;
+ }
+
+ @Override
+ public boolean isWritable() {
+ return writable == null ? true : writable;
+ }
+
+ @Override
+ public void setWritable(final boolean writable) {
+ this.writable = writable;
+ }
+
+ @Override
+ public boolean isRequired() {
+ return required == null ? false : required;
+ }
+
+ @Override
+ public void setRequired(final boolean required) {
+ this.required = required;
+ }
+
+ @Override
+ public String getDatePattern() {
+ return datePattern;
+ }
+
+ @Override
+ public void setDatePattern(final String datePattern) {
+ this.datePattern = datePattern;
+ }
+
+ @Override
+ public Map<String, String> getEnumValues() {
+ return Optional.ofNullable(enumValues).
+ map(v -> POJOHelper.deserialize(v, new TypeReference<Map<String, String>>() {
+ })).orElse(Map.of());
+ }
+
+ @Override
+ public void setEnumValues(final Map<String, String> enumValues) {
+ this.enumValues = Optional.ofNullable(enumValues).map(POJOHelper::serialize).orElse(null);
+ }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java
index ed87ec3..8660cd8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java
@@ -18,37 +18,26 @@
*/
package org.apache.syncope.core.persistence.jpa.entity.task;
-import com.fasterxml.jackson.core.type.TypeReference;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
-import jakarta.persistence.JoinColumn;
-import jakarta.persistence.JoinTable;
-import jakarta.persistence.Lob;
-import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
-import jakarta.persistence.PostLoad;
-import jakarta.persistence.PostPersist;
-import jakarta.persistence.PostUpdate;
-import jakarta.persistence.PrePersist;
-import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;
-import jakarta.persistence.Transient;
-import jakarta.persistence.UniqueConstraint;
+import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
-import org.apache.syncope.common.lib.command.CommandArgs;
import org.apache.syncope.common.lib.types.IdRepoImplementationType;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
@Entity
@Table(name = JPAMacroTask.TABLE)
@@ -58,9 +47,6 @@
public static final String TABLE = "MacroTask";
- protected static final TypeReference<List<CommandArgs>> TYPEREF = new TypeReference<List<CommandArgs>>() {
- };
-
@ManyToOne(fetch = FetchType.EAGER, optional = false)
private JPARealm realm;
@@ -70,21 +56,15 @@
@NotNull
private Boolean saveExecs = true;
- @ManyToMany(fetch = FetchType.EAGER)
- @JoinTable(name = TABLE + "Commands",
- joinColumns =
- @JoinColumn(name = "task_id"),
- inverseJoinColumns =
- @JoinColumn(name = "implementation_id"),
- uniqueConstraints =
- @UniqueConstraint(columnNames = { "task_id", "implementation_id" }))
- private List<JPAImplementation> commands = new ArrayList<>();
+ @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "macroTask")
+ private List<JPAMacroTaskCommand> macroTaskCommands = new ArrayList<>();
- @Lob
- private String commandArgs;
+ @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true, mappedBy = "macroTask")
+ @Valid
+ private List<JPAFormPropertyDef> formPropertyDefs = new ArrayList<>();
- @Transient
- private final List<CommandArgs> commandArgsList = new ArrayList<>();
+ @ManyToOne(fetch = FetchType.EAGER)
+ private JPAImplementation macroActions;
@OneToMany(targetEntity = JPAMacroTaskExec.class,
cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
@@ -102,25 +82,6 @@
}
@Override
- public void add(final Implementation command, final CommandArgs args) {
- checkType(command, JPAImplementation.class);
- checkImplementationType(command, IdRepoImplementationType.COMMAND);
- commands.add((JPAImplementation) command);
-
- getCommandArgs().add(args);
- }
-
- @Override
- public List<JPAImplementation> getCommands() {
- return commands;
- }
-
- @Override
- public List<CommandArgs> getCommandArgs() {
- return commandArgsList;
- }
-
- @Override
public boolean isContinueOnError() {
return continueOnError == null ? false : continueOnError;
}
@@ -150,29 +111,37 @@
return executions;
}
- protected void json2list(final boolean clearFirst) {
- if (clearFirst) {
- getCommandArgs().clear();
- }
- if (commandArgs != null) {
- getCommandArgs().addAll(POJOHelper.deserialize(commandArgs, TYPEREF));
- }
+ @Override
+ public void add(final MacroTaskCommand macroTaskCommand) {
+ checkType(macroTaskCommand, JPAMacroTaskCommand.class);
+ this.macroTaskCommands.add((JPAMacroTaskCommand) macroTaskCommand);
}
- @PostLoad
- public void postLoad() {
- json2list(false);
+ @Override
+ public List<? extends MacroTaskCommand> getCommands() {
+ return macroTaskCommands;
}
- @PostPersist
- @PostUpdate
- public void postSave() {
- json2list(true);
+ @Override
+ public void add(final FormPropertyDef formPropertyDef) {
+ checkType(formPropertyDef, JPAFormPropertyDef.class);
+ this.formPropertyDefs.add((JPAFormPropertyDef) formPropertyDef);
}
- @PrePersist
- @PreUpdate
- public void list2json() {
- commandArgs = POJOHelper.serialize(getCommandArgs(), TYPEREF);
+ @Override
+ public List<? extends FormPropertyDef> getFormPropertyDefs() {
+ return formPropertyDefs;
+ }
+
+ @Override
+ public Implementation getMacroActions() {
+ return macroActions;
+ }
+
+ @Override
+ public void setMacroAction(final Implementation macroActions) {
+ checkType(macroActions, JPAImplementation.class);
+ checkImplementationType(macroActions, IdRepoImplementationType.MACRO_ACTIONS);
+ this.macroActions = (JPAImplementation) macroActions;
}
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java
new file mode 100644
index 0000000..df3bea0
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTaskCommand.java
@@ -0,0 +1,87 @@
+/*
+ * 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.task;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Lob;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.OneToOne;
+import jakarta.persistence.Table;
+import java.util.Optional;
+import org.apache.syncope.common.lib.command.CommandArgs;
+import org.apache.syncope.common.lib.types.IdRepoImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
+import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+
+@Entity
+@Table(name = JPAMacroTaskCommand.TABLE)
+public class JPAMacroTaskCommand extends AbstractGeneratedKeyEntity implements MacroTaskCommand {
+
+ private static final long serialVersionUID = -8388668645348044783L;
+
+ public static final String TABLE = "MacroTaskCommand";
+
+ @ManyToOne(optional = false)
+ private JPAMacroTask macroTask;
+
+ @OneToOne(optional = false)
+ private JPAImplementation command;
+
+ @Lob
+ private String args;
+
+ @Override
+ public JPAMacroTask getMacroTask() {
+ return macroTask;
+ }
+
+ @Override
+ public void setMacroTask(final MacroTask macroTask) {
+ checkType(macroTask, JPAMacroTask.class);
+ this.macroTask = (JPAMacroTask) macroTask;
+ }
+
+ @Override
+ public Implementation getCommand() {
+ return command;
+ }
+
+ @Override
+ public void setCommand(final Implementation command) {
+ checkType(command, JPAImplementation.class);
+ checkImplementationType(command, IdRepoImplementationType.COMMAND);
+ this.command = (JPAImplementation) command;
+ }
+
+ @Override
+ public CommandArgs getArgs() {
+ return Optional.ofNullable(args).
+ map(a -> POJOHelper.deserialize(a, CommandArgs.class)).
+ orElse(null);
+ }
+
+ @Override
+ public void setArgs(final CommandArgs args) {
+ this.args = Optional.ofNullable(args).map(POJOHelper::serialize).orElse(null);
+ }
+}
diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
index 434d824..eef0510 100644
--- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml
@@ -28,18 +28,18 @@
<AnyTypeClass id="BaseGroup"/>
<AnyType_AnyTypeClass anyType_id="GROUP" anyTypeClass_id="BaseGroup"/>
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<SyncopeSchema id="email"/>
<PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
validator_id="EmailAddressValidator"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java
index 5401ef4..db95519 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java
@@ -71,7 +71,7 @@
implementations = implementationDAO.findByType(IdRepoImplementationType.PASSWORD_RULE);
assertEquals(3, implementations.size());
- implementations = implementationDAO.findByType(IdRepoImplementationType.VALIDATOR);
+ implementations = implementationDAO.findByType(IdRepoImplementationType.ATTR_VALUE_VALIDATOR);
assertEquals(2, implementations.size());
implementations = implementationDAO.findByType(IdMImplementationType.PULL_CORRELATION_RULE);
@@ -86,7 +86,7 @@
Implementation impl = entityFactory.newEntity(Implementation.class);
impl.setKey("new");
impl.setEngine(ImplementationEngine.GROOVY);
- impl.setType(IdRepoImplementationType.VALIDATOR);
+ impl.setType(IdRepoImplementationType.ATTR_VALUE_VALIDATOR);
impl.setBody("");
Implementation actual = implementationDAO.save(impl);
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java
index 62ff598..ab6717e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/TaskTest.java
@@ -22,20 +22,32 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.form.FormPropertyType;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoImplementationType;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
import org.apache.syncope.core.persistence.api.dao.TaskDAO;
import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
@@ -62,6 +74,12 @@
@Autowired
private ExternalResourceDAO resourceDAO;
+ @Autowired
+ private RealmDAO realmDAO;
+
+ @Autowired
+ private ImplementationDAO implementationDAO;
+
@Test
public void findByName() {
Optional<SchedTask> task = taskDAO.findByName(TaskType.SCHEDULED, "SampleJob Task");
@@ -149,6 +167,62 @@
}
@Test
+ public void saveMacroTask() throws Exception {
+ MacroTask task = entityFactory.newEntity(MacroTask.class);
+ task.setRealm(realmDAO.getRoot());
+ task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow());
+ task.setName("Macro test");
+ task.setContinueOnError(true);
+
+ Implementation command = entityFactory.newEntity(Implementation.class);
+ command.setKey("command");
+ command.setType(IdRepoImplementationType.COMMAND);
+ command.setEngine(ImplementationEngine.JAVA);
+ command.setBody("clazz");
+ command = implementationDAO.save(command);
+ assertNotNull(command);
+
+ MacroTaskCommand macroTaskCommand = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand.setCommand(command);
+ macroTaskCommand.setMacroTask(task);
+ task.add(macroTaskCommand);
+
+ FormPropertyDef formPropertyDef = entityFactory.newEntity(FormPropertyDef.class);
+ formPropertyDef.setKey("one");
+ formPropertyDef.setName("One");
+ formPropertyDef.setType(FormPropertyType.Enum);
+ formPropertyDef.setMacroTask(task);
+ task.add(formPropertyDef);
+
+ Implementation macroActions = entityFactory.newEntity(Implementation.class);
+ macroActions.setKey("macroActions");
+ macroActions.setType(IdRepoImplementationType.MACRO_ACTIONS);
+ macroActions.setEngine(ImplementationEngine.JAVA);
+ macroActions.setBody("clazz");
+ macroActions = implementationDAO.save(macroActions);
+ assertNotNull(macroActions);
+ task.setMacroAction(macroActions);
+
+ try {
+ taskDAO.save(task);
+ fail();
+ } catch (InvalidEntityException e) {
+ assertNotNull(e);
+ }
+ formPropertyDef.setEnumValues(Map.of("key", "value"));
+
+ task = taskDAO.save(task);
+ assertNotNull(task);
+ assertEquals(1, task.getCommands().size());
+ assertEquals(command, task.getCommands().get(0).getCommand());
+ assertEquals(1, task.getFormPropertyDefs().size());
+ assertEquals(formPropertyDef, task.getFormPropertyDefs().get(0));
+
+ MacroTask actual = (MacroTask) taskDAO.findById(TaskType.MACRO, task.getKey()).orElseThrow();
+ assertEquals(task, actual);
+ }
+
+ @Test
public void delete() {
PropagationTask task = (PropagationTask) taskDAO.findById(
TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow();
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
index 468cc74..42f6fe7 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java
@@ -33,6 +33,7 @@
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ExecStatus;
import org.apache.syncope.common.lib.types.IdMImplementationType;
+import org.apache.syncope.common.lib.types.IdRepoImplementationType;
import org.apache.syncope.common.lib.types.ImplementationEngine;
import org.apache.syncope.common.lib.types.MatchingRule;
import org.apache.syncope.common.lib.types.PullMode;
@@ -47,6 +48,8 @@
import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
import org.apache.syncope.core.persistence.api.entity.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.PullTask;
@@ -294,6 +297,56 @@
}
@Test
+ public void saveMacroTaskSameCommandMultipleOccurrencies() {
+ MacroTask task = entityFactory.newEntity(MacroTask.class);
+ task.setRealm(realmDAO.getRoot());
+ task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow());
+ task.setName("saveMacroTaskSameCommandMultipleOccurrencies");
+ task.setContinueOnError(true);
+
+ Implementation command1 = entityFactory.newEntity(Implementation.class);
+ command1.setKey("command1");
+ command1.setType(IdRepoImplementationType.COMMAND);
+ command1.setEngine(ImplementationEngine.JAVA);
+ command1.setBody("clazz1");
+ command1 = implementationDAO.save(command1);
+ assertNotNull(command1);
+
+ Implementation command2 = entityFactory.newEntity(Implementation.class);
+ command2.setKey("command2");
+ command2.setType(IdRepoImplementationType.COMMAND);
+ command2.setEngine(ImplementationEngine.JAVA);
+ command2.setBody("clazz2");
+ command2 = implementationDAO.save(command2);
+ assertNotNull(command2);
+
+ MacroTaskCommand macroTaskCommand1 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand1.setCommand(command1);
+ macroTaskCommand1.setMacroTask(task);
+ task.add(macroTaskCommand1);
+
+ MacroTaskCommand macroTaskCommand2 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand2.setCommand(command2);
+ macroTaskCommand2.setMacroTask(task);
+ task.add(macroTaskCommand2);
+
+ MacroTaskCommand macroTaskCommand3 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand3.setCommand(command1);
+ macroTaskCommand3.setMacroTask(task);
+ task.add(macroTaskCommand3);
+
+ task = (MacroTask) taskDAO.save(task);
+ assertNotNull(task);
+ assertEquals(3, task.getCommands().size());
+ assertEquals(command1, task.getCommands().get(0).getCommand());
+ assertEquals(command2, task.getCommands().get(1).getCommand());
+ assertEquals(command1, task.getCommands().get(2).getCommand());
+
+ MacroTask actual = (MacroTask) taskDAO.findById(TaskType.MACRO, task.getKey()).orElseThrow();
+ assertEquals(task, actual);
+ }
+
+ @Test
public void issueSYNCOPE144() {
ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow();
assertNotNull(resource);
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index f84831c..207a9d6 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -301,7 +301,7 @@
<PlainSchema id="fullname" type="String" anyTypeClass_id="minimal user"
mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"/>
<SyncopeSchema id="userId"/>
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<PlainSchema id="userId" type="String" anyTypeClass_id="minimal user"
mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"
@@ -352,7 +352,7 @@
mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
mimeType="image/jpeg"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
<SyncopeSchema id="csvuserid"/>
@@ -766,8 +766,8 @@
<VirSchema id="virtualdata" READONLY="0" anyTypeClass_id="minimal user"
resource_id="resource-db-virattr" anyType_id="USER" extAttrName="USERNAME"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
index 805d0ac..1a9a25d 100644
--- a/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/TwoContent.xml
@@ -26,18 +26,18 @@
<AnyType id="GROUP" kind="GROUP"/>
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<SyncopeSchema id="email"/>
<PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
validator_id="EmailAddressValidator"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.java
index 653605e..83a4f68 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/ContentLoaderHandler.java
@@ -45,6 +45,7 @@
import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy;
import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask;
+import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTaskCommandRelationship;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jProvisioningTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPullTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPushTask;
@@ -281,8 +282,9 @@
rightId,
rel.getType(),
Optional.ofNullable(rel.getRelationshipPropertiesEntity()).
- filter(rpe -> Neo4jImplementationRelationship.class.getSimpleName().
- equals(rpe.getPrimaryLabel())).map(rpe -> indexValue).orElse(null)));
+ filter(e -> Neo4jImplementationRelationship.class.getSimpleName().equals(e.getPrimaryLabel())
+ || Neo4jMacroTaskCommandRelationship.class.getSimpleName().equals(e.getPrimaryLabel())).
+ map(e -> indexValue).orElse(null)));
}
@Override
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java
index 107f421..015e7c6 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java
@@ -43,6 +43,7 @@
import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema;
import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask;
+import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTaskCommandRelationship;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jProvisioningTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jSchedTask;
import org.neo4j.driver.Driver;
@@ -169,16 +170,16 @@
rattrs.addAttribute("", "", "right", "CDATA", rightId);
Optional.ofNullable(relDesc.get().getRelationshipPropertiesEntity()).
- filter(rpe -> Neo4jImplementationRelationship.class.getSimpleName().
- equals(rpe.getPrimaryLabel())).ifPresent(rpe -> {
-
- String index = String.valueOf(session.run(
- "MATCH (n {id: $left})-[r:" + relDesc.get().getType() + "]-" + "(m {id: $right}) "
- + "RETURN r.index",
- Map.of("left", node.get("id").asString(), "right", rightId)).
- single().get("r.index").asInt());
- rattrs.addAttribute("", "", "index", "CDATA", index);
- });
+ filter(e -> Neo4jImplementationRelationship.class.getSimpleName().equals(e.getPrimaryLabel())
+ || Neo4jMacroTaskCommandRelationship.class.getSimpleName().equals(e.getPrimaryLabel())).
+ ifPresent(rpe -> {
+ String index = String.valueOf(session.run(
+ "MATCH (n {id: $left})-[r:" + relDesc.get().getType() + "]-" + "(m {id: $right}) "
+ + "RETURN r.index",
+ Map.of("left", node.get("id").asString(), "right", rightId)).
+ single().get("r.index").asInt());
+ rattrs.addAttribute("", "", "index", "CDATA", index);
+ });
String elementName = entity.getPrimaryLabel()
+ "_"
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java
index 8441aa9..cc8a3db 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java
@@ -54,7 +54,9 @@
import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm;
import org.apache.syncope.core.persistence.neo4j.entity.task.AbstractTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jAnyTemplatePullTask;
+import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jFormPropertyDef;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask;
+import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTaskCommand;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jNotificationTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPropagationTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPropagationTaskExec;
@@ -452,8 +454,6 @@
notificationTask.list2json();
case Neo4jPushTask pushTask ->
pushTask.map2json();
- case Neo4jMacroTask macroTask ->
- macroTask.list2json();
default -> {
}
}
@@ -497,18 +497,17 @@
Neo4jPushTask.PUSH_TASK_PUSH_ACTIONS_REL)));
}
- case Neo4jMacroTask macroTask -> {
- macroTask.postSave();
-
- neo4jTemplate.findById(macroTask.getKey(), Neo4jMacroTask.class).
- ifPresent(t -> t.getCommands().stream().filter(cmd -> !macroTask.getCommands().contains(cmd)).
- forEach(impl -> deleteRelationship(
- Neo4jMacroTask.NODE,
- Neo4jImplementation.NODE,
- macroTask.getKey(),
- impl.getKey(),
- Neo4jMacroTask.MACRO_TASK_COMMANDS_REL)));
- }
+ case Neo4jMacroTask macroTask ->
+ neo4jTemplate.findById(macroTask.getKey(), Neo4jMacroTask.class).ifPresent(t -> {
+ if (t.getMacroActions() != null && macroTask.getMacroActions() == null) {
+ deleteRelationship(
+ Neo4jMacroTask.NODE,
+ Neo4jImplementation.NODE,
+ macroTask.getKey(),
+ t.getMacroActions().getKey(),
+ Neo4jMacroTask.MACRO_TASK_MACRO_ACTIONS_REL);
+ }
+ });
default -> {
}
@@ -529,10 +528,22 @@
@Override
public void delete(final Task<?> task) {
- if (task instanceof PullTask pullTask) {
- remediationDAO.findByPullTask(pullTask).forEach(remediation -> remediation.setPullTask(null));
- pullTask.getTemplates().
- forEach(template -> neo4jTemplate.deleteById(template.getKey(), Neo4jAnyTemplatePullTask.class));
+ switch (task) {
+ case PullTask pullTask -> {
+ remediationDAO.findByPullTask(pullTask).forEach(remediation -> remediation.setPullTask(null));
+ pullTask.getTemplates().
+ forEach(e -> neo4jTemplate.deleteById(e.getKey(), Neo4jAnyTemplatePullTask.class));
+ }
+
+ case MacroTask macroTask -> {
+ macroTask.getCommands().
+ forEach(e -> neo4jTemplate.deleteById(e.getKey(), Neo4jMacroTaskCommand.class));
+ macroTask.getFormPropertyDefs().
+ forEach(e -> neo4jTemplate.deleteById(e.getKey(), Neo4jFormPropertyDef.class));
+ }
+
+ default -> {
+ }
}
TaskUtils taskUtils = taskUtilsFactory.getInstance(task);
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java
index 8db549c..5c8932c 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java
@@ -88,7 +88,9 @@
import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy;
import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.PullTask;
@@ -143,7 +145,9 @@
import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy;
import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jAnyTemplatePullTask;
+import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jFormPropertyDef;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask;
+import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTaskCommand;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jNotificationTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPropagationTask;
import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jPullTask;
@@ -280,6 +284,10 @@
result = (E) new Neo4jSchedTask();
} else if (reference.equals(AnyTemplatePullTask.class)) {
result = (E) new Neo4jAnyTemplatePullTask();
+ } else if (reference.equals(MacroTaskCommand.class)) {
+ result = (E) new Neo4jMacroTaskCommand();
+ } else if (reference.equals(FormPropertyDef.class)) {
+ result = (E) new Neo4jFormPropertyDef();
} else if (reference.equals(SecurityQuestion.class)) {
result = (E) new Neo4jSecurityQuestion();
} else if (reference.equals(AuditConf.class)) {
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java
index 12d564c..433a83b 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java
@@ -157,7 +157,8 @@
private SortedSet<Neo4jImplementationRelationship> propagationActions = new TreeSet<>();
@Transient
- private List<Neo4jImplementation> sortedPropagationActions = new SortedSetList(propagationActions);
+ private List<Neo4jImplementation> sortedPropagationActions = new SortedSetList<>(
+ propagationActions, Neo4jImplementationRelationship.builder());
@Override
public boolean isEnforceMandatoryCondition() {
@@ -383,7 +384,7 @@
@PostLoad
public void postLoad() {
- sortedPropagationActions = new SortedSetList(propagationActions);
+ sortedPropagationActions = new SortedSetList<>(propagationActions, Neo4jImplementationRelationship.builder());
json2list(false);
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementationRelationship.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementationRelationship.java
index 2ac5c75..7e148d3 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementationRelationship.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jImplementationRelationship.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.persistence.neo4j.entity;
+import java.util.function.BiFunction;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.springframework.data.neo4j.core.schema.RelationshipId;
@@ -25,7 +26,13 @@
import org.springframework.data.neo4j.core.schema.TargetNode;
@RelationshipProperties
-public class Neo4jImplementationRelationship implements Comparable<Neo4jImplementationRelationship> {
+public class Neo4jImplementationRelationship
+ extends Neo4jSortedRelationsihip<Neo4jImplementation>
+ implements Comparable<Neo4jImplementationRelationship> {
+
+ public static BiFunction<Integer, Neo4jImplementation, Neo4jImplementationRelationship> builder() {
+ return (Integer i, Neo4jImplementation e) -> new Neo4jImplementationRelationship(i, e);
+ }
@RelationshipId
private Long id;
@@ -40,11 +47,13 @@
this.implementation = implementation;
}
+ @Override
public int getIndex() {
return index;
}
- public Neo4jImplementation getImplementation() {
+ @Override
+ public Neo4jImplementation getEntity() {
return implementation;
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java
index 76af029..d9dcf06 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jPlainSchema.java
@@ -196,7 +196,7 @@
@Override
public void setValidator(final Implementation validator) {
checkType(validator, Neo4jImplementation.class);
- checkImplementationType(validator, IdRepoImplementationType.VALIDATOR);
+ checkImplementationType(validator, IdRepoImplementationType.ATTR_VALUE_VALIDATOR);
this.validator = (Neo4jImplementation) validator;
}
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java
index 9241456..5fa19b1 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jRealm.java
@@ -106,7 +106,8 @@
private SortedSet<Neo4jImplementationRelationship> actions = new TreeSet<>();
@Transient
- private List<Neo4jImplementation> sortedActions = new SortedSetList(actions);
+ private List<Neo4jImplementation> sortedActions = new SortedSetList<>(
+ actions, Neo4jImplementationRelationship.builder());
@Relationship(type = Neo4jAnyTemplateRealm.REALM_ANY_TEMPLATE_REL, direction = Relationship.Direction.INCOMING)
private List<Neo4jAnyTemplateRealm> templates = new ArrayList<>();
@@ -258,6 +259,6 @@
@PostLoad
public void postLoad() {
- sortedActions = new SortedSetList(actions);
+ sortedActions = new SortedSetList<>(actions, Neo4jImplementationRelationship.builder());
}
}
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSortedRelationsihip.java
similarity index 75%
copy from ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
copy to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSortedRelationsihip.java
index 5cd31c7..d4d5156 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jSortedRelationsihip.java
@@ -16,16 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.common.lib.types;
+package org.apache.syncope.core.persistence.neo4j.entity;
-public enum UserRequestFormPropertyType {
+import org.apache.syncope.core.persistence.api.entity.Entity;
- String,
- Long,
- Enum,
- Date,
- Boolean,
- Dropdown,
- Password
+public abstract class Neo4jSortedRelationsihip<E extends Entity> {
+ public abstract int getIndex();
+
+ public abstract E getEntity();
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/SortedSetList.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/SortedSetList.java
index 6fef725..5a679c8 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/SortedSetList.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/SortedSetList.java
@@ -25,16 +25,18 @@
import java.util.ListIterator;
import java.util.SortedSet;
import java.util.Spliterator;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Stream;
+import org.apache.syncope.core.persistence.api.entity.Entity;
-public class SortedSetList implements List<Neo4jImplementation> {
+public class SortedSetList<E extends Entity, R extends Neo4jSortedRelationsihip<E>> implements List<E> {
- private static class SortedSetListIterator implements Iterator<Neo4jImplementation> {
+ private class SortedSetListIterator implements Iterator<E> {
- private final Iterator<Neo4jImplementationRelationship> ditor;
+ private final Iterator<R> ditor;
- SortedSetListIterator(final Iterator<Neo4jImplementationRelationship> ditor) {
+ SortedSetListIterator(final Iterator<R> ditor) {
this.ditor = ditor;
}
@@ -44,8 +46,8 @@
}
@Override
- public Neo4jImplementation next() {
- return ditor.next().getImplementation();
+ public E next() {
+ return ditor.next().getEntity();
}
@Override
@@ -54,21 +56,21 @@
}
}
- private static class SortedSetListSplitIterator implements Spliterator<Neo4jImplementation> {
+ private class SortedSetListSplitIterator implements Spliterator<E> {
- private final Spliterator<Neo4jImplementationRelationship> ditor;
+ private final Spliterator<R> ditor;
- SortedSetListSplitIterator(final Spliterator<Neo4jImplementationRelationship> ditor) {
+ SortedSetListSplitIterator(final Spliterator<R> ditor) {
this.ditor = ditor;
}
@Override
- public boolean tryAdvance(final Consumer<? super Neo4jImplementation> action) {
- return ditor.tryAdvance(t -> action.accept(t.getImplementation()));
+ public boolean tryAdvance(final Consumer<? super E> action) {
+ return ditor.tryAdvance(t -> action.accept(t.getEntity()));
}
@Override
- public Spliterator<Neo4jImplementation> trySplit() {
+ public Spliterator<E> trySplit() {
return new SortedSetListSplitIterator(ditor.trySplit());
}
@@ -83,15 +85,21 @@
}
@Override
- public Comparator<? super Neo4jImplementation> getComparator() {
+ public Comparator<? super E> getComparator() {
throw new UnsupportedOperationException("NOT FOR NOW");
}
}
- private final SortedSet<Neo4jImplementationRelationship> delegate;
+ private final SortedSet<R> delegate;
- public SortedSetList(final SortedSet<Neo4jImplementationRelationship> delegate) {
+ private final BiFunction<Integer, E, R> builder;
+
+ public SortedSetList(
+ final SortedSet<R> delegate,
+ final BiFunction<Integer, E, R> builder) {
+
this.delegate = delegate;
+ this.builder = builder;
}
@Override
@@ -106,47 +114,44 @@
@Override
public boolean contains(final Object o) {
- return delegate.stream().anyMatch(e -> e.getImplementation().equals(o));
+ return delegate.stream().anyMatch(e -> e.getEntity().equals(o));
}
@Override
- public Iterator<Neo4jImplementation> iterator() {
+ public Iterator<E> iterator() {
return new SortedSetListIterator(delegate.iterator());
}
@Override
- public Spliterator<Neo4jImplementation> spliterator() {
+ public Spliterator<E> spliterator() {
return new SortedSetListSplitIterator(delegate.spliterator());
}
@Override
public Object[] toArray() {
return delegate.stream().
- sorted(Comparator.comparing(Neo4jImplementationRelationship::getIndex)).
- map(Neo4jImplementationRelationship::getImplementation).toList().toArray();
+ sorted(Comparator.comparing(Neo4jSortedRelationsihip::getIndex)).
+ map(Neo4jSortedRelationsihip::getEntity).toList().toArray();
}
@Override
public <T> T[] toArray(final T[] a) {
return delegate.stream().
- sorted(Comparator.comparing(Neo4jImplementationRelationship::getIndex)).
- map(Neo4jImplementationRelationship::getImplementation).toList().toArray(a);
+ sorted(Comparator.comparing(Neo4jSortedRelationsihip::getIndex)).
+ map(Neo4jSortedRelationsihip::getEntity).toList().toArray(a);
}
@Override
- public boolean add(final Neo4jImplementation e) {
- return delegate.add(new Neo4jImplementationRelationship(
- delegate.stream().map(Neo4jImplementationRelationship::getIndex).
+ public boolean add(final E e) {
+ return delegate.add(builder.apply(
+ delegate.stream().map(Neo4jSortedRelationsihip::getIndex).
max(Comparator.naturalOrder()).orElse(0) + 1,
e));
}
@Override
public boolean remove(final Object o) {
- if (o instanceof Neo4jImplementation impl) {
- return delegate.removeIf(impl::equals);
- }
- return false;
+ return delegate.removeIf(o::equals);
}
@Override
@@ -155,12 +160,12 @@
}
@Override
- public boolean addAll(final Collection<? extends Neo4jImplementation> c) {
+ public boolean addAll(final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
- public boolean addAll(final int index, final Collection<? extends Neo4jImplementation> c) {
+ public boolean addAll(final int index, final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@@ -180,19 +185,18 @@
}
@Override
- public Neo4jImplementation get(final int index) {
+ public E get(final int index) {
if (index < 0 || index >= delegate.size()) {
throw new IndexOutOfBoundsException();
}
int idx = 0;
- for (Iterator<Neo4jImplementationRelationship> itor = delegate.iterator();
- idx <= index && itor.hasNext();
- idx++) {
+ for (Iterator<R> itor = delegate.iterator();
+ idx <= index && itor.hasNext(); idx++) {
- Neo4jImplementationRelationship next = itor.next();
+ Neo4jSortedRelationsihip<E> next = itor.next();
if (idx == index) {
- return next.getImplementation();
+ return next.getEntity();
}
}
@@ -200,8 +204,8 @@
}
@Override
- public Stream<Neo4jImplementation> stream() {
- return delegate.stream().map(Neo4jImplementationRelationship::getImplementation);
+ public Stream<E> stream() {
+ return delegate.stream().map(Neo4jSortedRelationsihip::getEntity);
}
@Override
@@ -210,17 +214,17 @@
}
@Override
- public Neo4jImplementation set(final int index, final Neo4jImplementation element) {
+ public E set(final int index, final E element) {
throw new UnsupportedOperationException();
}
@Override
- public void add(final int index, final Neo4jImplementation element) {
+ public void add(final int index, final E element) {
throw new UnsupportedOperationException();
}
@Override
- public Neo4jImplementation remove(final int index) {
+ public E remove(final int index) {
throw new UnsupportedOperationException();
}
@@ -235,17 +239,17 @@
}
@Override
- public ListIterator<Neo4jImplementation> listIterator() {
+ public ListIterator<E> listIterator() {
throw new UnsupportedOperationException();
}
@Override
- public ListIterator<Neo4jImplementation> listIterator(final int index) {
+ public ListIterator<E> listIterator(final int index) {
throw new UnsupportedOperationException();
}
@Override
- public List<Neo4jImplementation> subList(final int fromIndex, final int toIndex) {
+ public List<E> subList(final int fromIndex, final int toIndex) {
throw new UnsupportedOperationException();
}
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.java
index 8d680ea..4a8ccd6 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jAccountPolicy.java
@@ -51,7 +51,8 @@
private SortedSet<Neo4jImplementationRelationship> rules = new TreeSet<>();
@Transient
- private List<Neo4jImplementation> sortedRules = new SortedSetList(rules);
+ private List<Neo4jImplementation> sortedRules = new SortedSetList<>(
+ rules, Neo4jImplementationRelationship.builder());
@Override
public boolean isPropagateSuspension() {
@@ -87,6 +88,6 @@
@PostLoad
public void postLoad() {
- sortedRules = new SortedSetList(rules);
+ sortedRules = new SortedSetList<>(rules, Neo4jImplementationRelationship.builder());
}
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.java
index 7f6c023..f4d1614 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPasswordPolicy.java
@@ -51,7 +51,8 @@
private SortedSet<Neo4jImplementationRelationship> rules = new TreeSet<>();
@Transient
- private List<Neo4jImplementation> sortedRules = new SortedSetList(rules);
+ private List<Neo4jImplementation> sortedRules = new SortedSetList<>(
+ rules, Neo4jImplementationRelationship.builder());
@Override
public boolean isAllowNullPassword() {
@@ -87,6 +88,6 @@
@PostLoad
public void postLoad() {
- sortedRules = new SortedSetList(rules);
+ sortedRules = new SortedSetList<>(rules, Neo4jImplementationRelationship.builder());
}
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java
new file mode 100644
index 0000000..af494c8
--- /dev/null
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java
@@ -0,0 +1,147 @@
+/*
+ * 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.neo4j.entity.task;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import jakarta.validation.constraints.NotNull;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.common.validation.FormPropertyDefCheck;
+import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.springframework.data.neo4j.core.schema.Node;
+import org.springframework.data.neo4j.core.schema.Relationship;
+
+@Node(Neo4jFormPropertyDef.NODE)
+@FormPropertyDefCheck
+public class Neo4jFormPropertyDef extends AbstractProvidedKeyNode implements FormPropertyDef {
+
+ private static final long serialVersionUID = -5839990371546587373L;
+
+ public static final String NODE = "FormPropertyDef";
+
+ @NotNull
+ @Relationship(type = Neo4jMacroTask.MACRO_TASK_FORM_PROPERTY_DEF_REL, direction = Relationship.Direction.OUTGOING)
+ private Neo4jMacroTask macroTask;
+
+ @NotNull
+ private String name;
+
+ @NotNull
+ private FormPropertyType type;
+
+ @NotNull
+ private Boolean readable = Boolean.TRUE;
+
+ @NotNull
+ private Boolean writable = Boolean.TRUE;
+
+ @NotNull
+ private Boolean required = Boolean.FALSE;
+
+ private String datePattern;
+
+ private String enumValues;
+
+ @Override
+ public Neo4jMacroTask getMacroTask() {
+ return macroTask;
+ }
+
+ @Override
+ public void setMacroTask(final MacroTask macroTask) {
+ checkType(macroTask, Neo4jMacroTask.class);
+ this.macroTask = (Neo4jMacroTask) macroTask;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public FormPropertyType getType() {
+ return type;
+ }
+
+ @Override
+ public void setType(final FormPropertyType type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean isReadable() {
+ return readable == null ? true : readable;
+ }
+
+ @Override
+ public void setReadable(final boolean readable) {
+ this.readable = readable;
+ }
+
+ @Override
+ public boolean isWritable() {
+ return writable == null ? true : writable;
+ }
+
+ @Override
+ public void setWritable(final boolean writable) {
+ this.writable = writable;
+ }
+
+ @Override
+ public boolean isRequired() {
+ return required == null ? false : required;
+ }
+
+ @Override
+ public void setRequired(final boolean required) {
+ this.required = required;
+ }
+
+ @Override
+ public String getDatePattern() {
+ return datePattern;
+ }
+
+ @Override
+ public void setDatePattern(final String datePattern) {
+ this.datePattern = datePattern;
+ }
+
+ @Override
+ public Map<String, String> getEnumValues() {
+ return Optional.ofNullable(enumValues).
+ map(v -> POJOHelper.deserialize(v, new TypeReference<Map<String, String>>() {
+ })).orElse(Map.of());
+ }
+
+ @Override
+ public void setEnumValues(final Map<String, String> enumValues) {
+ this.enumValues = Optional.ofNullable(enumValues).map(POJOHelper::serialize).orElse(null);
+ }
+}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java
index a9f1e06..ff83035 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java
@@ -18,24 +18,23 @@
*/
package org.apache.syncope.core.persistence.neo4j.entity.task;
-import com.fasterxml.jackson.core.type.TypeReference;
+import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
-import org.apache.syncope.common.lib.command.CommandArgs;
import org.apache.syncope.common.lib.types.IdRepoImplementationType;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation;
-import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementationRelationship;
import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm;
import org.apache.syncope.core.persistence.neo4j.entity.SortedSetList;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.springframework.data.annotation.Transient;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.PostLoad;
@@ -48,13 +47,14 @@
public static final String NODE = "MacroTask";
+ public static final String MACRO_TASK_FORM_PROPERTY_DEF_REL = "MACRO_TASK_FORM_PROPERTY_DEF_REL";
+
+ public static final String MACRO_TASK_MACRO_ACTIONS_REL = "MACRO_TASK_MACRO_ACTIONS";
+
public static final String MACRO_TASK_EXEC_REL = "MACRO_TASK_EXEC";
public static final String MACRO_TASK_COMMANDS_REL = "MACRO_TASK_COMMANDS";
- protected static final TypeReference<List<CommandArgs>> TYPEREF = new TypeReference<List<CommandArgs>>() {
- };
-
@NotNull
@Relationship(direction = Relationship.Direction.OUTGOING)
private Neo4jRealm realm;
@@ -66,15 +66,18 @@
private Boolean saveExecs = true;
@Relationship(type = MACRO_TASK_COMMANDS_REL, direction = Relationship.Direction.OUTGOING)
- private SortedSet<Neo4jImplementationRelationship> commands = new TreeSet<>();
+ private SortedSet<Neo4jMacroTaskCommandRelationship> commands = new TreeSet<>();
@Transient
- private List<Neo4jImplementation> sortedCommands = new SortedSetList(commands);
+ private List<Neo4jMacroTaskCommand> sortedCommands = new SortedSetList<>(
+ commands, Neo4jMacroTaskCommandRelationship.builder());
- private String commandArgs;
+ @Relationship(type = MACRO_TASK_FORM_PROPERTY_DEF_REL, direction = Relationship.Direction.INCOMING)
+ @Valid
+ private List<Neo4jFormPropertyDef> formPropertyDefs = new ArrayList<>();
- @Transient
- private final List<CommandArgs> commandArgsList = new ArrayList<>();
+ @Relationship(type = MACRO_TASK_MACRO_ACTIONS_REL, direction = Relationship.Direction.OUTGOING)
+ private Neo4jImplementation macroActions;
@Relationship(type = MACRO_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING)
private List<Neo4jMacroTaskExec> executions = new ArrayList<>();
@@ -91,24 +94,6 @@
}
@Override
- public void add(final Implementation command, final CommandArgs args) {
- checkType(command, Neo4jImplementation.class);
- checkImplementationType(command, IdRepoImplementationType.COMMAND);
- sortedCommands.add((Neo4jImplementation) command);
- getCommandArgs().add(args);
- }
-
- @Override
- public List<Neo4jImplementation> getCommands() {
- return sortedCommands;
- }
-
- @Override
- public List<CommandArgs> getCommandArgs() {
- return commandArgsList;
- }
-
- @Override
public boolean isContinueOnError() {
return continueOnError == null ? false : continueOnError;
}
@@ -143,26 +128,42 @@
return executions;
}
- protected void json2list(final boolean clearFirst) {
- if (clearFirst) {
- getCommandArgs().clear();
- }
- if (commandArgs != null) {
- getCommandArgs().addAll(POJOHelper.deserialize(commandArgs, TYPEREF));
- }
+ @Override
+ public void add(final MacroTaskCommand macroTaskCommand) {
+ checkType(macroTaskCommand, Neo4jMacroTaskCommand.class);
+ sortedCommands.add((Neo4jMacroTaskCommand) macroTaskCommand);
+ }
+
+ @Override
+ public List<? extends MacroTaskCommand> getCommands() {
+ return sortedCommands;
+ }
+
+ @Override
+ public void add(final FormPropertyDef formPropertyDef) {
+ checkType(formPropertyDef, Neo4jFormPropertyDef.class);
+ this.formPropertyDefs.add((Neo4jFormPropertyDef) formPropertyDef);
+ }
+
+ @Override
+ public List<? extends FormPropertyDef> getFormPropertyDefs() {
+ return formPropertyDefs;
+ }
+
+ @Override
+ public Implementation getMacroActions() {
+ return macroActions;
+ }
+
+ @Override
+ public void setMacroAction(final Implementation macroActions) {
+ checkType(macroActions, Neo4jImplementation.class);
+ checkImplementationType(macroActions, IdRepoImplementationType.MACRO_ACTIONS);
+ this.macroActions = (Neo4jImplementation) macroActions;
}
@PostLoad
public void postLoad() {
- sortedCommands = new SortedSetList(commands);
- json2list(false);
- }
-
- public void postSave() {
- json2list(true);
- }
-
- public void list2json() {
- commandArgs = POJOHelper.serialize(getCommandArgs(), TYPEREF);
+ sortedCommands = new SortedSetList<>(commands, Neo4jMacroTaskCommandRelationship.builder());
}
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskCommand.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskCommand.java
new file mode 100644
index 0000000..11af50b
--- /dev/null
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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.neo4j.entity.task;
+
+import jakarta.validation.constraints.NotNull;
+import java.util.Optional;
+import org.apache.syncope.common.lib.command.CommandArgs;
+import org.apache.syncope.common.lib.types.IdRepoImplementationType;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
+import org.apache.syncope.core.persistence.neo4j.entity.AbstractProvidedKeyNode;
+import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.springframework.data.neo4j.core.schema.Node;
+import org.springframework.data.neo4j.core.schema.Relationship;
+
+@Node(Neo4jMacroTaskCommand.NODE)
+public class Neo4jMacroTaskCommand extends AbstractProvidedKeyNode implements MacroTaskCommand {
+
+ private static final long serialVersionUID = -8388668645348044783L;
+
+ public static final String NODE = "MacroTaskCommand";
+
+ @NotNull
+ @Relationship(type = Neo4jMacroTask.MACRO_TASK_COMMANDS_REL, direction = Relationship.Direction.OUTGOING)
+ private Neo4jMacroTask macroTask;
+
+ @NotNull
+ @Relationship(direction = Relationship.Direction.OUTGOING)
+ private Neo4jImplementation command;
+
+ private String args;
+
+ @Override
+ public Neo4jMacroTask getMacroTask() {
+ return macroTask;
+ }
+
+ @Override
+ public void setMacroTask(final MacroTask macroTask) {
+ checkType(macroTask, Neo4jMacroTask.class);
+ this.macroTask = (Neo4jMacroTask) macroTask;
+ }
+
+ @Override
+ public Implementation getCommand() {
+ return command;
+ }
+
+ @Override
+ public void setCommand(final Implementation command) {
+ checkType(command, Neo4jImplementation.class);
+ checkImplementationType(command, IdRepoImplementationType.COMMAND);
+ this.command = (Neo4jImplementation) command;
+ }
+
+ @Override
+ public CommandArgs getArgs() {
+ return Optional.ofNullable(args).
+ map(a -> POJOHelper.deserialize(a, CommandArgs.class)).
+ orElse(null);
+ }
+
+ @Override
+ public void setArgs(final CommandArgs args) {
+ this.args = Optional.ofNullable(args).map(POJOHelper::serialize).orElse(null);
+ }
+}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskCommandRelationship.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskCommandRelationship.java
new file mode 100644
index 0000000..4aae340
--- /dev/null
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTaskCommandRelationship.java
@@ -0,0 +1,100 @@
+/*
+ * 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.neo4j.entity.task;
+
+import java.util.function.BiFunction;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSortedRelationsihip;
+import org.springframework.data.neo4j.core.schema.RelationshipId;
+import org.springframework.data.neo4j.core.schema.RelationshipProperties;
+import org.springframework.data.neo4j.core.schema.TargetNode;
+
+@RelationshipProperties
+public class Neo4jMacroTaskCommandRelationship
+ extends Neo4jSortedRelationsihip<Neo4jMacroTaskCommand>
+ implements Comparable<Neo4jMacroTaskCommandRelationship> {
+
+ public static BiFunction<Integer, Neo4jMacroTaskCommand, Neo4jMacroTaskCommandRelationship> builder() {
+ return (Integer i, Neo4jMacroTaskCommand e) -> new Neo4jMacroTaskCommandRelationship(i, e);
+ }
+
+ @RelationshipId
+ private Long id;
+
+ private int index;
+
+ @TargetNode
+ private Neo4jMacroTaskCommand command;
+
+ public Neo4jMacroTaskCommandRelationship(final int index, final Neo4jMacroTaskCommand command) {
+ this.index = index;
+ this.command = command;
+ }
+
+ @Override
+ public int getIndex() {
+ return index;
+ }
+
+ @Override
+ public Neo4jMacroTaskCommand getEntity() {
+ return command;
+ }
+
+ @Override
+ public int compareTo(final Neo4jMacroTaskCommandRelationship object) {
+ return Integer.compare(index, object.getIndex());
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ Neo4jMacroTaskCommandRelationship other = (Neo4jMacroTaskCommandRelationship) obj;
+ return new EqualsBuilder().
+ append(index, other.index).
+ append(command, other.command).
+ build();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().
+ append(index).
+ append(command).
+ build();
+ }
+
+ @Override
+ public String toString() {
+ return "Neo4jMacroTaskCommandRelationship{"
+ + "id=" + id
+ + ", index=" + index
+ + ", command=" + command
+ + '}';
+ }
+}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java
index 8aba681..23a5f1f 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java
@@ -70,7 +70,8 @@
private SortedSet<Neo4jImplementationRelationship> actions = new TreeSet<>();
@Transient
- private List<Neo4jImplementation> sortedActions = new SortedSetList(actions);
+ private List<Neo4jImplementation> sortedActions = new SortedSetList<>(
+ actions, Neo4jImplementationRelationship.builder());
@Relationship(type = PULL_TASK_TEMPLATE_REL, direction = Relationship.Direction.INCOMING)
private List<Neo4jAnyTemplatePullTask> templates = new ArrayList<>();
@@ -171,6 +172,6 @@
@PostLoad
public void postLoad() {
- sortedActions = new SortedSetList(actions);
+ sortedActions = new SortedSetList<>(actions, Neo4jImplementationRelationship.builder());
}
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java
index 41a895d..1343cb6 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPushTask.java
@@ -71,7 +71,8 @@
private SortedSet<Neo4jImplementationRelationship> actions = new TreeSet<>();
@Transient
- private List<Neo4jImplementation> sortedActions = new SortedSetList(actions);
+ private List<Neo4jImplementation> sortedActions = new SortedSetList<>(
+ actions, Neo4jImplementationRelationship.builder());
@Relationship(type = PUSH_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING)
private List<Neo4jPushTaskExec> executions = new ArrayList<>();
@@ -135,7 +136,7 @@
@PostLoad
public void postLoad() {
- sortedActions = new SortedSetList(actions);
+ sortedActions = new SortedSetList<>(actions, Neo4jImplementationRelationship.builder());
json2map(false);
}
diff --git a/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml b/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml
index adff541..256e121 100644
--- a/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml
@@ -29,18 +29,18 @@
<AnyType_AnyTypeClass anyType_id="GROUP" anyTypeClass_id="BaseGroup"/>
<!-- Actual plain schemas -->
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<SyncopeSchema id="email"/>
<PlainSchema id="email" type="String" anyTypeClass_id="BaseUser"
mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
validator_id="EmailAddressValidator"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java
index 839739e..169aa71 100644
--- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java
+++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java
@@ -71,7 +71,7 @@
implementations = implementationDAO.findByType(IdRepoImplementationType.PASSWORD_RULE);
assertEquals(3, implementations.size());
- implementations = implementationDAO.findByType(IdRepoImplementationType.VALIDATOR);
+ implementations = implementationDAO.findByType(IdRepoImplementationType.ATTR_VALUE_VALIDATOR);
assertEquals(2, implementations.size());
implementations = implementationDAO.findByType(IdMImplementationType.PULL_CORRELATION_RULE);
@@ -86,7 +86,7 @@
Implementation impl = entityFactory.newEntity(Implementation.class);
impl.setKey("new");
impl.setEngine(ImplementationEngine.GROOVY);
- impl.setType(IdRepoImplementationType.VALIDATOR);
+ impl.setType(IdRepoImplementationType.ATTR_VALUE_VALIDATOR);
impl.setBody("");
Implementation actual = implementationDAO.save(impl);
diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java
index 49c6927..50abb88 100644
--- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java
+++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/TaskTest.java
@@ -22,20 +22,32 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.form.FormPropertyType;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoImplementationType;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
import org.apache.syncope.core.persistence.api.dao.TaskDAO;
import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
@@ -62,6 +74,12 @@
@Autowired
private ExternalResourceDAO resourceDAO;
+ @Autowired
+ private RealmDAO realmDAO;
+
+ @Autowired
+ private ImplementationDAO implementationDAO;
+
@Test
public void findByName() {
Optional<SchedTask> task = taskDAO.findByName(TaskType.SCHEDULED, "SampleJob Task");
@@ -149,6 +167,62 @@
}
@Test
+ public void saveMacroTask() throws Exception {
+ MacroTask task = entityFactory.newEntity(MacroTask.class);
+ task.setRealm(realmDAO.getRoot());
+ task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow());
+ task.setName("Macro test");
+ task.setContinueOnError(true);
+
+ Implementation command = entityFactory.newEntity(Implementation.class);
+ command.setKey("command");
+ command.setType(IdRepoImplementationType.COMMAND);
+ command.setEngine(ImplementationEngine.JAVA);
+ command.setBody("clazz");
+ command = implementationDAO.save(command);
+ assertNotNull(command);
+
+ MacroTaskCommand macroTaskCommand = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand.setCommand(command);
+ macroTaskCommand.setMacroTask(task);
+ task.add(macroTaskCommand);
+
+ FormPropertyDef formPropertyDef = entityFactory.newEntity(FormPropertyDef.class);
+ formPropertyDef.setKey("one");
+ formPropertyDef.setName("One");
+ formPropertyDef.setType(FormPropertyType.Enum);
+ formPropertyDef.setMacroTask(task);
+ task.add(formPropertyDef);
+
+ Implementation macroActions = entityFactory.newEntity(Implementation.class);
+ macroActions.setKey("macroActions");
+ macroActions.setType(IdRepoImplementationType.MACRO_ACTIONS);
+ macroActions.setEngine(ImplementationEngine.JAVA);
+ macroActions.setBody("clazz");
+ macroActions = implementationDAO.save(macroActions);
+ assertNotNull(macroActions);
+ task.setMacroAction(macroActions);
+
+ try {
+ taskDAO.save(task);
+ fail();
+ } catch (InvalidEntityException e) {
+ assertNotNull(e);
+ }
+ formPropertyDef.setEnumValues(Map.of("key", "value"));
+
+ task = taskDAO.save(task);
+ assertNotNull(task);
+ assertEquals(1, task.getCommands().size());
+ assertEquals(command, task.getCommands().get(0).getCommand());
+ assertEquals(1, task.getFormPropertyDefs().size());
+ assertEquals(formPropertyDef, task.getFormPropertyDefs().get(0));
+
+ MacroTask actual = (MacroTask) taskDAO.findById(TaskType.MACRO, task.getKey()).orElseThrow();
+ assertEquals(task, actual);
+ }
+
+ @Test
public void delete() {
PropagationTask task = (PropagationTask) taskDAO.findById(
TaskType.PROPAGATION, "1e697572-b896-484c-ae7f-0c8f63fcbc6c").orElseThrow();
diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java
index 3640dfc..64942f8 100644
--- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java
+++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java
@@ -31,7 +31,6 @@
import java.util.List;
import java.util.Set;
import java.util.UUID;
-import org.apache.syncope.common.lib.command.CommandArgs;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ExecStatus;
import org.apache.syncope.common.lib.types.IdMImplementationType;
@@ -51,6 +50,7 @@
import org.apache.syncope.core.persistence.api.entity.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.PullTask;
@@ -358,38 +358,94 @@
@Rollback(false)
@Test
public void macroTaskCommandsOrdering() {
- Implementation impl1 = entityFactory.newEntity(Implementation.class);
- impl1.setKey("impl1");
- impl1.setEngine(ImplementationEngine.JAVA);
- impl1.setType(IdRepoImplementationType.COMMAND);
- impl1.setBody("TestCommand");
- impl1 = implementationDAO.save(impl1);
-
- Implementation impl2 = entityFactory.newEntity(Implementation.class);
- impl2.setKey("impl2");
- impl2.setEngine(ImplementationEngine.GROOVY);
- impl2.setType(IdRepoImplementationType.COMMAND);
- impl2.setBody("class GroovyCommand implements Command<CommandArgs> {}");
- impl2 = implementationDAO.save(impl2);
-
MacroTask task = entityFactory.newEntity(MacroTask.class);
task.setName("macro");
- task.setJobDelegate(implementationDAO.findById("MacroRunJobDelegate").orElseThrow());
+ task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow());
task.setRealm(realmDAO.getRoot());
- task.add(impl1, new CommandArgs());
- task.add(impl2, new CommandArgs());
+
+ Implementation command1 = entityFactory.newEntity(Implementation.class);
+ command1.setKey("impl1");
+ command1.setEngine(ImplementationEngine.JAVA);
+ command1.setType(IdRepoImplementationType.COMMAND);
+ command1.setBody("TestCommand");
+ command1 = implementationDAO.save(command1);
+
+ Implementation command2 = entityFactory.newEntity(Implementation.class);
+ command2.setKey("impl2");
+ command2.setEngine(ImplementationEngine.GROOVY);
+ command2.setType(IdRepoImplementationType.COMMAND);
+ command2.setBody("class GroovyCommand implements Command<CommandArgs> {}");
+ command2 = implementationDAO.save(command2);
+
+ MacroTaskCommand macroTaskCommand1 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand1.setCommand(command1);
+ macroTaskCommand1.setMacroTask(task);
+ task.add(macroTaskCommand1);
+
+ MacroTaskCommand macroTaskCommand2 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand2.setCommand(command2);
+ macroTaskCommand2.setMacroTask(task);
+ task.add(macroTaskCommand2);
task = taskDAO.save(task);
assertEquals(2, task.getCommands().size());
- assertEquals(2, task.getCommandArgs().size());
- assertEquals(impl1, task.getCommands().get(0));
- assertEquals(impl2, task.getCommands().get(1));
+ assertEquals(macroTaskCommand1, task.getCommands().get(0));
+ assertEquals(macroTaskCommand2, task.getCommands().get(1));
task = (MacroTask) taskDAO.findById(TaskType.MACRO, task.getKey()).orElseThrow();
assertEquals(2, task.getCommands().size());
- assertEquals(2, task.getCommandArgs().size());
- assertEquals(impl1, task.getCommands().get(0));
- assertEquals(impl2, task.getCommands().get(1));
+ assertEquals(macroTaskCommand1, task.getCommands().get(0));
+ assertEquals(macroTaskCommand2, task.getCommands().get(1));
+ }
+
+ @Test
+ public void saveMacroTaskSameCommandMultipleOccurrencies() {
+ MacroTask task = entityFactory.newEntity(MacroTask.class);
+ task.setRealm(realmDAO.getRoot());
+ task.setJobDelegate(implementationDAO.findById("MacroJobDelegate").orElseThrow());
+ task.setName("saveMacroTaskSameCommandMultipleOccurrencies");
+ task.setContinueOnError(true);
+
+ Implementation command1 = entityFactory.newEntity(Implementation.class);
+ command1.setKey("command1");
+ command1.setType(IdRepoImplementationType.COMMAND);
+ command1.setEngine(ImplementationEngine.JAVA);
+ command1.setBody("clazz1");
+ command1 = implementationDAO.save(command1);
+ assertNotNull(command1);
+
+ Implementation command2 = entityFactory.newEntity(Implementation.class);
+ command2.setKey("command2");
+ command2.setType(IdRepoImplementationType.COMMAND);
+ command2.setEngine(ImplementationEngine.JAVA);
+ command2.setBody("clazz2");
+ command2 = implementationDAO.save(command2);
+ assertNotNull(command2);
+
+ MacroTaskCommand macroTaskCommand1 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand1.setCommand(command1);
+ macroTaskCommand1.setMacroTask(task);
+ task.add(macroTaskCommand1);
+
+ MacroTaskCommand macroTaskCommand2 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand2.setCommand(command2);
+ macroTaskCommand2.setMacroTask(task);
+ task.add(macroTaskCommand2);
+
+ MacroTaskCommand macroTaskCommand3 = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand3.setCommand(command1);
+ macroTaskCommand3.setMacroTask(task);
+ task.add(macroTaskCommand3);
+
+ task = taskDAO.save(task);
+ assertNotNull(task);
+ assertEquals(3, task.getCommands().size());
+ assertEquals(command1, task.getCommands().get(0).getCommand());
+ assertEquals(command2, task.getCommands().get(1).getCommand());
+ assertEquals(command1, task.getCommands().get(2).getCommand());
+
+ MacroTask actual = (MacroTask) taskDAO.findById(TaskType.MACRO, task.getKey()).orElseThrow();
+ assertEquals(task, actual);
}
@Test
diff --git a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml
index 42680a0..d446254 100644
--- a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml
@@ -122,7 +122,7 @@
<PlainSchema id="fullname" type="String" mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"/>
<PlainSchema_AnyTypeClass left="fullname" right="minimal user"/>
- <Implementation id="EmailAddressValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="EmailAddressValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.EmailAddressValidator"/>
<PlainSchema id="userId" type="String" mandatoryCondition="true" multivalue="0" uniqueConstraint="1" readonly="0"/>
<PlainSchema_AnyTypeClass left="userId" right="minimal user"/>
@@ -163,7 +163,7 @@
mimeType="image/jpeg"/>
<PlainSchema_AnyTypeClass left="photo" right="other"/>
- <Implementation id="BinaryValidator" type="VALIDATOR" engine="JAVA"
+ <Implementation id="BinaryValidator" type="ATTR_VALUE_VALIDATOR" engine="JAVA"
body="org.apache.syncope.core.persistence.common.attrvalue.BinaryValidator"/>
<DerSchema id="csvuserid" expression="firstname + ',' + surname"/>
@@ -692,8 +692,8 @@
<VirSchema_AnyType left="virtualdata" right="USER"/>
<VirSchema_ExternalResource left="virtualdata" right="resource-db-virattr"/>
- <Implementation id="MacroRunJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
- body="org.apache.syncope.core.logic.job.MacroRunJobDelegate"/>
+ <Implementation id="MacroJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
+ body="org.apache.syncope.core.provisioning.java.job.MacroJobDelegate"/>
<Implementation id="PullJobDelegate" type="TASKJOB_DELEGATE" engine="JAVA"
body="org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate"/>
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java
index eacbb5c..a8a1dc5 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/TaskDataBinder.java
@@ -18,9 +18,11 @@
*/
package org.apache.syncope.core.provisioning.api.data;
+import org.apache.syncope.common.lib.form.SyncopeForm;
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.SchedTaskTO;
import org.apache.syncope.common.lib.to.TaskTO;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
import org.apache.syncope.core.persistence.api.entity.task.Task;
import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
@@ -37,4 +39,6 @@
ExecTO getExecTO(TaskExec<?> execution);
<T extends TaskTO> T getTaskTO(Task<?> task, TaskUtils taskUtil, boolean details);
+
+ SyncopeForm getMacroTaskForm(MacroTask task);
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
index 45b22af..b7c8c72 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
@@ -21,6 +21,7 @@
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
+import java.io.StringWriter;
import java.lang.reflect.Field;
import java.time.temporal.TemporalAccessor;
import java.util.Collection;
@@ -29,6 +30,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
@@ -70,7 +72,9 @@
private static JexlEngine JEXL_ENGINE;
- private static JexlEngine getEngine() {
+ private static JxltEngine JXTL_ENGINE;
+
+ private static JexlEngine getJexlEngine() {
synchronized (LOG) {
if (JEXL_ENGINE == null) {
JEXL_ENGINE = new JexlBuilder().
@@ -87,29 +91,35 @@
return JEXL_ENGINE;
}
- public static JxltEngine newJxltEngine() {
- return getEngine().createJxltEngine(false);
+ private static JxltEngine getJxltEngine() {
+ synchronized (LOG) {
+ if (JXTL_ENGINE == null) {
+ JXTL_ENGINE = getJexlEngine().createJxltEngine(false);
+ }
+ }
+
+ return JXTL_ENGINE;
}
public static boolean isExpressionValid(final String expression) {
boolean result;
try {
- getEngine().createExpression(expression);
+ getJexlEngine().createExpression(expression);
result = true;
} catch (JexlException e) {
- LOG.error("Invalid jexl expression: " + expression, e);
+ LOG.error("Invalid JEXL expression: " + expression, e);
result = false;
}
return result;
}
- public static Object evaluate(final String expression, final JexlContext jexlContext) {
+ public static Object evaluateExpr(final String expression, final JexlContext jexlContext) {
Object result = null;
if (StringUtils.isNotBlank(expression) && jexlContext != null) {
try {
- JexlExpression jexlExpression = getEngine().createExpression(expression);
+ JexlExpression jexlExpression = getJexlEngine().createExpression(expression);
result = jexlExpression.evaluate(jexlContext);
} catch (Exception e) {
LOG.error("Error while evaluating JEXL expression: " + expression, e);
@@ -118,7 +128,25 @@
LOG.debug("Expression not provided or invalid context");
}
- return result == null ? StringUtils.EMPTY : result;
+ return Optional.ofNullable(result).orElse(StringUtils.EMPTY);
+ }
+
+ public static String evaluateTemplate(final String template, final JexlContext jexlContext) {
+ String result = null;
+
+ if (StringUtils.isNotBlank(template) && jexlContext != null) {
+ try {
+ StringWriter writer = new StringWriter();
+ getJxltEngine().createTemplate(template).evaluate(jexlContext, writer);
+ result = writer.toString();
+ } catch (Exception e) {
+ LOG.error("Error while evaluating JEXL template: " + template, e);
+ }
+ } else {
+ LOG.debug("Template not provided or invalid context");
+ }
+
+ return Optional.ofNullable(result).orElse(template);
}
public static void addFieldsToContext(final Object object, final JexlContext jexlContext) {
@@ -256,7 +284,7 @@
addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
addDerAttrsToContext(any, derAttrHandler, jexlContext);
- return Boolean.parseBoolean(evaluate(mandatoryCondition, jexlContext).toString());
+ return Boolean.parseBoolean(evaluateExpr(mandatoryCondition, jexlContext).toString());
}
/**
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/Command.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
similarity index 94%
rename from core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/Command.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
index 88047c8..aea95cd 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/Command.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.logic.api;
+package org.apache.syncope.core.provisioning.api.macro;
import org.apache.syncope.common.lib.command.CommandArgs;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java
new file mode 100644
index 0000000..a631586
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java
@@ -0,0 +1,54 @@
+/*
+ * 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.provisioning.api.macro;
+
+import jakarta.validation.ValidationException;
+import java.util.Map;
+import org.apache.syncope.common.lib.command.CommandArgs;
+import org.apache.syncope.common.lib.form.SyncopeForm;
+
+/**
+ * Interface for actions to be performed during macro execution.
+ */
+public interface MacroActions {
+
+ default void validate(SyncopeForm macroTaskForm) throws ValidationException {
+ // does nothing by default
+ }
+
+ default Map<String, String> getDropdownValues(String formProperty) {
+ return Map.of();
+ }
+
+ default void beforeAll() {
+ // does nothing by default
+ }
+
+ default void beforeCommand(Command<CommandArgs> command, CommandArgs args) {
+ // does nothing by default
+ }
+
+ default void afterCommand(Command<CommandArgs> command, CommandArgs args, String output) {
+ // does nothing by default
+ }
+
+ default StringBuilder afterAll(StringBuilder output) {
+ return output;
+ }
+}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
index 82d8226..5f38aaf 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
@@ -20,7 +20,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
@@ -34,7 +33,6 @@
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.jexl3.JexlContext;
-import org.apache.commons.jexl3.JxltEngine;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.to.AnyTO;
@@ -54,12 +52,6 @@
private JexlContext context;
@Test
- public void newJxltEngine() {
- JxltEngine engine = JexlUtils.newJxltEngine();
- assertNotNull(engine);
- }
-
- @Test
public void isExpressionValid() {
String expression = "6 * 12 + 5 / 2.6";
assertTrue(JexlUtils.isExpressionValid(expression));
@@ -71,11 +63,11 @@
@Test
public void evaluate() {
String expression = null;
- assertEquals(StringUtils.EMPTY, JexlUtils.evaluate(expression, context));
+ assertEquals(StringUtils.EMPTY, JexlUtils.evaluateExpr(expression, context));
expression = "6 * 12 + 5 / 2.6";
double result = 73.92307692307692;
- assertEquals(result, JexlUtils.evaluate(expression, context));
+ assertEquals(result, JexlUtils.evaluateExpr(expression, context));
}
@Test
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java
index bfb6a2c..9cc4e36 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java
@@ -23,7 +23,6 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.IOException;
-import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -65,17 +64,9 @@
+ " $$ for(membership : user.memberships) {\n <li>${membership.groupName}</li>\n $$ }\n"
+ " </ul>\n $$ }\n </body> </html>";
- private static String evaluate(final String template, final Map<String, Object> jexlVars) {
- StringWriter writer = new StringWriter();
- JexlUtils.newJxltEngine().
- createTemplate(template).
- evaluate(new MapContext(jexlVars), writer);
- return writer.toString();
- }
-
@Test
public void confirmPasswordReset() throws IOException {
- String htmlBody = evaluate(CONFIRM_PASSWORD_RESET_TEMPLATE, new HashMap<>());
+ String htmlBody = JexlUtils.evaluateTemplate(CONFIRM_PASSWORD_RESET_TEMPLATE, new MapContext());
assertNotNull(htmlBody);
}
@@ -93,7 +84,7 @@
input.add(token);
ctx.put("input", input);
- String textBody = evaluate(REQUEST_PASSWORD_RESET_TEMPLATE, ctx);
+ String textBody = JexlUtils.evaluateTemplate(REQUEST_PASSWORD_RESET_TEMPLATE, new MapContext(ctx));
assertNotNull(textBody);
assertTrue(textBody.contains("a password reset was requested for " + username + "."));
@@ -129,7 +120,7 @@
ctx.put("events", List.of("event1"));
- String htmlBody = evaluate(OPTIN_TEMPLATE, ctx);
+ String htmlBody = JexlUtils.evaluateTemplate(OPTIN_TEMPLATE, new MapContext(ctx));
assertNotNull(htmlBody);
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java
index cd84f47..9badb8f 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java
@@ -47,10 +47,10 @@
JexlUtils.addFieldsToContext(user, jexlContext);
String connObjectLink = "'uid=' + username + ',ou=people,o=isp'";
- assertEquals("uid=rossini,ou=people,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));
+ assertEquals("uid=rossini,ou=people,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
connObjectLink = "'uid=' + username + realm.replaceAll('/', ',o=') + ',ou=people,o=isp'";
- assertEquals("uid=rossini,o=even,ou=people,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));
+ assertEquals("uid=rossini,o=even,ou=people,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
}
@Test
@@ -63,7 +63,7 @@
JexlUtils.addFieldsToContext(realm, jexlContext);
String connObjectLink = "syncope:fullPath2Dn(fullPath, 'ou') + ',o=isp'";
- assertEquals("ou=two,ou=even,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));
+ assertEquals("ou=two,ou=even,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
when(realm.getFullPath()).thenReturn("/even");
assertNotNull(realm);
@@ -71,7 +71,7 @@
jexlContext = new MapContext();
JexlUtils.addFieldsToContext(realm, jexlContext);
- assertEquals("ou=even,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));
+ assertEquals("ou=even,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
}
@Test
@@ -82,6 +82,6 @@
jexlContext.set("value", now);
String expression = "value.toInstant().toEpochMilli()";
- assertEquals(now.toInstant().toEpochMilli(), JexlUtils.evaluate(expression, jexlContext));
+ assertEquals(now.toInstant().toEpochMilli(), JexlUtils.evaluateExpr(expression, jexlContext));
}
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
index 3ff55e6..84cdb5e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
@@ -47,7 +47,7 @@
JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
JexlUtils.addFieldsToContext(any, jexlContext);
- result.put(schema, JexlUtils.evaluate(schema.getExpression(), jexlContext).toString());
+ result.put(schema, JexlUtils.evaluateExpr(schema.getExpression(), jexlContext).toString());
});
return result;
@@ -98,7 +98,7 @@
JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(membership), jexlContext);
JexlUtils.addFieldsToContext(any, jexlContext);
- result.put(schema, JexlUtils.evaluate(schema.getExpression(), jexlContext).toString());
+ result.put(schema, JexlUtils.evaluateExpr(schema.getExpression(), jexlContext).toString());
});
return result;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
index 2105868..43db073 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
@@ -254,7 +254,7 @@
JexlUtils.addFieldsToContext(any, jexlContext);
JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
JexlUtils.addDerAttrsToContext(any, derAttrHandler, jexlContext);
- evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext).toString();
+ evalConnObjectLink = JexlUtils.evaluateExpr(connObjectLink, jexlContext).toString();
}
return getName(evalConnObjectLink, connObjectKey);
@@ -282,7 +282,7 @@
if (StringUtils.isNotBlank(connObjectLink)) {
JexlContext jexlContext = new MapContext();
JexlUtils.addFieldsToContext(realm, jexlContext);
- evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext).toString();
+ evalConnObjectLink = JexlUtils.evaluateExpr(connObjectLink, jexlContext).toString();
}
return getName(evalConnObjectLink, connObjectKey);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
index fb67d03..4ff60ae 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
@@ -476,8 +476,8 @@
@ConditionalOnMissingBean
@Bean
- public ConnIdBundleManager connIdBundleManager(final ProvisioningProperties provisioningProperties) {
- return new DefaultConnIdBundleManager(provisioningProperties.getConnIdLocation());
+ public ConnIdBundleManager connIdBundleManager(final ProvisioningProperties props) {
+ return new DefaultConnIdBundleManager(props.getConnIdLocation());
}
@ConditionalOnMissingBean
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
index d59324e..b709a3c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
@@ -102,7 +102,7 @@
}
jexlContext.set("value", oValue);
- Object tValue = JexlUtils.evaluate(propagationJEXL, jexlContext);
+ Object tValue = JexlUtils.evaluateExpr(propagationJEXL, jexlContext);
value.setBinaryValue(null);
value.setBooleanValue(null);
@@ -183,7 +183,7 @@
JexlUtils.addAttrsToContext(((AnyTO) entityTO).getVirAttrs(), jexlContext);
}
- newValues.add(JexlUtils.evaluate(pullJEXL, jexlContext));
+ newValues.add(JexlUtils.evaluateExpr(pullJEXL, jexlContext));
});
return newValues;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
index 119eeea..d4eb849 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
@@ -19,12 +19,20 @@
package org.apache.syncope.core.provisioning.java.data;
import java.util.Comparator;
+import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.command.CommandArgs;
import org.apache.syncope.common.lib.command.CommandTO;
+import org.apache.syncope.common.lib.form.FormProperty;
+import org.apache.syncope.common.lib.form.FormPropertyValue;
+import org.apache.syncope.common.lib.form.SyncopeForm;
import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.FormPropertyDefTO;
import org.apache.syncope.common.lib.to.MacroTaskTO;
import org.apache.syncope.common.lib.to.NotificationTaskTO;
import org.apache.syncope.common.lib.to.PropagationTaskTO;
@@ -49,7 +57,9 @@
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
@@ -62,6 +72,8 @@
import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
import org.apache.syncope.core.provisioning.api.job.JobNamer;
+import org.apache.syncope.core.provisioning.api.macro.MacroActions;
+import org.apache.syncope.core.provisioning.java.job.MacroJobDelegate;
import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler;
import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
@@ -75,8 +87,6 @@
protected static final Logger LOG = LoggerFactory.getLogger(TaskDataBinder.class);
- protected static final String MACRO_RUN_JOB_DELEGATE = "org.apache.syncope.core.logic.job.MacroRunJobDelegate";
-
protected final RealmSearchDAO realmSearchDAO;
protected final ExternalResourceDAO resourceDAO;
@@ -93,6 +103,8 @@
protected final TaskUtilsFactory taskUtilsFactory;
+ protected final Map<String, MacroActions> perContextMacroActions = new ConcurrentHashMap<>();
+
public TaskDataBinderImpl(
final RealmSearchDAO realmSearchDAO,
final ExternalResourceDAO resourceDAO,
@@ -225,6 +237,7 @@
macroTask.setRealm(realmSearchDAO.findByFullPath(macroTaskTO.getRealm()).
orElseThrow(() -> new NotFoundException("Realm " + macroTaskTO.getRealm())));
+ macroTask.getCommands().clear();
macroTaskTO.getCommands().
forEach(command -> implementationDAO.findById(command.getKey()).ifPresentOrElse(
impl -> {
@@ -234,7 +247,12 @@
args = ImplementationManager.emptyArgs(impl);
}
- macroTask.add(impl, args);
+ MacroTaskCommand macroTaskCommand = entityFactory.newEntity(MacroTaskCommand.class);
+ macroTaskCommand.setCommand(impl);
+ macroTaskCommand.setArgs(args);
+
+ macroTaskCommand.setMacroTask(macroTask);
+ macroTask.add(macroTaskCommand);
} catch (Exception e) {
LOG.error("While adding Command {} to Macro", impl.getKey(), e);
@@ -248,6 +266,30 @@
macroTask.setContinueOnError(macroTaskTO.isContinueOnError());
macroTask.setSaveExecs(macroTaskTO.isSaveExecs());
+
+ macroTask.getFormPropertyDefs().clear();
+ macroTaskTO.getFormPropertyDefs().forEach(fpdTO -> {
+ FormPropertyDef fpd = entityFactory.newEntity(FormPropertyDef.class);
+ fpd.setKey(fpdTO.getKey());
+ fpd.setName(fpdTO.getName());
+ fpd.setType(fpdTO.getType());
+ fpd.setReadable(fpdTO.isReadable());
+ fpd.setWritable(fpdTO.isWritable());
+ fpd.setRequired(fpdTO.isRequired());
+ fpd.setDatePattern(fpdTO.getDatePattern());
+ fpd.setEnumValues(fpdTO.getEnumValues());
+
+ fpd.setMacroTask(macroTask);
+ macroTask.add(fpd);
+ });
+
+ if (macroTaskTO.getMacroActions() == null) {
+ macroTask.setMacroAction(null);
+ } else {
+ implementationDAO.findById(macroTaskTO.getMacroActions()).ifPresentOrElse(
+ macroTask::setMacroAction,
+ () -> LOG.debug("Invalid Implementation {}, ignoring...", macroTaskTO.getMacroActions()));
+ }
}
@Override
@@ -272,16 +314,16 @@
Implementation jobDelegate = (macroTaskTO.getJobDelegate() == null
? implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE).stream().
- filter(impl -> MACRO_RUN_JOB_DELEGATE.equals(impl.getBody())).
+ filter(impl -> MacroJobDelegate.class.getName().equals(impl.getBody())).
findFirst()
: implementationDAO.findById(macroTaskTO.getJobDelegate())).
orElse(null);
if (jobDelegate == null) {
jobDelegate = entityFactory.newEntity(Implementation.class);
- jobDelegate.setKey(StringUtils.substringAfterLast(MACRO_RUN_JOB_DELEGATE, "."));
+ jobDelegate.setKey(MacroJobDelegate.class.getSimpleName());
jobDelegate.setEngine(ImplementationEngine.JAVA);
jobDelegate.setType(IdRepoImplementationType.TASKJOB_DELEGATE);
- jobDelegate.setBody(MACRO_RUN_JOB_DELEGATE);
+ jobDelegate.setBody(MacroJobDelegate.class.getName());
jobDelegate = implementationDAO.save(jobDelegate);
}
macroTask.setJobDelegate(jobDelegate);
@@ -320,21 +362,10 @@
task.setCronExpression(taskTO.getCronExpression());
task.setActive(taskTO.isActive());
- switch (task) {
- case MacroTask macroTask -> {
- MacroTaskTO macroTaskTO = (MacroTaskTO) taskTO;
-
- macroTask.getCommands().clear();
- macroTask.getCommandArgs().clear();
-
- fill(macroTask, macroTaskTO);
- }
-
- case ProvisioningTask<?> provisioningTask ->
- fill(provisioningTask, (ProvisioningTaskTO) taskTO);
-
- default -> {
- }
+ if (task instanceof MacroTask) {
+ fill((MacroTask) task, (MacroTaskTO) taskTO);
+ } else if (task instanceof ProvisioningTask) {
+ fill((ProvisioningTask) task, (ProvisioningTaskTO) taskTO);
}
}
@@ -450,17 +481,33 @@
MacroTask macroTask = (MacroTask) task;
MacroTaskTO macroTaskTO = (MacroTaskTO) taskTO;
+ fill(macroTaskTO, macroTask);
+
macroTaskTO.setJobDelegate(macroTask.getJobDelegate().getKey());
macroTaskTO.setRealm(macroTask.getRealm().getFullPath());
- for (int i = 0; i < macroTask.getCommands().size(); i++) {
- macroTaskTO.getCommands().add(
- new CommandTO.Builder(macroTask.getCommands().get(i).getKey()).
- args(macroTask.getCommandArgs().get(i)).build());
- }
+
+ macroTask.getCommands().forEach(mct -> macroTaskTO.getCommands().add(
+ new CommandTO.Builder(mct.getCommand().getKey()).args(mct.getArgs()).build()));
+
macroTaskTO.setContinueOnError(macroTask.isContinueOnError());
macroTaskTO.setSaveExecs(macroTask.isSaveExecs());
- fill(macroTaskTO, macroTask);
+ macroTask.getFormPropertyDefs().forEach(fpd -> {
+ FormPropertyDefTO fpdTO = new FormPropertyDefTO();
+ fpdTO.setKey(fpd.getKey());
+ fpdTO.setName(fpd.getName());
+ fpdTO.setType(fpd.getType());
+ fpdTO.setReadable(fpd.isReadable());
+ fpdTO.setWritable(fpd.isWritable());
+ fpdTO.setRequired(fpd.isRequired());
+ fpdTO.setDatePattern(fpd.getDatePattern());
+ fpdTO.getEnumValues().putAll(fpd.getEnumValues());
+
+ macroTaskTO.getFormPropertyDefs().add(fpdTO);
+ });
+
+ Optional.ofNullable(macroTask.getMacroActions()).
+ ifPresent(fv -> macroTaskTO.setMacroActions(fv.getKey()));
}
case PULL -> {
@@ -476,9 +523,8 @@
? UnmatchingRule.PROVISION : pullTask.getUnmatchingRule());
pullTaskTO.setPullMode(pullTask.getPullMode());
- if (pullTask.getReconFilterBuilder() != null) {
- pullTaskTO.setReconFilterBuilder(pullTask.getReconFilterBuilder().getKey());
- }
+ Optional.ofNullable(pullTask.getReconFilterBuilder()).
+ ifPresent(rfb -> pullTaskTO.setReconFilterBuilder(rfb.getKey()));
pullTask.getTemplates().
forEach(template -> pullTaskTO.getTemplates().
@@ -527,4 +573,59 @@
return taskTO;
}
+
+ @Override
+ public SyncopeForm getMacroTaskForm(final MacroTask task) {
+ if (task.getFormPropertyDefs().isEmpty()) {
+ throw new NotFoundException("No form properties defined for MacroTask " + task.getKey());
+ }
+
+ Optional<MacroActions> actions;
+ if (task.getMacroActions() == null) {
+ actions = Optional.empty();
+ } else {
+ try {
+ actions = Optional.of(ImplementationManager.build(
+ task.getMacroActions(),
+ () -> perContextMacroActions.get(task.getMacroActions().getKey()),
+ instance -> perContextMacroActions.put(task.getMacroActions().getKey(), instance)));
+ } catch (Exception e) {
+ LOG.error("Could not build {}", task.getMacroActions().getKey(), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidImplementation);
+ sce.getElements().add("Could not build " + task.getMacroActions().getKey());
+ throw sce;
+ }
+ }
+
+ SyncopeForm form = new SyncopeForm();
+
+ form.getProperties().addAll(task.getFormPropertyDefs().stream().map(fpd -> {
+ FormProperty prop = new FormProperty();
+ prop.setId(fpd.getKey());
+ prop.setName(fpd.getName());
+ prop.setReadable(fpd.isReadable());
+ prop.setRequired(fpd.isRequired());
+ prop.setWritable(fpd.isWritable());
+ prop.setType(fpd.getType());
+ switch (prop.getType()) {
+ case Date ->
+ prop.setDatePattern(fpd.getDatePattern());
+
+ case Enum ->
+ fpd.getEnumValues().
+ forEach((key, value) -> prop.getEnumValues().add(new FormPropertyValue(key, value)));
+
+ case Dropdown ->
+ actions.ifPresent(a -> a.getDropdownValues(fpd.getKey()).
+ forEach((key, value) -> prop.getDropdownValues().add(new FormPropertyValue(key, value))));
+
+ default -> {
+ }
+ }
+ return prop;
+ }).collect(Collectors.toList()));
+
+ return form;
+ }
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
new file mode 100644
index 0000000..e563026
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
@@ -0,0 +1,303 @@
+/*
+ * 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.provisioning.java.job;
+
+import jakarta.annotation.Resource;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ValidationException;
+import jakarta.validation.Validator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.MapContext;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.command.CommandArgs;
+import org.apache.syncope.common.lib.form.SyncopeForm;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
+import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.persistence.api.utils.FormatUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.job.JobExecutionContext;
+import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
+import org.apache.syncope.core.provisioning.api.macro.Command;
+import org.apache.syncope.core.provisioning.api.macro.MacroActions;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.task.VirtualThreadPoolTaskExecutor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.concurrent.DelegatingSecurityContextCallable;
+import org.springframework.util.ReflectionUtils;
+
+public class MacroJobDelegate extends AbstractSchedTaskJobDelegate<MacroTask> {
+
+ public static final String MACRO_TASK_FORM_JOBDETAIL_KEY = "macroTaskForm";
+
+ @Autowired
+ protected ImplementationDAO implementationDAO;
+
+ @Autowired
+ protected Validator validator;
+
+ @Resource(name = "batchExecutor")
+ protected VirtualThreadPoolTaskExecutor executor;
+
+ protected final Map<String, MacroActions> perContextActions = new ConcurrentHashMap<>();
+
+ protected final Map<String, Command<?>> perContextCommands = new ConcurrentHashMap<>();
+
+ protected boolean validate(final FormPropertyDef fpd, final String value, final Optional<MacroActions> actions) {
+ if (!fpd.isWritable()) {
+ return false;
+ }
+
+ return switch (fpd.getType()) {
+ case Enum ->
+ fpd.getEnumValues().containsKey(value);
+ case Dropdown ->
+ actions.map(a -> a.getDropdownValues(fpd.getKey()).containsKey(value)).orElse(false);
+ default ->
+ value != null;
+ };
+ }
+
+ protected Optional<JexlContext> check(
+ final SyncopeForm macroTaskForm,
+ final Optional<MacroActions> actions,
+ final StringBuilder output) throws JobExecutionException {
+
+ if (macroTaskForm == null) {
+ return Optional.empty();
+ }
+
+ // check if there is any required property with no value provided
+ Set<String> missingFormProperties = task.getFormPropertyDefs().stream().
+ filter(FormPropertyDef::isRequired).
+ map(fpd -> Pair.of(
+ fpd.getKey(),
+ macroTaskForm.getProperty(fpd.getKey()).map(p -> p.getValue() != null))).
+ filter(pair -> pair.getRight().isEmpty()).
+ map(Pair::getLeft).
+ collect(Collectors.toSet());
+ if (!missingFormProperties.isEmpty()) {
+ throw new JobExecutionException("Required form properties missing: " + missingFormProperties);
+ }
+
+ // if validator is defined, validate the provided form
+ try {
+ actions.ifPresent(a -> a.validate(macroTaskForm));
+ } catch (ValidationException e) {
+ throw new JobExecutionException("Invalid form submitted for task " + task.getKey(), e);
+ }
+
+ // build the JEXL context where variables are mapped to property values, built according to the defined type
+ Map<String, Object> vars = macroTaskForm.getProperties().stream().
+ map(p -> task.getFormPropertyDefs().stream().
+ filter(fpd -> fpd.getKey().equals(p.getId()) && validate(fpd, p.getValue(), actions)).findFirst().
+ map(fpd -> Pair.of(fpd, p.getValue()))).
+ filter(Optional::isPresent).map(Optional::get).
+ map(pair -> {
+ Object value;
+ switch (pair.getLeft().getType()) {
+ case Boolean:
+ value = BooleanUtils.toBoolean(pair.getRight());
+ break;
+
+ case Date:
+ value = StringUtils.isBlank(pair.getLeft().getDatePattern())
+ ? FormatUtils.parseDate(pair.getRight())
+ : FormatUtils.parseDate(pair.getRight(), pair.getLeft().getDatePattern());
+ break;
+
+ case Long:
+ value = NumberUtils.toLong(pair.getRight());
+ break;
+
+ case Enum:
+ case Dropdown:
+ case String:
+ case Password:
+ default:
+ value = pair.getRight();
+ }
+
+ return Pair.of(pair.getLeft().getKey(), value);
+ }).collect(Collectors.toMap(Pair::getLeft, Pair::getRight));
+
+ output.append("Form parameter values: ").append(vars).append("\n\n");
+
+ return vars.isEmpty() ? Optional.empty() : Optional.of(new MapContext(vars));
+ }
+
+ protected String run(
+ final List<Pair<Command<CommandArgs>, CommandArgs>> commands,
+ final Optional<MacroActions> actions,
+ final StringBuilder output,
+ final boolean dryRun)
+ throws JobExecutionException {
+
+ Future<AtomicReference<Pair<String, Exception>>> future = executor.submit(
+ new DelegatingSecurityContextCallable<>(() -> {
+
+ AtomicReference<Pair<String, Exception>> error = new AtomicReference<>();
+
+ for (int i = 0; i < commands.size() && error.get() == null; i++) {
+ Pair<Command<CommandArgs>, CommandArgs> command = commands.get(i);
+
+ try {
+ String args = POJOHelper.serialize(command.getRight());
+ output.append("Command[").append(command.getLeft().getClass().getName()).append("]: ").
+ append(args).append("\n");
+
+ if (!dryRun) {
+ actions.ifPresent(a -> a.beforeCommand(command.getLeft(), command.getRight()));
+
+ String cmdOut = command.getLeft().run(command.getRight());
+
+ actions.ifPresent(a -> a.afterCommand(command.getLeft(), command.getRight(), cmdOut));
+
+ output.append(cmdOut);
+ }
+ } catch (Exception e) {
+ if (task.isContinueOnError()) {
+ output.append("Continuing on error: <").append(e.getMessage()).append('>');
+
+ LOG.error("While running {} with args {}, continuing on error",
+ command.getLeft().getClass().getName(), command.getRight(), e);
+ } else {
+ error.set(Pair.of(command.getLeft().getClass().getName(), e));
+ }
+ }
+ output.append("\n\n");
+ }
+
+ return error;
+ }));
+
+ try {
+ AtomicReference<Pair<String, Exception>> error = future.get();
+ if (error.get() != null) {
+ throw new JobExecutionException("While running " + error.get().getLeft(), error.get().getRight());
+ }
+ } catch (ExecutionException | InterruptedException e) {
+ throw new JobExecutionException("While waiting for macro commands completion", e);
+ }
+
+ output.append("COMPLETED");
+
+ return actions.filter(a -> !dryRun).map(a -> a.afterAll(output)).orElse(output).toString();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context)
+ throws JobExecutionException {
+
+ Optional<MacroActions> actions;
+ if (task.getMacroActions() == null) {
+ actions = Optional.empty();
+ } else {
+ try {
+ actions = Optional.of(ImplementationManager.build(
+ task.getMacroActions(),
+ () -> perContextActions.get(task.getMacroActions().getKey()),
+ instance -> perContextActions.put(task.getMacroActions().getKey(), instance)));
+ } catch (Exception e) {
+ throw new JobExecutionException("Could not build " + task.getMacroActions().getKey(), e);
+ }
+ }
+
+ StringBuilder output = new StringBuilder();
+
+ SyncopeForm macroTaskForm = (SyncopeForm) context.getData().get(MACRO_TASK_FORM_JOBDETAIL_KEY);
+ Optional<JexlContext> jexlContext = check(macroTaskForm, actions, output);
+
+ if (!dryRun) {
+ actions.ifPresent(MacroActions::beforeAll);
+ }
+
+ List<Pair<Command<CommandArgs>, CommandArgs>> commands = new ArrayList<>();
+ for (MacroTaskCommand command : task.getCommands()) {
+ Command<CommandArgs> runnable;
+ try {
+ runnable = (Command<CommandArgs>) ImplementationManager.build(
+ command.getCommand(),
+ () -> perContextCommands.get(command.getCommand().getKey()),
+ instance -> perContextCommands.put(command.getCommand().getKey(), instance));
+ } catch (Exception e) {
+ throw new JobExecutionException("Could not build " + command.getCommand().getKey(), e);
+ }
+
+ CommandArgs args;
+ if (command.getArgs() == null) {
+ try {
+ args = ImplementationManager.emptyArgs(command.getCommand());
+ } catch (Exception e) {
+ throw new JobExecutionException("While getting empty args from " + command.getKey(), e);
+ }
+ } else {
+ args = command.getArgs();
+
+ jexlContext.ifPresent(ctx -> ReflectionUtils.doWithFields(
+ args.getClass(),
+ field -> {
+ if (String.class.equals(field.getType())) {
+ field.setAccessible(true);
+ Object value = field.get(args);
+ if (value instanceof String) {
+ field.set(args, JexlUtils.evaluateTemplate((String) value, ctx));
+ }
+ }
+ },
+ field -> !field.isSynthetic()));
+
+ Set<ConstraintViolation<Object>> violations = validator.validate(args);
+ if (!violations.isEmpty()) {
+ LOG.error("While validating {}: {}", args, violations);
+
+ throw new JobExecutionException(
+ "While running " + command.getKey(),
+ new IllegalArgumentException(args.getClass().getName()));
+ }
+ }
+
+ commands.add(Pair.of(runnable, args));
+ }
+
+ return run(commands, actions, output, dryRun);
+ }
+
+ @Override
+ protected boolean hasToBeRegistered(final TaskExec<?> execution) {
+ return task.isSaveExecs();
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
index 575b812..d6b88d1 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
@@ -18,7 +18,6 @@
*/
package org.apache.syncope.core.provisioning.java.notification;
-import java.io.StringWriter;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -27,6 +26,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
@@ -231,6 +231,7 @@
jexlVars.put("recipients", recipientTOs);
jexlVars.put("syncopeConf", confParamOps.list(SyncopeConstants.MASTER_DOMAIN));
jexlVars.put("events", notification.getEvents());
+ JexlContext ctx = new MapContext(jexlVars);
NotificationTask task = entityFactory.newEntity(NotificationTask.class);
task.setNotification(notification);
@@ -244,23 +245,15 @@
task.setSubject(notification.getSubject());
if (StringUtils.isNotBlank(notification.getTemplate().getTextTemplate())) {
- task.setTextBody(evaluate(notification.getTemplate().getTextTemplate(), jexlVars));
+ task.setTextBody(JexlUtils.evaluateTemplate(notification.getTemplate().getTextTemplate(), ctx));
}
if (StringUtils.isNotBlank(notification.getTemplate().getHTMLTemplate())) {
- task.setHtmlBody(evaluate(notification.getTemplate().getHTMLTemplate(), jexlVars));
+ task.setHtmlBody(JexlUtils.evaluateTemplate(notification.getTemplate().getHTMLTemplate(), ctx));
}
return task;
}
- protected static String evaluate(final String template, final Map<String, Object> jexlVars) {
- StringWriter writer = new StringWriter();
- JexlUtils.newJxltEngine().
- createTemplate(template).
- evaluate(new MapContext(jexlVars), writer);
- return writer.toString();
- }
-
@Override
public boolean notificationsAvailable(
final String domain,
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
index d071bf0..fda71b0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
@@ -92,7 +92,7 @@
JexlUtils.addPlainAttrsToContext(group.getPlainAttrs(), jexlContext);
JexlUtils.addDerAttrsToContext(group, derAttrHandler, jexlContext);
- return JexlUtils.evaluate(connObjectLinkTemplate, jexlContext).toString();
+ return JexlUtils.evaluateExpr(connObjectLinkTemplate, jexlContext).toString();
}
protected void buildManagedGroupConnObjectLinks(
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
index 6834520..b9260f4 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
@@ -92,7 +92,7 @@
if (template.getValues() != null && !template.getValues().isEmpty()) {
template.getValues().forEach(value -> {
- String evaluated = JexlUtils.evaluate(value, jexlContext).toString();
+ String evaluated = JexlUtils.evaluateExpr(value, jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
result.getValues().add(evaluated);
}
@@ -110,7 +110,7 @@
JexlUtils.addAttrsToContext(realmMember.getVirAttrs(), jexlContext);
if (template.getRealm() != null) {
- String evaluated = JexlUtils.evaluate(template.getRealm(), jexlContext).toString();
+ String evaluated = JexlUtils.evaluateExpr(template.getRealm(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
realmMember.setRealm(evaluated);
}
@@ -196,7 +196,7 @@
case UserTO userTO -> {
if (StringUtils.isNotBlank(userTO.getUsername())) {
- String evaluated = JexlUtils.evaluate(userTO.getUsername(), jexlContext).toString();
+ String evaluated = JexlUtils.evaluateExpr(userTO.getUsername(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
switch (realmMember) {
case UserTO urm ->
@@ -210,7 +210,7 @@
}
if (StringUtils.isNotBlank(userTO.getPassword())) {
- String evaluated = JexlUtils.evaluate(userTO.getPassword(), jexlContext).toString();
+ String evaluated = JexlUtils.evaluateExpr(userTO.getPassword(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
switch (realmMember) {
case UserTO urm ->
@@ -262,7 +262,7 @@
case GroupTO groupTO -> {
if (StringUtils.isNotBlank(groupTO.getName())) {
- String evaluated = JexlUtils.evaluate(groupTO.getName(), jexlContext).toString();
+ String evaluated = JexlUtils.evaluateExpr(groupTO.getName(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
switch (realmMember) {
case GroupTO grm ->
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
index 16dc35e..01be169 100644
--- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityContext.java
@@ -34,10 +34,12 @@
import org.apache.syncope.core.spring.security.jws.AccessTokenJWSVerifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
@EnableConfigurationProperties(SecurityProperties.class)
@@ -46,17 +48,13 @@
private static final Logger LOG = LoggerFactory.getLogger(SecurityContext.class);
+ @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
- public CipherAlgorithm adminPasswordAlgorithm(final SecurityProperties props) {
- return props.getAdminPasswordAlgorithm();
+ public static GrantedAuthorityDefaults grantedAuthorityDefaults() {
+ return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
- @Bean
- public JWSAlgorithm jwsAlgorithm(final SecurityProperties props) {
- return JWSAlgorithm.parse(props.getJwsAlgorithm().toUpperCase());
- }
-
- private static String jwsKey(final JWSAlgorithm jwsAlgorithm, final SecurityProperties props) {
+ protected static String jwsKey(final JWSAlgorithm jwsAlgorithm, final SecurityProperties props) {
String jwsKey = props.getJwsKey();
if (jwsKey == null) {
throw new IllegalArgumentException("No JWS key provided");
@@ -79,6 +77,16 @@
return jwsKey;
}
+ @Bean
+ public CipherAlgorithm adminPasswordAlgorithm(final SecurityProperties props) {
+ return props.getAdminPasswordAlgorithm();
+ }
+
+ @Bean
+ public JWSAlgorithm jwsAlgorithm(final SecurityProperties props) {
+ return JWSAlgorithm.parse(props.getJwsAlgorithm().toUpperCase());
+ }
+
@ConditionalOnMissingBean
@Bean
public DefaultCredentialChecker credentialChecker(
@@ -135,11 +143,6 @@
}
@Bean
- public GrantedAuthorityDefaults grantedAuthorityDefaults() {
- return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
- }
-
- @Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
diff --git a/ext/flowable/client-common-ui/pom.xml b/ext/flowable/client-common-ui/pom.xml
deleted file mode 100644
index 7acb692..0000000
--- a/ext/flowable/client-common-ui/pom.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.apache.syncope.ext</groupId>
- <artifactId>syncope-ext-flowable</artifactId>
- <version>4.0.0-SNAPSHOT</version>
- </parent>
-
- <name>Apache Syncope Ext: Flowable Client Common UI</name>
- <description>Apache Syncope Ext: Flowable Client Common UI</description>
- <groupId>org.apache.syncope.ext.flowable</groupId>
- <artifactId>syncope-ext-flowable-client-common-ui</artifactId>
- <packaging>jar</packaging>
-
- <properties>
- <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.apache.syncope.client.idrepo</groupId>
- <artifactId>syncope-client-idrepo-common-ui</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.syncope.ext.flowable</groupId>
- <artifactId>syncope-ext-flowable-common-lib</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.syncope.ext.flowable</groupId>
- <artifactId>syncope-ext-flowable-rest-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-</project>
diff --git a/ext/flowable/client-console/pom.xml b/ext/flowable/client-console/pom.xml
index 17ec624..2a0fe86 100644
--- a/ext/flowable/client-console/pom.xml
+++ b/ext/flowable/client-console/pom.xml
@@ -47,11 +47,6 @@
<dependency>
<groupId>org.apache.syncope.ext.flowable</groupId>
- <artifactId>syncope-ext-flowable-client-common-ui</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.syncope.ext.flowable</groupId>
<artifactId>syncope-ext-flowable-rest-api</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
index 1db22cf..ff5264f 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormDirectoryPanel.java
@@ -214,8 +214,8 @@
public void onClick(final AjaxRequestTarget target, final UserRequestForm ignore) {
manageFormModal.setFormModel(new CompoundPropertyModel<>(model.getObject()));
- target.add(manageFormModal.setContent(new UserRequestFormModal(manageFormModal, pageRef, model.
- getObject()) {
+ target.add(manageFormModal.setContent(
+ new UserRequestFormModal(manageFormModal, pageRef, model.getObject()) {
private static final long serialVersionUID = 5546519445061007248L;
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormModal.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormModal.java
index 622b2c2..177fdf1 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormModal.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormModal.java
@@ -26,7 +26,6 @@
import org.apache.syncope.client.ui.commons.panels.SubmitableModalPanel;
import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
import org.apache.syncope.common.lib.to.UserRequestForm;
-import org.apache.syncope.ext.client.common.ui.panels.UserRequestFormPanel;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.panel.Panel;
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormPanel.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormPanel.java
new file mode 100644
index 0000000..78f64fa
--- /dev/null
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/UserRequestFormPanel.java
@@ -0,0 +1,53 @@
+/*
+ * 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.client.console.panels;
+
+import org.apache.syncope.client.ui.commons.panels.SyncopeFormPanel;
+import org.apache.syncope.common.lib.to.UserRequestForm;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+
+public abstract class UserRequestFormPanel extends SyncopeFormPanel<UserRequestForm> {
+
+ private static final long serialVersionUID = 6064351260702815499L;
+
+ public UserRequestFormPanel(final String id, final UserRequestForm form) {
+ super(id, form);
+
+ AjaxLink<String> userDetails = new AjaxLink<>("userDetails") {
+
+ private static final long serialVersionUID = -4804368561204623354L;
+
+ @Override
+ public void onClick(final AjaxRequestTarget target) {
+ viewDetails(target);
+ }
+ };
+ MetaDataRoleAuthorizationStrategy.authorize(userDetails, ENABLE, IdRepoEntitlement.USER_READ);
+
+ boolean enabled = form.getUserTO() != null;
+ userDetails.setVisible(enabled).setEnabled(enabled);
+
+ add(userDetails);
+ }
+
+ protected abstract void viewDetails(AjaxRequestTarget target);
+}
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel.html
similarity index 89%
rename from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
rename to ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel.html
index aef81e5..c5a8c98 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.html
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel.html
@@ -17,15 +17,11 @@
under the License.
-->
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
- <wicket:panel>
- <div wicket:id="propView">
- <span wicket:id="value">[value]</span>
- </div>
-
+ <wicket:extend>
<div style="margin: 20px 0">
<a href="#" alt="user details" class="btn btn-success btn-circle btn-lg" wicket:id="userDetails" wicket:message="title:userDetails">
<i class="fas fa-eye"></i>
</a>
</div>
- </wicket:panel>
+ </wicket:extend>
</html>
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel.properties
similarity index 97%
rename from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
rename to ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel.properties
index 450ff50..5d29cc4 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel.properties
@@ -15,4 +15,3 @@
# specific language governing permissions and limitations
# under the License.
userDetails=User details
-userForm=Edit User
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_it.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_it.properties
similarity index 96%
rename from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_it.properties
rename to ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_it.properties
index 92c475d..b96f57c 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_it.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_it.properties
@@ -15,4 +15,3 @@
# specific language governing permissions and limitations
# under the License.
userDetails=Dettagli utente
-userForm=Modifica utente
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_ja.properties
similarity index 93%
rename from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
rename to ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_ja.properties
index 5a9cc2d..93119db 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ja.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_ja.properties
@@ -15,4 +15,3 @@
# specific language governing permissions and limitations
# under the License.
userDetails=\u30e6\u30fc\u30b6\u30fc\u8a73\u7d30
-userForm=\u30e6\u30fc\u30b6\u30fc\u3092\u7de8\u96c6
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_pt_BR.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_pt_BR.properties
similarity index 95%
rename from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_pt_BR.properties
rename to ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_pt_BR.properties
index 00a8971..b2b000a 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_pt_BR.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_pt_BR.properties
@@ -15,4 +15,3 @@
# specific language governing permissions and limitations
# under the License.
userDetails=Detalhes do Usu\u00e1rio
-userForm=Detalhes do Usu\u00e1rio
diff --git a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ru.properties b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_ru.properties
similarity index 88%
rename from ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ru.properties
rename to ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_ru.properties
index 02c159a..c82ab29 100644
--- a/ext/flowable/client-common-ui/src/main/resources/org/apache/syncope/ext/client/common/ui/panels/UserRequestFormPanel_ru.properties
+++ b/ext/flowable/client-console/src/main/resources/org/apache/syncope/client/console/panels/UserRequestFormPanel_ru.properties
@@ -17,4 +17,3 @@
#
# userDetails=\u00d0\u0098\u00d0\u00bd\u00d1\u0084\u00d0\u00be\u00d1\u0080\u00d0\u00bc\u00d0\u00b0\u00d1\u0086\u00d0\u00b8\u00d1\u008f \u00d0\u00be \u00d0\u00bf\u00d0\u00be\u00d0\u00bb\u00d1\u008c\u00d0\u00b7\u00d0\u00be\u00d0\u00b2\u00d0\u00b0\u00d1\u0082\u00d0\u00b5\u00d0\u00bb\u00d0\u00b5
userDetails=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435
-userForm=\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435
diff --git a/ext/flowable/client-enduser/pom.xml b/ext/flowable/client-enduser/pom.xml
index 9f57116..a2ed5b1 100644
--- a/ext/flowable/client-enduser/pom.xml
+++ b/ext/flowable/client-enduser/pom.xml
@@ -38,11 +38,6 @@
</properties>
<dependencies>
- <dependency>
- <groupId>org.apache.syncope.ext.flowable</groupId>
- <artifactId>syncope-ext-flowable-client-common-ui</artifactId>
- <version>${project.version}</version>
- </dependency>
<dependency>
<groupId>org.apache.syncope.ext.flowable</groupId>
<artifactId>syncope-ext-flowable-rest-api</artifactId>
diff --git a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java
index c55907b..ae03ab3 100644
--- a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java
+++ b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/panels/UserRequestDetails.java
@@ -21,13 +21,13 @@
import org.apache.syncope.client.enduser.SyncopeEnduserSession;
import org.apache.syncope.client.enduser.rest.UserRequestRestClient;
import org.apache.syncope.client.ui.commons.panels.NotificationPanel;
+import org.apache.syncope.client.ui.commons.panels.SyncopeFormPanel;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.UserRequest;
import org.apache.syncope.common.lib.to.UserRequestForm;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.ExecStatus;
-import org.apache.syncope.ext.client.common.ui.panels.UserRequestFormPanel;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
@@ -72,15 +72,7 @@
} else {
Form<Void> form = new Form<>("userRequestWrapForm");
- form.add(new UserRequestFormPanel("userRequestFormPanel", formTO, false) {
-
- private static final long serialVersionUID = 3617895525072546591L;
-
- @Override
- protected void viewDetails(final AjaxRequestTarget target) {
- // do nothing
- }
- });
+ form.add(new SyncopeFormPanel<>("userRequestFormPanel", formTO));
form.add(new AjaxButton("submit") {
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java
index 229859c..cb0212b 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java
@@ -18,26 +18,16 @@
*/
package org.apache.syncope.common.lib.to;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.util.Date;
import java.util.Optional;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.syncope.common.lib.BaseBean;
-@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "_class")
-@JsonPropertyOrder(value = { "_class", "bpmnProcess" })
public class UserRequest implements BaseBean {
private static final long serialVersionUID = -8430826310789942133L;
- @JacksonXmlProperty(localName = "_class", isAttribute = true)
- @JsonProperty("_class")
- private final String clazz = "org.apache.syncope.common.lib.to.UserRequest";
-
private String bpmnProcess;
private Date startTime;
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
index 7a6d769..5f51fe5 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
@@ -18,31 +18,17 @@
*/
package org.apache.syncope.common.lib.to;
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
-import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
-import java.util.ArrayList;
import java.util.Date;
-import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.syncope.common.lib.BaseBean;
+import org.apache.syncope.common.lib.form.SyncopeForm;
import org.apache.syncope.common.lib.request.UserUR;
-@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "_class")
-@JsonPropertyOrder(value = { "_class", "bpmnProcess" })
-public class UserRequestForm implements BaseBean {
+public class UserRequestForm extends SyncopeForm {
private static final long serialVersionUID = -7044543391316529128L;
- @JacksonXmlProperty(localName = "_class", isAttribute = true)
- @JsonProperty("_class")
- private final String clazz = "org.apache.syncope.common.lib.to.UserRequestForm";
-
private String bpmnProcess;
private String username;
@@ -63,8 +49,6 @@
private UserUR userUR;
- private final List<UserRequestFormProperty> properties = new ArrayList<>();
-
public String getBpmnProcess() {
return bpmnProcess;
}
@@ -145,20 +129,10 @@
this.userUR = userUR;
}
- @JsonIgnore
- public Optional<UserRequestFormProperty> getProperty(final String id) {
- return properties.stream().filter(property -> id.equals(property.getId())).findFirst();
- }
-
- @JacksonXmlElementWrapper(localName = "properties")
- @JacksonXmlProperty(localName = "property")
- public List<UserRequestFormProperty> getProperties() {
- return properties;
- }
-
@Override
public int hashCode() {
return new HashCodeBuilder().
+ appendSuper(super.hashCode()).
append(bpmnProcess).
append(username).
append(executionId).
@@ -169,7 +143,6 @@
append(assignee).
append(userTO).
append(userUR).
- append(properties).
build();
}
@@ -186,6 +159,7 @@
}
UserRequestForm other = (UserRequestForm) obj;
return new EqualsBuilder().
+ appendSuper(super.equals(obj)).
append(bpmnProcess, other.bpmnProcess).
append(username, other.username).
append(executionId, other.executionId).
@@ -196,7 +170,6 @@
append(assignee, other.assignee).
append(userTO, other.userTO).
append(userUR, other.userUR).
- append(properties, other.properties).
build();
}
}
diff --git a/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/to/SerializationTest.java b/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/to/SerializationTest.java
index eb7e2f0..d4f1fc8 100644
--- a/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/to/SerializationTest.java
+++ b/ext/flowable/common-lib/src/test/java/org/apache/syncope/common/lib/to/SerializationTest.java
@@ -27,9 +27,11 @@
import java.util.Date;
import java.util.UUID;
import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.form.FormProperty;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.common.lib.form.FormPropertyValue;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.request.UserUR;
-import org.apache.syncope.common.lib.types.UserRequestFormPropertyType;
import org.junit.jupiter.api.Test;
public abstract class SerializationTest {
@@ -54,14 +56,14 @@
userUR.getPlainAttrs().add(new AttrPatch.Builder(new Attr.Builder("schema1").value("value1").build()).build());
form.setUserUR(userUR);
- UserRequestFormProperty property = new UserRequestFormProperty();
+ FormProperty property = new FormProperty();
property.setId("printMode");
property.setName("Preferred print mode");
- property.setType(UserRequestFormPropertyType.Dropdown);
+ property.setType(FormPropertyType.Dropdown);
property.getDropdownValues().add(
- new UserRequestFormPropertyValue("8559d14d-58c2-46eb-a2d4-a7d35161e8f8", "value1"));
+ new FormPropertyValue("8559d14d-58c2-46eb-a2d4-a7d35161e8f8", "value1"));
property.getDropdownValues().add(
- new UserRequestFormPropertyValue(UUID.randomUUID().toString(), "value2 / value3"));
+ new FormPropertyValue(UUID.randomUUID().toString(), "value2 / value3"));
form.getProperties().add(property);
PagedResult<UserRequestForm> original = new PagedResult<>();
@@ -72,9 +74,8 @@
StringWriter writer = new StringWriter();
objectMapper().writeValue(writer, original);
- PagedResult<UserRequestForm> actual = objectMapper().readValue(writer.toString(),
- new TypeReference<>() {
- });
+ PagedResult<UserRequestForm> actual = objectMapper().readValue(writer.toString(), new TypeReference<>() {
+ });
assertEquals(original, actual);
}
}
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
index ae56ebb..5134570 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
@@ -28,16 +28,16 @@
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.form.FormProperty;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.common.lib.form.FormPropertyValue;
import org.apache.syncope.common.lib.request.PasswordPatch;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.UserRequest;
import org.apache.syncope.common.lib.to.UserRequestForm;
-import org.apache.syncope.common.lib.to.UserRequestFormProperty;
-import org.apache.syncope.common.lib.to.UserRequestFormPropertyValue;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.to.WorkflowTaskExecInput;
import org.apache.syncope.common.lib.types.ResourceOperation;
-import org.apache.syncope.common.lib.types.UserRequestFormPropertyType;
import org.apache.syncope.core.flowable.api.DropdownValueProvider;
import org.apache.syncope.core.flowable.api.UserRequestHandler;
import org.apache.syncope.core.flowable.support.DomainProcessEngine;
@@ -56,7 +56,6 @@
import org.apache.syncope.core.workflow.api.WorkflowException;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
-import org.flowable.engine.form.FormProperty;
import org.flowable.engine.form.FormType;
import org.flowable.engine.form.TaskFormData;
import org.flowable.engine.history.HistoricActivityInstance;
@@ -275,33 +274,33 @@
}
}
- protected static UserRequestFormPropertyType fromFlowableFormType(final FormType flowableFormType) {
- UserRequestFormPropertyType result = UserRequestFormPropertyType.String;
+ protected static FormPropertyType fromFlowableFormType(final FormType flowableFormType) {
+ FormPropertyType result = FormPropertyType.String;
if (null != flowableFormType.getName()) {
switch (flowableFormType.getName()) {
case "long":
- result = UserRequestFormPropertyType.Long;
+ result = FormPropertyType.Long;
break;
case "enum":
- result = UserRequestFormPropertyType.Enum;
+ result = FormPropertyType.Enum;
break;
case "date":
- result = UserRequestFormPropertyType.Date;
+ result = FormPropertyType.Date;
break;
case "boolean":
- result = UserRequestFormPropertyType.Boolean;
+ result = FormPropertyType.Boolean;
break;
case "dropdown":
- result = UserRequestFormPropertyType.Dropdown;
+ result = FormPropertyType.Dropdown;
break;
case "password":
- result = UserRequestFormPropertyType.Password;
+ result = FormPropertyType.Password;
break;
case "string":
@@ -389,7 +388,7 @@
getVariable(procInstId, FlowableRuntimeUtils.USER_UR, UserUR.class));
formTO.getProperties().addAll(props.stream().map(prop -> {
- UserRequestFormProperty propertyTO = new UserRequestFormProperty();
+ FormProperty propertyTO = new FormProperty();
propertyTO.setId(prop.getPropertyId());
propertyTO.setName(prop.getPropertyId());
propertyTO.setValue(prop.getPropertyValue());
@@ -404,7 +403,7 @@
final String procInstId,
final String taskId,
final String formKey,
- final List<FormProperty> props) {
+ final List<org.flowable.engine.form.FormProperty> props) {
UserRequestForm formTO = new UserRequestForm();
@@ -424,7 +423,7 @@
getVariable(procInstId, FlowableRuntimeUtils.USER_UR, UserUR.class));
formTO.getProperties().addAll(props.stream().map(fProp -> {
- UserRequestFormProperty propertyTO = new UserRequestFormProperty();
+ FormProperty propertyTO = new FormProperty();
propertyTO.setId(fProp.getId());
propertyTO.setName(fProp.getName());
propertyTO.setReadable(fProp.isReadable());
@@ -438,8 +437,7 @@
case Enum ->
((Map<String, String>) fProp.getType().getInformation("values")).
- forEach((key, value) -> propertyTO.getEnumValues().add(
- new UserRequestFormPropertyValue(key, value)));
+ forEach((key, value) -> propertyTO.getEnumValues().add(new FormPropertyValue(key, value)));
case Dropdown -> {
String valueProviderBean = (String) fProp.getType().getInformation(DropdownValueProvider.NAME);
@@ -447,7 +445,7 @@
DropdownValueProvider valueProvider = ApplicationContextProvider.getApplicationContext().
getBean(valueProviderBean, DropdownValueProvider.class);
valueProvider.getValues().forEach((key, value) -> propertyTO.getDropdownValues().add(
- new UserRequestFormPropertyValue(key, value)));
+ new FormPropertyValue(key, value)));
} catch (Exception e) {
LOG.error("Could not find bean {} of type {} for form property {}",
valueProviderBean, DropdownValueProvider.class.getName(), propertyTO.getId(), e);
@@ -623,7 +621,7 @@
protected Map<String, String> getPropertiesForSubmit(final UserRequestForm form) {
Map<String, String> props = new HashMap<>();
form.getProperties().stream().
- filter(UserRequestFormProperty::isWritable).
+ filter(FormProperty::isWritable).
forEach(prop -> props.put(prop.getId(), prop.getValue()));
return Collections.unmodifiableMap(props);
}
diff --git a/ext/flowable/pom.xml b/ext/flowable/pom.xml
index d12afd3..dabc6cf 100644
--- a/ext/flowable/pom.xml
+++ b/ext/flowable/pom.xml
@@ -45,7 +45,6 @@
<module>flowable-bpmn</module>
<module>client-console</module>
<module>client-enduser</module>
- <module>client-common-ui</module>
</modules>
</project>
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
index 41feaaa..f39f839 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
@@ -714,7 +714,7 @@
final SCIMPatchOperation op) {
confs.stream().
- filter(conf -> BooleanUtils.toBoolean(JexlUtils.evaluate(
+ filter(conf -> BooleanUtils.toBoolean(JexlUtils.evaluateExpr(
filter2JexlExpression(op.getPath().getFilter()),
new MapContext(Map.of("type", conf.getType().name()))).toString())).findFirst().
ifPresent(conf -> {
@@ -936,7 +936,7 @@
ifPresent(addressConf -> setAttribute(userUR.getPlainAttrs(), addressConf, op)));
} else if (op.getPath().getFilter() != null) {
conf.getUserConf().getAddresses().stream().
- filter(addressConf -> BooleanUtils.toBoolean(JexlUtils.evaluate(
+ filter(addressConf -> BooleanUtils.toBoolean(JexlUtils.evaluateExpr(
filter2JexlExpression(op.getPath().getFilter()),
new MapContext(Map.of("type", addressConf.getType().name()))).toString())).findFirst().
ifPresent(addressConf -> setAttribute(userUR.getPlainAttrs(), addressConf, op));
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
index d26defa..9aabf25 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java
@@ -151,7 +151,7 @@
if (CollectionUtils.isEmpty(op.getValue())) {
members(id).stream().filter(member -> op.getPath().getFilter() == null
? true
- : BooleanUtils.toBoolean(JexlUtils.evaluate(
+ : BooleanUtils.toBoolean(JexlUtils.evaluateExpr(
SCIMDataBinder.filter2JexlExpression(op.getPath().getFilter()),
new MapContext(Map.of("value", member))).toString())).
forEach(member -> changeMembership(member, id, op.getOp()));
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
index aec8c1e..c851145 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
@@ -36,7 +36,6 @@
import org.apache.syncope.common.lib.report.ReportConf;
import org.apache.syncope.common.lib.types.IdMImplementationType;
import org.apache.syncope.common.lib.types.IdRepoImplementationType;
-import org.apache.syncope.core.logic.job.MacroRunJobDelegate;
import org.apache.syncope.core.persistence.api.DomainHolder;
import org.apache.syncope.core.persistence.common.attrvalue.AlwaysTrueValidator;
import org.apache.syncope.core.persistence.common.attrvalue.BasicValidator;
@@ -50,6 +49,7 @@
import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule;
import org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup;
import org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup;
+import org.apache.syncope.core.provisioning.java.job.MacroJobDelegate;
import org.apache.syncope.core.provisioning.java.propagation.AzurePropagationActions;
import org.apache.syncope.core.provisioning.java.propagation.DBPasswordPropagationActions;
import org.apache.syncope.core.provisioning.java.propagation.GoogleAppsPropagationActions;
@@ -134,7 +134,7 @@
put(IdRepoImplementationType.ITEM_TRANSFORMER, classNames);
classNames = new HashSet<>();
- classNames.add(MacroRunJobDelegate.class.getName());
+ classNames.add(MacroJobDelegate.class.getName());
classNames.add(PullJobDelegate.class.getName());
classNames.add(PushJobDelegate.class.getName());
classNames.add(ExpiredAccessTokenCleanup.class.getName());
@@ -147,6 +147,9 @@
classNames = new HashSet<>();
put(IdRepoImplementationType.LOGIC_ACTIONS, classNames);
+ classNames = new HashSet<>();
+ classNames.add(TestMacroActions.class.getName());
+ put(IdRepoImplementationType.MACRO_ACTIONS, classNames);
classNames = new HashSet<>();
classNames.add(LDAPMembershipPropagationActions.class.getName());
@@ -179,7 +182,7 @@
classNames.add(EmailAddressValidator.class.getName());
classNames.add(AlwaysTrueValidator.class.getName());
classNames.add(BinaryValidator.class.getName());
- put(IdRepoImplementationType.VALIDATOR, classNames);
+ put(IdRepoImplementationType.ATTR_VALUE_VALIDATOR, classNames);
classNames = new HashSet<>();
classNames.add(TestNotificationRecipientsProvider.class.getName());
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
index 2289bd5..462570a 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
@@ -25,7 +25,7 @@
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.core.logic.AnyObjectLogic;
import org.apache.syncope.core.logic.RealmLogic;
-import org.apache.syncope.core.logic.api.Command;
+import org.apache.syncope.core.provisioning.api.macro.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommandArgs.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommandArgs.java
index 871e409..936b933 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommandArgs.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommandArgs.java
@@ -20,6 +20,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
+import java.util.Objects;
import org.apache.syncope.common.lib.command.CommandArgs;
public class TestCommandArgs extends CommandArgs {
@@ -64,6 +65,36 @@
}
@Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 89 * hash + Objects.hashCode(this.parentRealm);
+ hash = 89 * hash + Objects.hashCode(this.realmName);
+ hash = 89 * hash + Objects.hashCode(this.printerName);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final TestCommandArgs other = (TestCommandArgs) obj;
+ if (!Objects.equals(this.parentRealm, other.parentRealm)) {
+ return false;
+ }
+ if (!Objects.equals(this.realmName, other.realmName)) {
+ return false;
+ }
+ return Objects.equals(this.printerName, other.printerName);
+ }
+
+ @Override
public String toString() {
return "TestCommandArgs{"
+ "parentRealm=" + parentRealm
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestMacroActions.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestMacroActions.java
new file mode 100644
index 0000000..ebaeada
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestMacroActions.java
@@ -0,0 +1,44 @@
+/*
+ * 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.fit.core.reference;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.provisioning.api.macro.MacroActions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+public class TestMacroActions implements MacroActions {
+
+ @Autowired
+ private RealmDAO realmDAO;
+
+ @Autowired
+ private RealmSearchDAO realmSearchDAO;
+
+ @Transactional(readOnly = true)
+ @Override
+ public Map<String, String> getDropdownValues(final String formProperty) {
+ return realmSearchDAO.findChildren(realmDAO.getRoot()).stream().
+ collect(Collectors.toMap(Realm::getFullPath, Realm::getName));
+ }
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroITCase.java
deleted file mode 100644
index 250d5d4..0000000
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroITCase.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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.fit.core;
-
-import static org.awaitility.Awaitility.await;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import jakarta.ws.rs.core.Response;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.TimeUnit;
-import org.apache.commons.io.IOUtils;
-import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.command.CommandTO;
-import org.apache.syncope.common.lib.request.UserCR;
-import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.ExecTO;
-import org.apache.syncope.common.lib.to.ImplementationTO;
-import org.apache.syncope.common.lib.to.MacroTaskTO;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.ClientExceptionType;
-import org.apache.syncope.common.lib.types.ExecStatus;
-import org.apache.syncope.common.lib.types.IdRepoEntitlement;
-import org.apache.syncope.common.lib.types.IdRepoImplementationType;
-import org.apache.syncope.common.lib.types.ImplementationEngine;
-import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.beans.ExecSpecs;
-import org.apache.syncope.common.rest.api.beans.RealmQuery;
-import org.apache.syncope.common.rest.api.beans.TaskQuery;
-import org.apache.syncope.common.rest.api.service.TaskService;
-import org.apache.syncope.fit.AbstractITCase;
-import org.apache.syncope.fit.core.reference.TestCommand;
-import org.apache.syncope.fit.core.reference.TestCommandArgs;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-public class MacroITCase extends AbstractITCase {
-
- private static String MACRO_TASK_KEY;
-
- private static final TestCommandArgs TCA = new TestCommandArgs();
-
- static {
- TCA.setParentRealm("/odd");
- TCA.setRealmName("macro");
- TCA.setPrinterName("aprinter112");
- }
-
- @BeforeAll
- public static void testCommandsSetup() throws Exception {
- CommandITCase.testCommandSetup();
-
- ImplementationTO command = null;
- try {
- command = IMPLEMENTATION_SERVICE.read(
- IdRepoImplementationType.COMMAND, "GroovyCommand");
- } catch (SyncopeClientException e) {
- if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
- command = new ImplementationTO();
- command.setKey("GroovyCommand");
- command.setEngine(ImplementationEngine.GROOVY);
- command.setType(IdRepoImplementationType.COMMAND);
- command.setBody(IOUtils.toString(
- MacroITCase.class.getResourceAsStream("/GroovyCommand.groovy"), StandardCharsets.UTF_8));
- Response response = IMPLEMENTATION_SERVICE.create(command);
- command = IMPLEMENTATION_SERVICE.read(
- command.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
- assertNotNull(command.getKey());
- }
- }
- assertNotNull(command);
-
- if (MACRO_TASK_KEY == null) {
- MACRO_TASK_KEY = TASK_SERVICE.<MacroTaskTO>search(
- new TaskQuery.Builder(TaskType.MACRO).build()).getResult().
- stream().filter(t -> "Test Macro".equals(t.getName())).findFirst().map(MacroTaskTO::getKey).
- orElseGet(() -> {
- MacroTaskTO task = new MacroTaskTO();
- task.setName("Test Macro");
- task.setActive(true);
- task.setRealm("/odd");
- task.getCommands().add(new CommandTO.Builder("GroovyCommand").build());
- task.getCommands().add(
- new CommandTO.Builder(TestCommand.class.getSimpleName()).args(TCA).build());
-
- Response response = TASK_SERVICE.create(TaskType.MACRO, task);
- return response.getHeaderString(RESTHeaders.RESOURCE_KEY);
- });
- }
- }
-
- @AfterAll
- public static void cleanup() {
- TestCommandArgs args = new TestCommandArgs();
- try {
- ANY_OBJECT_SERVICE.delete(args.getPrinterName());
- REALM_SERVICE.delete(args.getParentRealm() + "/" + args.getRealmName());
- } catch (Exception e) {
- // ignore
- }
- }
-
- @Test
- public void execute() {
- int preExecs = TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().size();
- ExecTO execution = TASK_SERVICE.execute(new ExecSpecs.Builder().key(MACRO_TASK_KEY).build());
- assertNotNull(execution.getExecutor());
-
- await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> {
- try {
- return preExecs < TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().size();
- } catch (Exception e) {
- return false;
- }
- });
-
- ExecTO exec = TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().get(preExecs);
- assertEquals(ExecStatus.SUCCESS.name(), exec.getStatus());
-
- AnyObjectTO printer = ANY_OBJECT_SERVICE.read(PRINTER, TCA.getPrinterName());
- assertNotNull(printer);
- assertEquals(TCA.getParentRealm() + "/" + TCA.getRealmName(), printer.getRealm());
- assertFalse(REALM_SERVICE.search(
- new RealmQuery.Builder().base(printer.getRealm()).build()).getResult().isEmpty());
- }
-
- @Test
- public void cantExecute() {
- // 1. create Role for task execution
- RoleTO role = new RoleTO();
- role.setKey("new" + getUUIDString());
- role.getRealms().add("/even");
- role.getEntitlements().add(IdRepoEntitlement.TASK_EXECUTE);
- role = createRole(role);
- assertNotNull(role);
-
- // 2. create User with such a Role granted
- UserCR userCR = UserITCase.getUniqueSample("cantrunncommand@test.org");
- userCR.getRoles().add(role.getKey());
- UserTO userTO = createUser(userCR).getEntity();
- assertNotNull(userTO);
-
- // 3. attempt to run the macro task -> fail
- TaskService taskService = CLIENT_FACTORY.create(
- userTO.getUsername(), "password123").getService(TaskService.class);
- try {
- taskService.execute(new ExecSpecs.Builder().key(MACRO_TASK_KEY).build());
- fail();
- } catch (SyncopeClientException e) {
- assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
- }
- }
-}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroTaskITCase.java
new file mode 100644
index 0000000..ef83017
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/MacroTaskITCase.java
@@ -0,0 +1,266 @@
+/*
+ * 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.fit.core;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import jakarta.ws.rs.core.Response;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.command.CommandTO;
+import org.apache.syncope.common.lib.form.FormProperty;
+import org.apache.syncope.common.lib.form.FormPropertyType;
+import org.apache.syncope.common.lib.form.SyncopeForm;
+import org.apache.syncope.common.lib.request.UserCR;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.ExecTO;
+import org.apache.syncope.common.lib.to.FormPropertyDefTO;
+import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.to.MacroTaskTO;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.ExecStatus;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoImplementationType;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.ExecSpecs;
+import org.apache.syncope.common.rest.api.beans.RealmQuery;
+import org.apache.syncope.common.rest.api.beans.TaskQuery;
+import org.apache.syncope.common.rest.api.service.TaskService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.apache.syncope.fit.core.reference.TestCommand;
+import org.apache.syncope.fit.core.reference.TestCommandArgs;
+import org.apache.syncope.fit.core.reference.TestMacroActions;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class MacroTaskITCase extends AbstractITCase {
+
+ private static String MACRO_TASK_KEY;
+
+ private static final TestCommandArgs TCA = new TestCommandArgs();
+
+ static {
+ TCA.setParentRealm("${parent}");
+ TCA.setRealmName("${realm}");
+ TCA.setPrinterName("aprinter112");
+ }
+
+ @BeforeAll
+ public static void testCommandsSetup() throws Exception {
+ CommandITCase.testCommandSetup();
+
+ ImplementationTO command = null;
+ try {
+ command = IMPLEMENTATION_SERVICE.read(
+ IdRepoImplementationType.COMMAND, "GroovyCommand");
+ } catch (SyncopeClientException e) {
+ if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
+ command = new ImplementationTO();
+ command.setKey("GroovyCommand");
+ command.setEngine(ImplementationEngine.GROOVY);
+ command.setType(IdRepoImplementationType.COMMAND);
+ command.setBody(IOUtils.toString(
+ MacroTaskITCase.class.getResourceAsStream("/GroovyCommand.groovy"), StandardCharsets.UTF_8));
+ Response response = IMPLEMENTATION_SERVICE.create(command);
+ command = IMPLEMENTATION_SERVICE.read(
+ command.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+ assertNotNull(command.getKey());
+ }
+ }
+ assertNotNull(command);
+
+ ImplementationTO macroActions = null;
+ try {
+ macroActions = IMPLEMENTATION_SERVICE.read(IdRepoImplementationType.MACRO_ACTIONS,
+ TestMacroActions.class.getSimpleName());
+ } catch (SyncopeClientException e) {
+ if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
+ macroActions = new ImplementationTO();
+ macroActions.setKey(TestMacroActions.class.getSimpleName());
+ macroActions.setEngine(ImplementationEngine.JAVA);
+ macroActions.setType(IdRepoImplementationType.MACRO_ACTIONS);
+ macroActions.setBody(TestMacroActions.class.getName());
+ Response response = IMPLEMENTATION_SERVICE.create(macroActions);
+ macroActions = IMPLEMENTATION_SERVICE.read(
+ macroActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+ assertNotNull(macroActions.getKey());
+ }
+ }
+ assertNotNull(macroActions);
+
+ if (MACRO_TASK_KEY == null) {
+ MACRO_TASK_KEY = TASK_SERVICE.<MacroTaskTO>search(
+ new TaskQuery.Builder(TaskType.MACRO).build()).getResult().
+ stream().filter(t -> "Test Macro".equals(t.getName())).findFirst().map(MacroTaskTO::getKey).
+ orElseGet(() -> {
+ MacroTaskTO task = new MacroTaskTO();
+ task.setName("Test Macro");
+ task.setActive(true);
+ task.setRealm("/odd");
+ task.getCommands().add(new CommandTO.Builder("GroovyCommand").build());
+ task.getCommands().add(
+ new CommandTO.Builder(TestCommand.class.getSimpleName()).args(TCA).build());
+
+ FormPropertyDefTO realm = new FormPropertyDefTO();
+ realm.setKey("realm");
+ realm.setName("Realm");
+ realm.setWritable(true);
+ realm.setRequired(true);
+ realm.setType(FormPropertyType.String);
+ task.getFormPropertyDefs().add(realm);
+
+ FormPropertyDefTO parent = new FormPropertyDefTO();
+ parent.setKey("parent");
+ parent.setName("Parent Realm");
+ parent.setWritable(true);
+ parent.setRequired(true);
+ parent.setType(FormPropertyType.Dropdown);
+ task.getFormPropertyDefs().add(parent);
+
+ task.setMacroActions(TestMacroActions.class.getSimpleName());
+
+ Response response = TASK_SERVICE.create(TaskType.MACRO, task);
+ return response.getHeaderString(RESTHeaders.RESOURCE_KEY);
+ });
+ }
+ }
+
+ @AfterAll
+ public static void cleanup() {
+ TestCommandArgs args = new TestCommandArgs();
+ try {
+ ANY_OBJECT_SERVICE.delete(args.getPrinterName());
+ REALM_SERVICE.delete(args.getParentRealm() + "/" + args.getRealmName());
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ @Test
+ public void execute() {
+ SyncopeForm form = TASK_SERVICE.getMacroTaskForm(MACRO_TASK_KEY);
+ form.getProperty("realm").orElseThrow().setValue("macro");
+ FormProperty parent = form.getProperty("parent").orElseThrow();
+ assertTrue(parent.getDropdownValues().stream().anyMatch(v -> "/odd".equals(v.getKey())));
+ parent.setValue("/odd");
+
+ int preExecs = TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().size();
+ ExecTO execution = TASK_SERVICE.execute(new ExecSpecs.Builder().key(MACRO_TASK_KEY).build(), form);
+ assertNotNull(execution.getExecutor());
+
+ await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until(() -> {
+ try {
+ return preExecs < TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().size();
+ } catch (Exception e) {
+ return false;
+ }
+ });
+
+ ExecTO exec = TASK_SERVICE.read(TaskType.MACRO, MACRO_TASK_KEY, true).getExecutions().get(preExecs);
+ assertEquals(ExecStatus.SUCCESS.name(), exec.getStatus());
+
+ AnyObjectTO printer = ANY_OBJECT_SERVICE.read(PRINTER, TCA.getPrinterName());
+ assertNotNull(printer);
+ assertEquals("/odd/macro", printer.getRealm());
+ assertFalse(REALM_SERVICE.search(
+ new RealmQuery.Builder().base(printer.getRealm()).build()).getResult().isEmpty());
+ }
+
+ @Test
+ public void saveSameCommandMultipleOccurrencies() {
+ TestCommandArgs tca1 = new TestCommandArgs();
+ tca1.setParentRealm("parent1");
+ tca1.setRealmName("realm1");
+ tca1.setPrinterName("printer1");
+
+ MacroTaskTO task = new MacroTaskTO();
+ task.setName("saveSameCommandMultipleOccurrencies");
+ task.setActive(true);
+ task.setRealm("/");
+ task.getCommands().add(new CommandTO.Builder("GroovyCommand").build());
+ task.getCommands().add(new CommandTO.Builder(TestCommand.class.getSimpleName()).args(tca1).build());
+ task.getCommands().add(new CommandTO.Builder("GroovyCommand").build());
+
+ Response response = TASK_SERVICE.create(TaskType.MACRO, task);
+ String newTaskKey = response.getHeaderString(RESTHeaders.RESOURCE_KEY);
+
+ task = TASK_SERVICE.<MacroTaskTO>read(TaskType.MACRO, newTaskKey, false);
+ assertEquals(3, task.getCommands().size());
+ assertEquals("GroovyCommand", task.getCommands().get(0).getKey());
+ assertEquals(TestCommand.class.getSimpleName(), task.getCommands().get(1).getKey());
+ assertEquals(tca1, task.getCommands().get(1).getArgs());
+ assertEquals("GroovyCommand", task.getCommands().get(2).getKey());
+
+ TestCommandArgs tca2 = new TestCommandArgs();
+ tca2.setParentRealm("parent2");
+ tca2.setRealmName("realm2");
+ tca2.setPrinterName("printer2");
+ task.getCommands().add(new CommandTO.Builder(TestCommand.class.getSimpleName()).args(tca2).build());
+
+ TASK_SERVICE.update(TaskType.MACRO, task);
+
+ task = TASK_SERVICE.<MacroTaskTO>read(TaskType.MACRO, newTaskKey, false);
+ assertEquals(4, task.getCommands().size());
+ assertEquals("GroovyCommand", task.getCommands().get(0).getKey());
+ assertEquals(TestCommand.class.getSimpleName(), task.getCommands().get(1).getKey());
+ assertEquals(tca1, task.getCommands().get(1).getArgs());
+ assertEquals("GroovyCommand", task.getCommands().get(2).getKey());
+ assertEquals(TestCommand.class.getSimpleName(), task.getCommands().get(3).getKey());
+ assertEquals(tca2, task.getCommands().get(3).getArgs());
+ }
+
+ @Test
+ public void cantExecute() {
+ // 1. create Role for task execution
+ RoleTO role = new RoleTO();
+ role.setKey("new" + getUUIDString());
+ role.getRealms().add("/even");
+ role.getEntitlements().add(IdRepoEntitlement.TASK_EXECUTE);
+ role = createRole(role);
+ assertNotNull(role);
+
+ // 2. create User with such a Role granted
+ UserCR userCR = UserITCase.getUniqueSample("cantrunncommand@test.org");
+ userCR.getRoles().add(role.getKey());
+ UserTO userTO = createUser(userCR).getEntity();
+ assertNotNull(userTO);
+
+ // 3. attempt to run the macro task -> fail
+ TaskService taskService = CLIENT_FACTORY.create(
+ userTO.getUsername(), "password123").getService(TaskService.class);
+ try {
+ taskService.execute(new ExecSpecs.Builder().key(MACRO_TASK_KEY).build());
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.DelegatedAdministration, e.getType());
+ }
+ }
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
index 3f06b23..e22f385 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
@@ -581,10 +581,8 @@
@Test
public void issueSYNCOPE741() {
for (int i = 0; i < 3; i++) {
- TASK_SERVICE.execute(new ExecSpecs.Builder().
- key("1e697572-b896-484c-ae7f-0c8f63fcbc6c").build());
- TASK_SERVICE.execute(new ExecSpecs.Builder().
- key("316285cc-ae52-4ea2-a33b-7355e189ac3f").build());
+ TASK_SERVICE.execute(new ExecSpecs.Builder().key("1e697572-b896-484c-ae7f-0c8f63fcbc6c").build());
+ TASK_SERVICE.execute(new ExecSpecs.Builder().key("316285cc-ae52-4ea2-a33b-7355e189ac3f").build());
}
try {
Thread.sleep(3000);
diff --git a/fit/core-reference/src/test/resources/GroovyCommand.groovy b/fit/core-reference/src/test/resources/GroovyCommand.groovy
index 92a216a..7dcfd5b 100644
--- a/fit/core-reference/src/test/resources/GroovyCommand.groovy
+++ b/fit/core-reference/src/test/resources/GroovyCommand.groovy
@@ -19,7 +19,7 @@
import org.apache.syncope.common.lib.command.CommandArgs
import org.apache.syncope.core.logic.SyncopeLogic
-import org.apache.syncope.core.logic.api.Command
+import org.apache.syncope.core.provisioning.api.macro.Command
import org.springframework.beans.factory.annotation.Autowired
class GroovyCommand implements Command<CommandArgs> {
diff --git a/pom.xml b/pom.xml
index e0adf77..9fa89cc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -406,7 +406,7 @@
<connid.azure.version>2.0.2</connid.azure.version>
<connid.scim.version>1.0.4</connid.scim.version>
<connid.servicenow.version>1.0.3</connid.servicenow.version>
- <connid.okta.version>3.0.3</connid.okta.version>
+ <connid.okta.version>3.0.4</connid.okta.version>
<connid.cmd.version>0.5</connid.cmd.version>
<cxf.version>4.0.4</cxf.version>
@@ -495,7 +495,7 @@
<tomcat.version>10.1.23</tomcat.version>
<wildfly.version>32.0.0.Final</wildfly.version>
- <payara.version>6.2024.4</payara.version>
+ <payara.version>6.2024.5</payara.version>
<jakarta.faces.version>4.0.7</jakarta.faces.version>
<docker.postgresql.version>16</docker.postgresql.version>
diff --git a/src/main/asciidoc/reference-guide/architecture/core.adoc b/src/main/asciidoc/reference-guide/architecture/core.adoc
index e490ec3..e671146 100644
--- a/src/main/asciidoc/reference-guide/architecture/core.adoc
+++ b/src/main/asciidoc/reference-guide/architecture/core.adoc
@@ -68,9 +68,8 @@
The Workflow layer is responsible for managing the internal lifecycle of Users, Groups and Any Objects.
Besides the default engine, another engine is available based on https://www.flowable.org/[Flowable^], the
-reference open source http://www.bpmn.org/[BPMN 2.0^] implementation. It enables advanced features such
-as approval management and new statuses definitions; a web-based GUI editor, the
-https://www.flowable.org/docs/userguide/index.html#flowableModelerApp[Flowable Modeler^], is also available.
+reference open source http://www.bpmn.org/[BPMN 2.0^] implementation. It enables advanced features such as approval
+management and new statuses definitions; a web-based GUI editor to model workflows and user requests is also available.
[.text-center]
image::userWorkflow.png[title="Default Flowable user workflow",alt="Default Flowable user workflow"]
diff --git a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
index 319a2ac..f99e87a 100644
--- a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
@@ -27,10 +27,11 @@
** https://apereo.github.io/cas/6.6.x/authentication/Database-Authentication.html[Database^]
** https://apereo.github.io/cas/6.6.x/authentication/JAAS-Authentication.html[JAAS^]
** https://apereo.github.io/cas/6.6.x/authentication/LDAP-Authentication.html[LDAP^]
- ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Generic-OpenID-Connect.html[OpenID Connect^]
- ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-OAuth20.html[OAuth2^]
+ ** https://apereo.github.io/cas/6.6.x/authentication/SPNEGO-Authentication.html[SPNEGO^]
** https://apereo.github.io/cas/6.6.x/authentication/Syncope-Authentication.html[Syncope^]
** https://apereo.github.io/cas/6.6.x/authentication/X509-Authentication.html[X509^]
+ ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Generic-OpenID-Connect.html[OpenID Connect^]
+ ** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-OAuth20.html[OAuth2^]
** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-SAML.htmll[SAML^]
** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Apple.html[Apple Signin^]
** https://apereo.github.io/cas/6.6.x/integration/Delegate-Authentication-Azure-AD.html[Azure Active Directory^]
diff --git a/src/main/asciidoc/reference-guide/concepts/tasks.adoc b/src/main/asciidoc/reference-guide/concepts/tasks.adoc
index 044a863..d6f0887 100644
--- a/src/main/asciidoc/reference-guide/concepts/tasks.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/tasks.adoc
@@ -221,6 +221,18 @@
** when to start
** https://docs.spring.io/spring-framework/reference/integration/scheduling.html#scheduling-cron-expression[cron expression^]
+===== MacroActions
+
+Macro task execution can be decorated with custom logic to be invoked around task execution, by associating
+macro tasks to one or more <<implementations,implementations>> of the
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/MacroActions.java[MacroActions^]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/blob/3_0_X/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/api/MacroActions.java[MacroActions^]
+endif::[]
+interface.
+
[[tasks-scheduled]]
==== Scheduled
diff --git a/src/main/asciidoc/reference-guide/concepts/workflow.adoc b/src/main/asciidoc/reference-guide/concepts/workflow.adoc
index 69e7ec5..19a7558 100644
--- a/src/main/asciidoc/reference-guide/concepts/workflow.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/workflow.adoc
@@ -120,12 +120,11 @@
. Besides mandatory statuses, which are modeled as BPMN `userTask` instances, more can be freely added
at runtime, provided that adequate transitions and conditions are also inserted; more details about available BPMN
-constructs are available in the https://www.flowable.org/docs/userguide/index.html#bpmnConstructs[Flowable User Guide^]. +
+constructs are available in the https://www.flowable.com/open-source/docs/bpmn/ch07b-BPMN-Constructs[Flowable User Guide^]. +
Additional statuses and transitions allow the internal processes of Apache Syncope to better adapt to suit organizational flows.
. Custom logic can be injected into the workflow process by providing BPMN `serviceTask` instances.
-. https://www.flowable.org/docs/userguide/index.html#forms[Flowable forms^] are used for implementing <<approval,approval>>.
-. The https://www.flowable.org/docs/userguide/index.html#flowableModelerApp[Flowable Modeler^] is available with the
-<<admin-console,admin console>>, thus allowing web-based graphical modeling of the workflow definition.
+. Flowable forms are used for implementing <<approval,approval>>.
+. <<admin-console,admin console>> supports web-based graphical modeling of the workflow definition.
[.text-center]
image::userWorkflow.png[title="Default Flowable user workflow",alt="Default Flowable user workflow"]
@@ -139,7 +138,7 @@
Managers could also be asked to complete the information provided before the requested operation is finished.
In order to define an approval form, a dedicated BPMN `userTask` needs to be defined, following the rules established
-for https://www.flowable.org/docs/userguide/index.html#forms[Flowable forms^].
+for Flowable forms.
[NOTE]
.What is required for administrators to manage approval?
@@ -153,7 +152,7 @@
.. `USER_READ`
. The BPMN `userTask` must either indicate `U` among `candidateUsers` or at least one of the groups assigned to `U`
among `candidateGroups`, as required by
-https://www.flowable.org/docs/userguide/index.html#bpmnUserTaskUserAssignmentExtension[Flowable's task assignment rules^]
+https://www.flowable.com/open-source/docs/bpmn/ch07b-BPMN-Constructs#flowable-extensions-for-task-assignment[Flowable's task assignment rules^]
The special super-user `admin` is entitled to manage all approvals, even those not specifying any
`candidateUsers` or `candidateGroups`.
@@ -162,8 +161,8 @@
[[sample-selfreg-approval]]
.Approving self-registration
====
-The snippet below shows how to define an approval form in XML; the same operation can be performed via the
-https://www.flowable.org/docs/userguide/index.html#flowableModelerApp[Flowable Modeler^].
+The snippet below shows how to define an approval form in XML; the same operation can be performed via the GUI editor
+provided by <<admin-console,admin console>>.
[source,xml]
----
@@ -209,8 +208,8 @@
[[sample-user-request]]
.Assigning printer to user
====
-The BPMN process below shows how to define an user request in XML; the same operation can be performed via the
-https://www.flowable.org/docs/userguide/index.html#flowableModelerApp[Flowable Modeler^].
+The BPMN process below shows how to define an user request in XML; the same operation can be performed via the GUI
+editor provided by <<admin-console,admin console>>.
In this user request definition:
@@ -258,8 +257,7 @@
</process>
----
<1> the first form defined is self-assigned to the user which has started this request
-<2> the `dropdown` type is a Syncope extension of the
-https://www.flowable.org/docs/userguide/index.html#formProperties[form property types supported by Flowable^]
+<2> the `dropdown` type is a Syncope extension of the form property types supported by Flowable
and allows to inject a list of elements via the `dropdownValueProvider` value (with name `printersValueProvider` in this
sample), which must be a Spring bean implementing the
ifeval::["{snapshotOrRelease}" == "release"]
diff --git a/src/main/asciidoc/reference-guide/usage/customization.adoc b/src/main/asciidoc/reference-guide/usage/customization.adoc
index af42264..85d6e86 100644
--- a/src/main/asciidoc/reference-guide/usage/customization.adoc
+++ b/src/main/asciidoc/reference-guide/usage/customization.adoc
@@ -255,7 +255,7 @@
be provided - in the source tree under `core/src/main/java` when Java or via REST services if Groovy - for the following
components:
-* <<propagationactions,propagation>>, <<pushactions,push>>, <<pullactions,pull>> and <<logicactions,logic>> actions
+* <<propagationactions,propagation>>, <<pushactions,push>>, <<pullactions,pull>>, <<macroactions,macro>> and <<logicactions,logic>> actions
* <<push-correlation-rules,push>> / <<pull-correlation-rules,pull>> correlation rules
* <<pull-mode,reconciliation filter builders>>
* <<commands,commands>>
diff --git a/src/site/xdoc/building.xml b/src/site/xdoc/building.xml
index 5272b8f..b4b8386 100644
--- a/src/site/xdoc/building.xml
+++ b/src/site/xdoc/building.xml
@@ -35,7 +35,7 @@
<section name="Prerequisites">
<p>
<ul>
- <li>JDK 8 or higher for ≤ 2.1; JDK 11 or higher for later versions</li>
+ <li>JDK 8 for ≤ 2.1; JDK 11 for 3.0; JDK 21 for later versions</li>
<li>
Latest <a href="https://maven.apache.org/download.html">Apache Maven</a>
</li>
@@ -65,7 +65,7 @@
On Windows you can find it in several distributions including
<a href="http://gnuwin32.sourceforge.net/packages/patch.htm">GNUWin32</a>
</p>
- </subsection>
+ </subsection>
</section>
<section name="Building Syncope">
@@ -73,11 +73,11 @@
Before building Syncope, you need to setup an environment variable to give Maven more memory.
</p>
<p>
- On Unix with JDK 8 or later
+ On Unix
<source>export MAVEN_OPTS="-Xms512m -Xmx1024m"</source>
</p>
<p>
- On Windows with JDK 8 or later
+ On Windows
<source>set MAVEN_OPTS=-Xms512m -Xmx1024m</source>
</p>
<p>
@@ -144,9 +144,7 @@
</p>
<h4>HotSwapAgent</h4>
- Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled (requires
- <a href="https://github.com/dcevm/dcevm">DCEVM Java</a> installed as "alternative JVM" and IDE of choice
- <a href="http://hotswapagent.org/mydoc_setup_netbeans.html">set up properly</a>).
+ Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled.
<source>$ mvn -Photswap,all</source>
<h4>DBMSes</h4>
@@ -155,20 +153,32 @@
</div>
<h5>PostgreSQL</h5>
+ <div class="alert alert-warning">
+ <p>This build profile requires <a href="https://www.docker.com/">Docker</a> to work.</p>
+ </div>
Perform the full test suite against a real <a href="https://www.postgresql.org/">PostgreSQL</a> database via
<source>$ mvn -Ppostgres-it</source> or <source>$ mvn -Ppgjsonb-it</source> (for JSONB support)
<h5>MySQL</h5>
+ <div class="alert alert-warning">
+ <p>This build profile requires <a href="https://www.docker.com/">Docker</a> to work.</p>
+ </div>
Perform the full test suite against a real <a href="https://www.mysql.com/">MySQL</a> database via
<source>$ mvn -Pmysql-it</source> or <source>$ mvn -Pmyjson-it</source> (for JSON support)
<h5>MariaDB</h5>
+ <div class="alert alert-warning">
+ <p>This build profile requires <a href="https://www.docker.com/">Docker</a> to work.</p>
+ </div>
Perform the full test suite against a real <a href="https://mariadb.org/">MariaDB</a> database via
<source>$ mvn -Pmariadb-it</source>
<h5>Oracle database</h5>
+ <div class="alert alert-warning">
+ <p>This build profile requires <a href="https://www.docker.com/">Docker</a> to work.</p>
+ </div>
Perform the full test suite against a real <a href="https://www.oracle.com/products/database/">Oracle</a> database via
- <source>$ mvn -Poracle-it</source>
+ <source>$ mvn -Poracle-it</source> or <source>$ mvn -Pojson-it</source> (for JSON support)
<h5>MS SQL Server</h5>
Prform the full test suite against a real <a href="https://www.microsoft.com/en-us/sql-server/">MS SQL Server</a> database via
@@ -186,13 +196,21 @@
<a href="http://www.wildfly.org">Wildfly</a> via
<source>$ mvn -Pwildfly-it</source>
- <h4>Elasticsearch</h4>
- <div class="alert alert-warning">
- <p>This build profile require <a href="https://www.docker.com/">Docker</a> to work.</p>
- </div>
+ <h4>External search engines</h4>
+ <h5>Elasticsearch</h5>
+ <div class="alert alert-warning">
+ <p>This build profile requires <a href="https://www.docker.com/">Docker</a> to work.</p>
+ </div>
Perform the full test suite relying on a real <a href="https://www.elastic.co/">Elasticsearch</a> instance via
<source>$ mvn -Pelasticsearch-it</source>
+
+ <h5>OpenSearch</h5>
+ <div class="alert alert-warning">
+ <p>This build profile requires <a href="https://www.docker.com/">Docker</a> to work.</p>
+ </div>
+ Perform the full test suite relying on a real <a href="https://opensearch.org/">OpenSearch</a> instance via
+ <source>$ mvn -Popensearch-it</source>
</subsection>
<subsection name="fit/console-reference">
@@ -203,9 +221,7 @@
<source>$ mvn -Pdebug</source>
<h4>HotSwapAgent</h4>
- Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled (requires
- <a href="https://github.com/dcevm/dcevm">DCEVM Java</a> installed as "alternative JVM" and IDE of choice
- <a href="http://hotswapagent.org/mydoc_setup_netbeans.html">set up properly</a>).
+ Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled.
<source>$ mvn -Photswap</source>
</subsection>
@@ -217,9 +233,7 @@
<source>$ mvn -Pdebug</source>
<h4>HotSwapAgent</h4>
- Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled (requires
- <a href="https://github.com/dcevm/dcevm">DCEVM Java</a> installed as "alternative JVM" and IDE of choice
- <a href="http://hotswapagent.org/mydoc_setup_netbeans.html">set up properly</a>).
+ Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled.
<source>$ mvn -Photswap</source>
</subsection>
@@ -231,9 +245,7 @@
<source>$ mvn -Pdebug</source>
<h4>HotSwapAgent</h4>
- Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled (requires
- <a href="https://github.com/dcevm/dcevm">DCEVM Java</a> installed as "alternative JVM" and IDE of choice
- <a href="http://hotswapagent.org/mydoc_setup_netbeans.html">set up properly</a>).
+ Similar to Debug, but with <a href="http://hotswapagent.org/">HotSwapAgent</a> features enabled.
<source>$ mvn -Photswap</source>
</subsection>
</section>
diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java
index 7d44f46..17dd7dd 100644
--- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java
+++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/mapping/AuthModulePropertySourceMapper.java
@@ -22,6 +22,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
+import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.auth.AbstractOIDCAuthModuleConf;
@@ -477,7 +478,7 @@
props.getSystem().setKerberosKdc(conf.getKerberosKdc());
props.getSystem().setKerberosRealm(conf.getKerberosRealm());
props.getSystem().setKerberosConf(conf.getKerberosConf());
- props.getSystem().setKerberosDebug(conf.isKerberosDebug() ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ props.getSystem().setKerberosDebug(BooleanUtils.toStringTrueFalse(conf.isKerberosDebug()));
if (conf.getLdap() != null) {
SpnegoLdapProperties ldapProps = new SpnegoLdapProperties();