From a91205d5f01e3a46a9abd61b94d4641fb0dc4d65 Mon Sep 17 00:00:00 2001 From: virgo47 Date: Fri, 24 Jan 2020 14:02:21 +0100 Subject: [PATCH 01/41] first working REST-ish endpoint, very simple one --- gui/admin-gui/pom.xml | 5 + .../evolveum/midpoint/web/boot/WebConfig.java | 12 +- .../web/security/BasicWebSecurityConfig.java | 12 +- .../midpoint/web/security/PageUrlMapping.java | 4 + model/pom.xml | 2 + model/rest-impl/pom.xml | 28 +++++ .../midpoint/rest/impl/RestApiIndex.java | 118 ++++++++++++++++++ 7 files changed, 167 insertions(+), 14 deletions(-) create mode 100644 model/rest-impl/pom.xml create mode 100644 model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java diff --git a/gui/admin-gui/pom.xml b/gui/admin-gui/pom.xml index dcb8be4271f..0d99b89edc8 100644 --- a/gui/admin-gui/pom.xml +++ b/gui/admin-gui/pom.xml @@ -423,6 +423,11 @@ system-init 4.1-SNAPSHOT + + com.evolveum.midpoint.model + rest-impl + 4.1-SNAPSHOT + com.evolveum.midpoint.repo diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java index 90f73ec29ad..a102111a637 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java @@ -4,25 +4,21 @@ * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ - package com.evolveum.midpoint.web.boot; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.web.ResourceProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import com.evolveum.midpoint.gui.api.factory.GuiComponentFactory; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.impl.factory.LockoutStatusPanelFactory; -import com.evolveum.midpoint.gui.impl.factory.TextAreaPanelFactory; -import com.evolveum.midpoint.gui.impl.registry.GuiComponentRegistryImpl; import com.evolveum.midpoint.web.application.AsyncWebProcessManager; import com.evolveum.midpoint.web.application.AsyncWebProcessManagerImpl; import com.evolveum.midpoint.web.security.MidPointApplication; import com.evolveum.midpoint.web.util.validation.MidpointFormValidatorRegistry; import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -32,6 +28,8 @@ * Created by Viliam Repan (lazyman). */ @Configuration +@ComponentScan(basePackages = "com.evolveum.midpoint.rest.impl") +@EnableWebMvc public class WebConfig { @Bean @@ -44,7 +42,6 @@ public MidpointFormValidatorRegistry midpointFormValidatorRegistry() { return new MidpointFormValidatorRegistry(); } - @Bean public AsyncWebProcessManager asyncWebProcessManager() { return new AsyncWebProcessManagerImpl(); @@ -55,6 +52,7 @@ public static class StaticResourceConfiguration implements WebMvcConfigurer { @Autowired private ResourceProperties resourceProperties; + @Value("${midpoint.home}") private String midpointHome; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java index f623d554b1f..d6c0908985e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java @@ -16,10 +16,10 @@ import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.security.factory.channel.AuthChannelRegistryImpl; +import com.evolveum.midpoint.web.security.factory.module.AuthModuleRegistryImpl; import com.evolveum.midpoint.web.security.filter.MidpointAnonymousAuthenticationFilter; import com.evolveum.midpoint.web.security.filter.MidpointRequestAttributeAuthenticationFilter; import com.evolveum.midpoint.web.security.filter.configurers.AuthFilterConfigurer; -import com.evolveum.midpoint.web.security.factory.module.AuthModuleRegistryImpl; import org.jasig.cas.client.session.SingleSignOutFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -58,7 +58,6 @@ /** * @author skublik */ - @Order(SecurityProperties.BASIC_AUTH_ORDER - 1) @Configuration @EnableWebSecurity @@ -96,15 +95,12 @@ public class BasicWebSecurityConfig extends WebSecurityConfigurerAdapter { @Value("${auth.logout.url:/}") private String authLogoutUrl; - private ObjectPostProcessor objectObjectPostProcessor; - - public BasicWebSecurityConfig(){ + public BasicWebSecurityConfig() { super(true); } @Override public void setObjectPostProcessor(ObjectPostProcessor objectPostProcessor) { - this.objectObjectPostProcessor = objectPostProcessor; super.setObjectPostProcessor(objectPostProcessor); } @@ -171,6 +167,9 @@ public boolean matches(HttpServletRequest httpServletRequest) { if (mather.match("/ws/**", httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()))) { return true; } + if (mather.match("/rest2/**", httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()))) { + return true; + } return false; } }); @@ -211,7 +210,6 @@ protected void configure(HttpSecurity http) throws Exception { .securityContext(); http.apply(new AuthFilterConfigurer()); - http.sessionManagement() .maximumSessions(-1) .sessionRegistry(sessionRegistry) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/PageUrlMapping.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/PageUrlMapping.java index 64539d83f8e..81141c75875 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/PageUrlMapping.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/PageUrlMapping.java @@ -94,6 +94,10 @@ public enum PageUrlMapping { REST("/ws/rest/**", new DisplayableValue[]{ new AuthorizationActionValue(AUTZ_REST_ALL_URL, "RestEndpoint.authRest.all.label", "RestEndpoint.authRest.all.description") + }), + REST2("/rest2/**", new DisplayableValue[]{ + new AuthorizationActionValue(AUTZ_REST_ALL_URL, + "RestEndpoint.authRest.all.label", "RestEndpoint.authRest.all.description") }); private String url; diff --git a/model/pom.xml b/model/pom.xml index 6f23efaacd5..a5c031fbe42 100644 --- a/model/pom.xml +++ b/model/pom.xml @@ -51,6 +51,8 @@ report-impl certification-api certification-impl + + rest-impl diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml new file mode 100644 index 00000000000..4597710209f --- /dev/null +++ b/model/rest-impl/pom.xml @@ -0,0 +1,28 @@ + + + + + 4.0.0 + + + model + com.evolveum.midpoint + 4.1-SNAPSHOT + + + midPoint REST-ish service implementation + com.evolveum.midpoint.model + rest-impl + + + + org.springframework.boot + spring-boot-starter-web + + + diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java new file mode 100644 index 00000000000..bedcd0a6299 --- /dev/null +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java @@ -0,0 +1,118 @@ +package com.evolveum.midpoint.rest.impl; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.condition.MediaTypeExpression; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +/** + * Support for simple index page with REST API endpoints (HTML and JSON). + */ +@RestController +@RequestMapping("/rest2") +public class RestApiIndex { + + private final RequestMappingHandlerMapping handlerMapping; + private final List uiRestInfo; + + public RestApiIndex(RequestMappingHandlerMapping handlerMapping) { + this.handlerMapping = handlerMapping; + uiRestInfo = operationInfoStream() + .filter(info -> info.handler.getBeanType() + .getPackageName().startsWith("com.evolveum.midpoint.rest.")) + .collect(Collectors.toList()); + } + + @GetMapping() + public List index() { + return uiRestInfo.stream() + .flatMap(OperationInfo::operationJsonStream) + .sorted(Comparator.comparing(json -> json.urlPattern)) + .collect(Collectors.toList()); + } + + @GetMapping(value = "/x", produces = "text/html") + public String indexHtml() { + StringBuilder html = new StringBuilder("" + + "REST-ish API" + + "" + + "" + + "

REST operations

This is NOT Swagger! Click at your own risk!") + .toString(); + } + + private Stream operationInfoStream() { + return handlerMapping.getHandlerMethods().entrySet().stream() + .map(entry -> new OperationInfo(entry.getKey(), entry.getValue())); + } + + private static class OperationInfo { + final RequestMappingInfo mappingInfo; + final HandlerMethod handler; + + OperationInfo(RequestMappingInfo mappingInfo, HandlerMethod handler) { + this.mappingInfo = mappingInfo; + this.handler = handler; + } + + Stream operationJsonStream() { + return mappingInfo.getPatternsCondition().getPatterns().stream() + .map(pattern -> new OperationJson(pattern, + mappingInfo.getMethodsCondition().getMethods(), + mappingInfo.getConsumesCondition().getConsumableMediaTypes(), + mappingInfo.getProducesCondition().getExpressions())); + } + } + + @SuppressWarnings("WeakerAccess") + public static class OperationJson { + public final String urlPattern; + public final String[] methods; + public final String[] accepts; + public final String[] produces; + + public OperationJson(String urlPattern, + Set methods, + Set accepts, + Set produces) + { + this.urlPattern = urlPattern; + this.methods = toStringArray(methods); + this.accepts = toStringArray(accepts); + this.produces = toStringArray(produces); + } + + private String[] toStringArray(Collection collection) { + return collection.isEmpty() + ? null + : collection.stream() + .map(Object::toString) + .toArray(String[]::new); + } + } +} From 81d4439d0df000ed4aa68a649c9e9d60cd047a4a Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 24 Jan 2020 15:26:53 +0100 Subject: [PATCH 02/41] RestApiIndex.java: no tabs, of course, sorry for that --- .../midpoint/rest/impl/RestApiIndex.java | 171 +++++++++--------- 1 file changed, 83 insertions(+), 88 deletions(-) diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java index bedcd0a6299..eea6c9fe98d 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java @@ -1,13 +1,5 @@ package com.evolveum.midpoint.rest.impl; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,6 +10,10 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Support for simple index page with REST API endpoints (HTML and JSON). */ @@ -25,94 +21,93 @@ @RequestMapping("/rest2") public class RestApiIndex { - private final RequestMappingHandlerMapping handlerMapping; - private final List uiRestInfo; + private final RequestMappingHandlerMapping handlerMapping; + private final List uiRestInfo; - public RestApiIndex(RequestMappingHandlerMapping handlerMapping) { - this.handlerMapping = handlerMapping; - uiRestInfo = operationInfoStream() - .filter(info -> info.handler.getBeanType() - .getPackageName().startsWith("com.evolveum.midpoint.rest.")) - .collect(Collectors.toList()); - } + public RestApiIndex(RequestMappingHandlerMapping handlerMapping) { + this.handlerMapping = handlerMapping; + uiRestInfo = operationInfoStream() + .filter(info -> info.handler.getBeanType() + .getPackageName().startsWith("com.evolveum.midpoint.rest.")) + .collect(Collectors.toList()); + } - @GetMapping() - public List index() { - return uiRestInfo.stream() - .flatMap(OperationInfo::operationJsonStream) - .sorted(Comparator.comparing(json -> json.urlPattern)) - .collect(Collectors.toList()); - } + @GetMapping() + public List index() { + return uiRestInfo.stream() + .flatMap(OperationInfo::operationJsonStream) + .sorted(Comparator.comparing(json -> json.urlPattern)) + .collect(Collectors.toList()); + } - @GetMapping(value = "/x", produces = "text/html") - public String indexHtml() { - StringBuilder html = new StringBuilder("" - + "REST-ish API" - + "" - + "" - + "

REST operations

This is NOT Swagger! Click at your own risk!") - .toString(); - } + @GetMapping(value = "/x", produces = "text/html") + public String indexHtml() { + StringBuilder html = new StringBuilder("" + + "REST-ish API" + + "" + + "" + + "

REST operations

This is NOT Swagger! Click at your own risk!") + .toString(); + } - private Stream operationInfoStream() { - return handlerMapping.getHandlerMethods().entrySet().stream() - .map(entry -> new OperationInfo(entry.getKey(), entry.getValue())); - } + private Stream operationInfoStream() { + return handlerMapping.getHandlerMethods().entrySet().stream() + .map(entry -> new OperationInfo(entry.getKey(), entry.getValue())); + } - private static class OperationInfo { - final RequestMappingInfo mappingInfo; - final HandlerMethod handler; + private static class OperationInfo { + final RequestMappingInfo mappingInfo; + final HandlerMethod handler; - OperationInfo(RequestMappingInfo mappingInfo, HandlerMethod handler) { - this.mappingInfo = mappingInfo; - this.handler = handler; - } + OperationInfo(RequestMappingInfo mappingInfo, HandlerMethod handler) { + this.mappingInfo = mappingInfo; + this.handler = handler; + } - Stream operationJsonStream() { - return mappingInfo.getPatternsCondition().getPatterns().stream() - .map(pattern -> new OperationJson(pattern, - mappingInfo.getMethodsCondition().getMethods(), - mappingInfo.getConsumesCondition().getConsumableMediaTypes(), - mappingInfo.getProducesCondition().getExpressions())); - } - } + Stream operationJsonStream() { + return mappingInfo.getPatternsCondition().getPatterns().stream() + .map(pattern -> new OperationJson(pattern, + mappingInfo.getMethodsCondition().getMethods(), + mappingInfo.getConsumesCondition().getConsumableMediaTypes(), + mappingInfo.getProducesCondition().getExpressions())); + } + } - @SuppressWarnings("WeakerAccess") - public static class OperationJson { - public final String urlPattern; - public final String[] methods; - public final String[] accepts; - public final String[] produces; + @SuppressWarnings("WeakerAccess") + public static class OperationJson { + public final String urlPattern; + public final String[] methods; + public final String[] accepts; + public final String[] produces; - public OperationJson(String urlPattern, - Set methods, - Set accepts, - Set produces) - { - this.urlPattern = urlPattern; - this.methods = toStringArray(methods); - this.accepts = toStringArray(accepts); - this.produces = toStringArray(produces); - } + public OperationJson(String urlPattern, + Set methods, + Set accepts, + Set produces) { + this.urlPattern = urlPattern; + this.methods = toStringArray(methods); + this.accepts = toStringArray(accepts); + this.produces = toStringArray(produces); + } - private String[] toStringArray(Collection collection) { - return collection.isEmpty() - ? null - : collection.stream() - .map(Object::toString) - .toArray(String[]::new); - } - } + private String[] toStringArray(Collection collection) { + return collection.isEmpty() + ? null + : collection.stream() + .map(Object::toString) + .toArray(String[]::new); + } + } } From 9f3347efb5d08b25715df002578d69b890e8bbe8 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 24 Jan 2020 17:07:35 +0100 Subject: [PATCH 03/41] experiments on new ModelRestController --- model/rest-impl/pom.xml | 6 ++ .../rest/impl/ModelRestController.java | 55 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 4597710209f..0997dcb8a62 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -20,6 +20,12 @@ rest-impl + + com.evolveum.midpoint.model + model-impl + 4.1-SNAPSHOT + + org.springframework.boot spring-boot-starter-web diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java new file mode 100644 index 00000000000..169a87f95c3 --- /dev/null +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java @@ -0,0 +1,55 @@ +package com.evolveum.midpoint.rest.impl; + +import com.evolveum.midpoint.model.impl.ModelRestService; +import com.evolveum.midpoint.model.impl.util.RestServiceUtil; +import com.evolveum.midpoint.security.api.SecurityUtil; +import com.evolveum.midpoint.task.api.TaskManager; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/rest2") +public class ModelRestController { + + private static final Trace LOGGER = TraceManager.getTrace(ModelRestService.class); + + @Autowired private TaskManager taskManager; + + @GetMapping( + value = "/self", + produces = { + MediaType.APPLICATION_XML_VALUE, + MediaType.APPLICATION_JSON_UTF8_VALUE, + RestServiceUtil.APPLICATION_YAML + }) + public String getSelf() { //@Context MessageContext mc){ + LOGGER.debug("model rest service for get operation start"); +// Task task = initRequest(mc); +// OperationResult parentResult = task.getResult().createSubresult(OPERATION_SELF); +// Response response; + + try { + UserType loggedInUser = SecurityUtil.getPrincipal().getUser(); + System.out.println("loggedInUser = " + loggedInUser); +// PrismObject user = model.getObject(UserType.class, loggedInUser.getOid(), null, task, parentResult); +// response = RestServiceUtil.createResponse(Response.Status.OK, user, parentResult, true); +// parentResult.recordSuccessIfUnknown(); + } catch (SecurityViolationException e) { +// response = RestServiceUtil.handleException(parentResult, e); + e.printStackTrace(); + return e.getMessage(); + } + + // finishRequest(task, mc.getHttpServletRequest()); +// return response; + return "yes"; + } + +} From 0b9ece7aff10c1b33aac4e944bc88ff974756bbf Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 29 Jan 2020 20:57:55 +0100 Subject: [PATCH 04/41] rest-impl module also skips the tests (there are none yet) --- model/rest-impl/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 0997dcb8a62..05c7725d13d 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -19,6 +19,10 @@ com.evolveum.midpoint.model rest-impl + + true + + com.evolveum.midpoint.model From 7c8a0c67d3b2d01f3036a570eccd89b9558f8572 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 30 Jan 2020 18:03:19 +0100 Subject: [PATCH 05/41] Class.getPackageName is not yet in Java 8 --- .../java/com/evolveum/midpoint/rest/impl/RestApiIndex.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java index eea6c9fe98d..31d75da9e95 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java @@ -28,7 +28,8 @@ public RestApiIndex(RequestMappingHandlerMapping handlerMapping) { this.handlerMapping = handlerMapping; uiRestInfo = operationInfoStream() .filter(info -> info.handler.getBeanType() - .getPackageName().startsWith("com.evolveum.midpoint.rest.")) + // TODO getPackageName on Java 11 + .getPackage().getName().startsWith("com.evolveum.midpoint.rest.")) .collect(Collectors.toList()); } From 2f921a87f6c77bf7a928d0fd7da9a6bb9f71804f Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 30 Jan 2020 22:38:14 +0100 Subject: [PATCH 06/41] new REST experiments: added missing explicit dependencies --- model/rest-impl/pom.xml | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 05c7725d13d..76d582c4c1a 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -27,12 +27,44 @@ com.evolveum.midpoint.model model-impl - 4.1-SNAPSHOT + ${project.version} + + + com.evolveum.midpoint.repo + task-api + ${project.version} + + + com.evolveum.midpoint.repo + security-api + ${project.version} + + + com.evolveum.midpoint.infra + schema + ${project.version} + + + com.evolveum.midpoint.infra + util + ${project.version} org.springframework.boot spring-boot-starter-web + + org.springframework + spring-web + + + org.springframework + spring-webmvc + + + org.springframework + spring-beans + From fc62f240b241cef9713b895f58e9ae9162ea5006 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 31 Jan 2020 15:10:07 +0100 Subject: [PATCH 07/41] gui/admin-gui/pom.xml: rest-impl is just runtime dependency --- gui/admin-gui/pom.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/gui/admin-gui/pom.xml b/gui/admin-gui/pom.xml index 8dea942bc98..232f8ad894e 100644 --- a/gui/admin-gui/pom.xml +++ b/gui/admin-gui/pom.xml @@ -425,11 +425,7 @@ system-init 4.1-SNAPSHOT - - com.evolveum.midpoint.model - rest-impl - 4.1-SNAPSHOT - + com.evolveum.midpoint.repo @@ -443,6 +439,13 @@ ${project.version} runtime + + com.evolveum.midpoint.model + rest-impl + ${project.version} + runtime + + org.springframework spring-aspects From a99b4187c64e08ca3f34ffb282d25663ea27d5c8 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 31 Jan 2020 15:11:09 +0100 Subject: [PATCH 08/41] BasicWebSecurityConfig.java: reintroduction of objectObjectPostProcessor This is only a matter of feature/new-ws branch. --- .../evolveum/midpoint/web/security/BasicWebSecurityConfig.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java index 64396566876..1dced1bd5fc 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java @@ -92,12 +92,15 @@ public class BasicWebSecurityConfig extends WebSecurityConfigurerAdapter { @Value("${auth.logout.url:/}") private String authLogoutUrl; + private ObjectPostProcessor objectObjectPostProcessor; + public BasicWebSecurityConfig() { super(true); } @Override public void setObjectPostProcessor(ObjectPostProcessor objectPostProcessor) { + this.objectObjectPostProcessor = objectPostProcessor; super.setObjectPostProcessor(objectPostProcessor); } From 4b9dc5dfe6e9900bdb61448a74ff8498b128fc65 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Sat, 1 Feb 2020 18:06:10 +0100 Subject: [PATCH 09/41] fixes in security, so /rest2 gets principal ready --- .../evolveum/midpoint/web/security/BasicWebSecurityConfig.java | 3 --- .../com/evolveum/midpoint/web/security/util/SecurityUtils.java | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java index 1dced1bd5fc..31b58a0b51d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/BasicWebSecurityConfig.java @@ -164,9 +164,6 @@ public boolean matches(HttpServletRequest httpServletRequest) { if (mather.match("/ws/**", httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()))) { return true; } - if (mather.match("/rest2/**", httpServletRequest.getRequestURI().substring(httpServletRequest.getContextPath().length()))) { - return true; - } return false; } }); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/util/SecurityUtils.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/util/SecurityUtils.java index b4799f1520d..9fd6c62c967 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/util/SecurityUtils.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/security/util/SecurityUtils.java @@ -83,8 +83,9 @@ public static MidPointUserProfilePrincipal getPrincipalUser() { private static final Map LOCAL_PATH_AND_CHANNEL; static { - Map map = new HashMap(); + Map map = new HashMap<>(); map.put("ws/rest", SchemaConstants.CHANNEL_REST_URI); + map.put("rest2", SchemaConstants.CHANNEL_REST_URI); map.put("actuator", SchemaConstants.CHANNEL_ACTUATOR_URI); map.put("resetPassword", SchemaConstants.CHANNEL_GUI_RESET_PASSWORD_URI); map.put("registration", SchemaConstants.CHANNEL_GUI_SELF_REGISTRATION_URI); From 97ad6aa819c55c30f8fa6a8d6c236eeefdbdce02 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 3 Feb 2020 23:08:22 +0100 Subject: [PATCH 10/41] basic /rest2/self implementation, but doesn't serialize PrismObject yet --- model/rest-impl/pom.xml | 5 ++ .../rest/impl/ModelRestController.java | 68 +++++++++++++------ 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 76d582c4c1a..00f58a2aff7 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -29,6 +29,11 @@ model-impl ${project.version} + + com.evolveum.midpoint.infra + prism-api + ${project.version} + com.evolveum.midpoint.repo task-api diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java index 169a87f95c3..5f3ad35d0a0 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java @@ -1,18 +1,27 @@ package com.evolveum.midpoint.rest.impl; +import static com.evolveum.midpoint.model.impl.ModelRestService.OPERATION_SELF; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.evolveum.midpoint.model.impl.ModelCrudService; import com.evolveum.midpoint.model.impl.ModelRestService; import com.evolveum.midpoint.model.impl.util.RestServiceUtil; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.SecurityUtil; +import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.task.api.TaskManager; -import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/rest2") @@ -21,6 +30,7 @@ public class ModelRestController { private static final Trace LOGGER = TraceManager.getTrace(ModelRestService.class); @Autowired private TaskManager taskManager; + @Autowired private ModelCrudService model; @GetMapping( value = "/self", @@ -29,27 +39,47 @@ public class ModelRestController { MediaType.APPLICATION_JSON_UTF8_VALUE, RestServiceUtil.APPLICATION_YAML }) - public String getSelf() { //@Context MessageContext mc){ + public ResponseEntity getSelf() { + //@Context MessageContext mc){ TODO otazka: toto uz nechceme pouzivat? po novom to uz nepotrebujeme na init requestu? LOGGER.debug("model rest service for get operation start"); -// Task task = initRequest(mc); -// OperationResult parentResult = task.getResult().createSubresult(OPERATION_SELF); -// Response response; + // uses experimental version, does not require CXF/JAX-RS + Task task = RestServiceUtil.initRequest(taskManager); + OperationResult parentResult = task.getResult().createSubresult(OPERATION_SELF); + ResponseEntity response; try { UserType loggedInUser = SecurityUtil.getPrincipal().getUser(); System.out.println("loggedInUser = " + loggedInUser); -// PrismObject user = model.getObject(UserType.class, loggedInUser.getOid(), null, task, parentResult); -// response = RestServiceUtil.createResponse(Response.Status.OK, user, parentResult, true); -// parentResult.recordSuccessIfUnknown(); - } catch (SecurityViolationException e) { -// response = RestServiceUtil.handleException(parentResult, e); + PrismObject user = model.getObject(UserType.class, loggedInUser.getOid(), null, task, parentResult); + response = createResponse(HttpStatus.OK, user, parentResult, true); + parentResult.recordSuccessIfUnknown(); + } catch (SecurityViolationException | ObjectNotFoundException | SchemaException | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { e.printStackTrace(); - return e.getMessage(); + response = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); } - // finishRequest(task, mc.getHttpServletRequest()); -// return response; - return "yes"; + // TODO ako tu nahradit mc? ak staci len HSRequest, tak to asi bude trivialne + // finishRequest(task, mc.getHttpServletRequest()); + return response; + } + + public static ResponseEntity createResponse(HttpStatus statusCode, T body, OperationResult result, boolean sendOriginObjectIfNotSuccess) { + result.computeStatusIfUnknown(); + +// if (result.isPartialError()) { +// return createBody(Response.status(250), sendOriginObjectIfNotSuccess, body, result); +// } else if (result.isHandledError()) { +// return createBody(Response.status(240), sendOriginObjectIfNotSuccess, body, result); +// } + + return ResponseEntity.status(statusCode).body(body); } +// private static ResponseEntity createBody( +// Response.ResponseBuilder builder, boolean sendOriginObjectIfNotSuccess, T body, OperationResult result) { +// if (sendOriginObjectIfNotSuccess) { +// return builder.entity(body); +// } +// return builder.entity(result); +// } } From 70fdea2f4e88e49137dad08db6e638cc46d62b2a Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Fri, 7 Feb 2020 09:11:01 +0100 Subject: [PATCH 11/41] experimental ModelRestController: added finishRequest --- .../rest/impl/ModelRestController.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java index 5f3ad35d0a0..8fe49b8f1d6 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java @@ -2,6 +2,8 @@ import static com.evolveum.midpoint.model.impl.ModelRestService.OPERATION_SELF; +import javax.servlet.http.HttpServletRequest; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -12,6 +14,7 @@ import com.evolveum.midpoint.model.impl.ModelCrudService; import com.evolveum.midpoint.model.impl.ModelRestService; +import com.evolveum.midpoint.model.impl.security.SecurityHelper; import com.evolveum.midpoint.model.impl.util.RestServiceUtil; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.schema.result.OperationResult; @@ -31,6 +34,7 @@ public class ModelRestController { @Autowired private TaskManager taskManager; @Autowired private ModelCrudService model; + @Autowired private SecurityHelper securityHelper; @GetMapping( value = "/self", @@ -39,7 +43,7 @@ public class ModelRestController { MediaType.APPLICATION_JSON_UTF8_VALUE, RestServiceUtil.APPLICATION_YAML }) - public ResponseEntity getSelf() { + public ResponseEntity getSelf(HttpServletRequest request) { //@Context MessageContext mc){ TODO otazka: toto uz nechceme pouzivat? po novom to uz nepotrebujeme na init requestu? LOGGER.debug("model rest service for get operation start"); // uses experimental version, does not require CXF/JAX-RS @@ -53,13 +57,13 @@ public ResponseEntity getSelf() { PrismObject user = model.getObject(UserType.class, loggedInUser.getOid(), null, task, parentResult); response = createResponse(HttpStatus.OK, user, parentResult, true); parentResult.recordSuccessIfUnknown(); - } catch (SecurityViolationException | ObjectNotFoundException | SchemaException | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { + } catch (SecurityViolationException | ObjectNotFoundException | SchemaException | + CommunicationException | ConfigurationException | ExpressionEvaluationException e) { e.printStackTrace(); response = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); } - // TODO ako tu nahradit mc? ak staci len HSRequest, tak to asi bude trivialne - // finishRequest(task, mc.getHttpServletRequest()); + finishRequest(task, request); return response; } @@ -82,4 +86,13 @@ public static ResponseEntity createResponse(HttpStatus statusCode, T body // } // return builder.entity(result); // } + + private void finishRequest(Task task, HttpServletRequest request) { +// if (isExperimentalEnabled()) { +// auditEvent(request); +// SecurityContextHolder.getContext().setAuthentication(null); +// } else { + RestServiceUtil.finishRequest(task, securityHelper); +// } + } } From c599d11d41a73c8bd9f623736a118a8ff9852f0d Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 12 Feb 2020 15:13:56 +0100 Subject: [PATCH 12/41] added JSON serialization, http :8080/.../rest2/self -j works now --- gui/admin-gui/pom.xml | 1 - .../evolveum/midpoint/web/boot/WebConfig.java | 16 ++--- model/rest-impl/pom.xml | 22 +++++- .../rest/impl/ModelRestController.java | 2 +- .../midpoint/rest/impl/RestConfig.java | 68 +++++++++++++++++++ pom.xml | 33 ++++----- 6 files changed, 110 insertions(+), 32 deletions(-) create mode 100644 model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java diff --git a/gui/admin-gui/pom.xml b/gui/admin-gui/pom.xml index 693242f1009..70b2470070e 100644 --- a/gui/admin-gui/pom.xml +++ b/gui/admin-gui/pom.xml @@ -360,7 +360,6 @@ com.evolveum.midpoint.model rest-impl ${project.version} - runtime diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java index a102111a637..b2406601510 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java @@ -6,30 +6,28 @@ */ package com.evolveum.midpoint.web.boot; +import java.time.Duration; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.web.ResourceProperties; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.evolveum.midpoint.web.application.AsyncWebProcessManager; import com.evolveum.midpoint.web.application.AsyncWebProcessManagerImpl; import com.evolveum.midpoint.web.security.MidPointApplication; import com.evolveum.midpoint.web.util.validation.MidpointFormValidatorRegistry; -import org.springframework.http.CacheControl; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import java.time.Duration; /** * Created by Viliam Repan (lazyman). */ @Configuration -@ComponentScan(basePackages = "com.evolveum.midpoint.rest.impl") -@EnableWebMvc +@Import(com.evolveum.midpoint.rest.impl.RestConfig.class) public class WebConfig { @Bean diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 00f58a2aff7..4ede679178f 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -11,14 +11,14 @@ model - com.evolveum.midpoint + com.evolveum.midpoint.model 4.1-SNAPSHOT - midPoint REST-ish service implementation - com.evolveum.midpoint.model rest-impl + midPoint REST-ish service implementation + true @@ -71,5 +71,21 @@ org.springframework spring-beans + + javax.servlet + servlet-api + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-core + + + org.springframework + spring-context + diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java index 8fe49b8f1d6..0841ec955e6 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java @@ -44,7 +44,7 @@ public class ModelRestController { RestServiceUtil.APPLICATION_YAML }) public ResponseEntity getSelf(HttpServletRequest request) { - //@Context MessageContext mc){ TODO otazka: toto uz nechceme pouzivat? po novom to uz nepotrebujeme na init requestu? + //@Context MessageContext mc){ TODO: do we need it in init request in new erea? LOGGER.debug("model rest service for get operation start"); // uses experimental version, does not require CXF/JAX-RS Task task = RestServiceUtil.initRequest(taskManager); diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java new file mode 100644 index 00000000000..abba51940ab --- /dev/null +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.rest.impl; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; + +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismSerializer; +import com.evolveum.midpoint.prism.SerializationOptions; +import com.evolveum.midpoint.util.exception.SchemaException; + +/** + * Spring configuration for MVC-based REST service. TODO: experimental as of early 2020 + * This prepares needed (de)serializers and also drives the package scan for REST controllers. + */ +@ComponentScan +@EnableWebMvc +public class RestConfig extends WebMvcConfigurationSupport { + + @Bean + public MappingJackson2HttpMessageConverter myConverter(ObjectMapper objectMapper) { + return new MappingJackson2HttpMessageConverter(objectMapper); + } + + @Bean + public ObjectMapper jsonObjectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + + SimpleModule module = new SimpleModule(); + module.addSerializer(PrismObject.class, prismObjectJsonSerializer()); + objectMapper.registerModule(module); + + return objectMapper; + } + + @SuppressWarnings("rawtypes") // no , otherwise the line module.addSerializer doesn't compile + private JsonSerializer prismObjectJsonSerializer() { + return new JsonSerializer() { + @Override + public void serialize(PrismObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + PrismSerializer prismSerializer = value.getPrismContext().jsonSerializer() + .options(SerializationOptions.createSerializeReferenceNames()); + try { + // TODO: this is just one instance of from MidpointAbstractProvider#writeTo - how much more do we need? + String rawJson = prismSerializer.serialize(value); + gen.writeRaw(rawJson); + } catch (SchemaException e) { + throw new IOException("JSON serialization problems", e); + } + } + }; + } +} diff --git a/pom.xml b/pom.xml index 1c6aa014594..904d44a460c 100644 --- a/pom.xml +++ b/pom.xml @@ -1720,8 +1720,7 @@ javax.xml.bind:jaxb-api - com.sun.activation:javax.activation - + com.sun.activation:javax.activation javax.activation:activation - com.evolveum.midpoint.tools:test-ng - + com.evolveum.midpoint.tools:test-ng org.testng:testng commons-lang:commons-lang xml-apis:xml-apis stax:stax-api - org.springframework.ldap:spring-ldap-core - + org.springframework.ldap:spring-ldap-core org.codehaus.woodstox:stax2-api @@ -1751,28 +1748,28 @@ javax.xml.bind:jaxb-api - com.sun.activation:javax.activation - + com.sun.activation:javax.activation javax.activation:activation xml-apis:xml-apis stax:stax-api - org.springframework.ldap:spring-ldap-core - + org.springframework.ldap:spring-ldap-core - jakarta.activation:jakarta.activation-api - - jakarta.xml.bind:jakarta.xml.bind-api - + jakarta.activation:jakarta.activation-api + jakarta.xml.bind:jakarta.xml.bind-api org.glassfish.jaxb:xsom org.glassfish.jaxb:jaxb-runtime - org.danekja:jdk-serializable-functional - - org.springframework.boot:spring-boot-test - + org.danekja:jdk-serializable-functional + org.springframework.boot:spring-boot-test + + + org.apache.tomcat.embed:tomcat-embed-core From 05883787daff5b60f36a82c1bd544d10fbdd08aa Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 9 Apr 2020 12:28:25 +0200 Subject: [PATCH 13/41] POM fixes after master merge, fixed falsely reported used tomcat --- gui/admin-gui/pom.xml | 5 ----- model/rest-impl/pom.xml | 4 ---- pom.xml | 4 ++++ 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/gui/admin-gui/pom.xml b/gui/admin-gui/pom.xml index 6e67779ad14..a9bb178452d 100644 --- a/gui/admin-gui/pom.xml +++ b/gui/admin-gui/pom.xml @@ -357,11 +357,6 @@ ${project.version} - - org.springframework - spring-aspects - runtime - org.codehaus.groovy groovy-all diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 4ede679178f..98be421433f 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -55,10 +55,6 @@ ${project.version} - - org.springframework.boot - spring-boot-starter-web - org.springframework spring-web diff --git a/pom.xml b/pom.xml index e4635172e98..44441820a0c 100644 --- a/pom.xml +++ b/pom.xml @@ -1768,6 +1768,7 @@ org.slf4j:jcl-over-slf4j org.slf4j:slf4j-api org.danekja:jdk-serializable-functional + org.apache.tomcat.embed:tomcat-embed-core @@ -1782,7 +1783,9 @@ maven-checkstyle-plugin ${checkstyle.version} + ${maven.multiModuleProjectDirectory}/config/checkstyle/checkstyle.xml + ${maven.multiModuleProjectDirectory}/config/checkstyle/checkstyle-suppressions.xml UTF-8 true @@ -2025,6 +2028,7 @@ ${checkstyle.version} + ${maven.multiModuleProjectDirectory}/build-system/checkstyle-configs/checkstyle.xml UTF-8 From 0c02eb5f5c45f2bad364688a223f9fa8c33ce8be Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 9 Apr 2020 12:29:29 +0200 Subject: [PATCH 14/41] ModelRestController.java: fix for principal returning Focus, not User --- .../evolveum/midpoint/rest/impl/ModelRestController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java index 0841ec955e6..3d5f7a477c9 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java @@ -24,6 +24,7 @@ import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @RestController @@ -44,7 +45,7 @@ public class ModelRestController { RestServiceUtil.APPLICATION_YAML }) public ResponseEntity getSelf(HttpServletRequest request) { - //@Context MessageContext mc){ TODO: do we need it in init request in new erea? + //@Context MessageContext mc){ TODO: do we need it in init request in new era? LOGGER.debug("model rest service for get operation start"); // uses experimental version, does not require CXF/JAX-RS Task task = RestServiceUtil.initRequest(taskManager); @@ -52,7 +53,7 @@ public ResponseEntity getSelf(HttpServletRequest request) { ResponseEntity response; try { - UserType loggedInUser = SecurityUtil.getPrincipal().getUser(); + FocusType loggedInUser = SecurityUtil.getPrincipal().getFocus(); System.out.println("loggedInUser = " + loggedInUser); PrismObject user = model.getObject(UserType.class, loggedInUser.getOid(), null, task, parentResult); response = createResponse(HttpStatus.OK, user, parentResult, true); @@ -67,7 +68,8 @@ public ResponseEntity getSelf(HttpServletRequest request) { return response; } - public static ResponseEntity createResponse(HttpStatus statusCode, T body, OperationResult result, boolean sendOriginObjectIfNotSuccess) { + public static ResponseEntity createResponse( + HttpStatus statusCode, T body, OperationResult result, boolean sendOriginObjectIfNotSuccess) { result.computeStatusIfUnknown(); // if (result.isPartialError()) { From d8ce11954946037cd18d17fccafa2116efb308b0 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 16 Apr 2020 13:06:03 +0200 Subject: [PATCH 15/41] rest-impl experimental module: version bump to 4.2-SNAPSHOT --- model/rest-impl/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 98be421433f..96b7af62619 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -12,7 +12,7 @@ model com.evolveum.midpoint.model - 4.1-SNAPSHOT + 4.2-SNAPSHOT rest-impl @@ -69,7 +69,7 @@ javax.servlet - servlet-api + javax.servlet-api com.fasterxml.jackson.core From a184ad3a97d64828d1471d9a4a236cc17e95d476 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 16 Apr 2020 13:06:08 +0200 Subject: [PATCH 16/41] removed ReportWebService(Raw), related port, tests --- .../midpoint/report/api/ReportPort.java | 23 -- model/report-impl/pom.xml | 8 - .../report/impl/ReportWebService.java | 213 ----------------- .../report/impl/ReportWebServiceRaw.java | 174 -------------- .../src/main/resources/ctx-report.xml | 41 +--- .../midpoint/report/TestReportWebService.java | 219 ------------------ .../report/TestReportWebServiceSafe.java | 27 --- .../test/resources/ctx-report-test-main.xml | 23 +- model/report-impl/testng-integration.xml | 2 - 9 files changed, 10 insertions(+), 720 deletions(-) delete mode 100644 model/report-api/src/main/java/com/evolveum/midpoint/report/api/ReportPort.java delete mode 100644 model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebService.java delete mode 100644 model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebServiceRaw.java delete mode 100644 model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebService.java delete mode 100644 model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebServiceSafe.java diff --git a/model/report-api/src/main/java/com/evolveum/midpoint/report/api/ReportPort.java b/model/report-api/src/main/java/com/evolveum/midpoint/report/api/ReportPort.java deleted file mode 100644 index 92b7df69d67..00000000000 --- a/model/report-api/src/main/java/com/evolveum/midpoint/report/api/ReportPort.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.report.api; - -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.schema.constants.SchemaConstants; - -public interface ReportPort { - String CLASS_NAME_WITH_DOT = ReportPort.class.getName() + "."; - String PROCESS_REPORT = CLASS_NAME_WITH_DOT + "processReport"; - - QName PARSE_QUERY_RESPONSE = new QName(SchemaConstants.NS_REPORT_WS, "parseQueryResponse"); - QName PROCESS_REPORT_RESPONSE = new QName(SchemaConstants.NS_REPORT_WS, "processReportResponse"); - QName EVALUATE_SCRIPT_RESPONSE = new QName(SchemaConstants.NS_REPORT_WS, "evaluateScriptResponse"); - QName EVALUATE_AUDIT_SCRIPT_RESPONSE = new QName(SchemaConstants.NS_REPORT_WS, "evaluateAuditScriptResponse"); - QName SEARCH_OBJECTS_RESPONSE = new QName(SchemaConstants.NS_REPORT_WS, "searchObjectsResponse"); - QName GET_FIELD_VALUE_RESPONSE = new QName(SchemaConstants.NS_REPORT_WS, "getFieldValueResponse"); -} diff --git a/model/report-impl/pom.xml b/model/report-impl/pom.xml index 6980d5cfafc..f184371fb24 100644 --- a/model/report-impl/pom.xml +++ b/model/report-impl/pom.xml @@ -152,14 +152,6 @@ javax.annotation javax.annotation-api - - javax.xml.ws - jaxws-api - - - javax.xml.soap - javax.xml.soap-api - diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebService.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebService.java deleted file mode 100644 index 7e7650053a0..00000000000 --- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebService.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.report.impl; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.logging.Logger; - -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.xml.ns._public.common.common_3.SelectorQualifiedGetOptionsType; - -import org.apache.commons.lang3.StringUtils; -import org.apache.cxf.interceptor.Fault; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.evolveum.midpoint.audit.api.AuditEventRecord; -import com.evolveum.midpoint.model.api.ModelAuthorizationAction; -import com.evolveum.midpoint.model.common.util.AbstractModelWebService; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.report.api.ReportPort; -import com.evolveum.midpoint.report.api.ReportService; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.MiscSchemaUtil; -import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters; -import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType; -import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordListType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportParameterType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.RemoteReportParameterType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.RemoteReportParametersType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.ReportPortType; - -@Service -public class ReportWebService extends AbstractModelWebService implements ReportPortType, ReportPort { - - private static final String OP_EVALUATE_SCRIPT = ReportWebService.class.getName() + ".evaluateScript"; - private static final String OP_EVALUATE_AUDIT_SCRIPT = ReportWebService.class.getName() + ".evaluateAuditScript"; - private static final String OP_PROCESS_REPORT = ReportWebService.class.getName() + ".processReport"; - - private static final Trace LOGGER = TraceManager.getTrace(ReportWebService.class); - - @Autowired private PrismContext prismContext; - @Autowired private ReportService reportService; - - @Override - public ObjectListType evaluateScript(String reportOid, String script, RemoteReportParametersType parameters) { - - Task task = createTaskInstance(OP_EVALUATE_SCRIPT); - auditLogin(task); - OperationResult operationResult = task.getResult(); - - try { - - PrismObject report = authorizeReportProcessing("evaluateScript", reportOid, task, operationResult); - - VariablesMap params = getParamsMap(parameters); - Collection resultList = reportService.evaluateScript(report, script, params, task, operationResult); - return createObjectListType(resultList); - } catch (Throwable e) { - throw new Fault(e); - } - - } - - @Override - public AuditEventRecordListType evaluateAuditScript(String reportOid, String script, RemoteReportParametersType parameters) { - - Task task = createTaskInstance(OP_EVALUATE_AUDIT_SCRIPT); - auditLogin(task); - OperationResult operationResult = task.getResult(); - - try { - PrismObject report = authorizeReportProcessing("evaluateAuditScript", reportOid, task, operationResult); - - VariablesMap params = getParamsMap(parameters); - Collection resultList = reportService.evaluateAuditScript(report, script, params, task, operationResult); - return createAuditEventRecordListType(resultList); - } catch (Throwable e) { - // TODO Auto-generated catch block - throw new Fault(e); - } - - } - - private VariablesMap getParamsMap(RemoteReportParametersType parametersType) throws SchemaException { - - prismContext.adopt(parametersType); - VariablesMap parametersMap = new VariablesMap(); - if (parametersType == null || parametersType.getRemoteParameter() == null - || parametersType.getRemoteParameter().isEmpty()) { - return parametersMap; - } - List items = parametersType.getRemoteParameter(); - for (RemoteReportParameterType item : items) { - String paramName = item.getParameterName(); - ReportParameterType param = item.getParameterValue(); - if (param == null){ - parametersMap.put(paramName, null); - continue; - } - if (param.getAny().size() == 1) { - parametersMap.put(paramName, param.getAny().get(0), param.getAny().get(0).getClass()); - } else { - parametersMap.put(paramName, param.getAny(), List.class); - } - - } - - return parametersMap; - - - } - - private ObjectListType createObjectListType(Collection resultList) { - if (resultList == null) { - return new ObjectListType(); - } - - ObjectListType results = new ObjectListType(); - int skipped = 0; - for (Object object : resultList) { - if (object instanceof PrismObject) { - results.getObject().add(((PrismObject) object).asObjectable()); - } else if (object instanceof ObjectType) { - results.getObject().add((ObjectType) object); - } else { - skipped++; - } - } - if (skipped > 0) { - LOGGER.warn("{} non-PrismObject data objects not returned, as these are not supported by ReportWebService yet", skipped); - } - - return results; - } - - private AuditEventRecordListType createAuditEventRecordListType(Collection resultList) { - if (resultList == null) { - return new AuditEventRecordListType(); - } - - AuditEventRecordListType results = new AuditEventRecordListType(); - for (AuditEventRecord auditRecord : resultList) { - results.getObject().add(auditRecord.createAuditEventRecordType(true)); - } - - return results; - } - - - @Override - public ObjectListType processReport(String reportOid, String query, RemoteReportParametersType parameters, SelectorQualifiedGetOptionsType options) { - - Task task = createTaskInstance(OP_PROCESS_REPORT); - auditLogin(task); - OperationResult operationResult = task.getResult(); - - try { - - PrismObject report = authorizeReportProcessing("processReport", reportOid, task, operationResult); - - VariablesMap parametersMap = getParamsMap(parameters); - ObjectQuery q = reportService.parseQuery(report, query, parametersMap, task, operationResult); - Collection> resultList = (Collection) reportService.searchObjects(q, - MiscSchemaUtil.optionsTypeToOptions(options, prismContext), task, operationResult); - - return createObjectListType(resultList); - } catch (SchemaException | ObjectNotFoundException | SecurityViolationException - | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { - // TODO Auto-generated catch block - throw new Fault(e); - } - - } - - private PrismObject authorizeReportProcessing(String operationName, String reportOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - if (StringUtils.isBlank(reportOid)) { - LOGGER.error("No report OID was specified during access to report service operation {}", operationName); - throw new SchemaException("No report OID specified"); - } - PrismObject report = reportService.getReportDefinition(reportOid, task, result); - if (!reportService.isAuthorizedToRunReport(report, task, result)) { - LOGGER.error("User is not authorized to run report {}, therefore access to report service operation {} was denied", report, operationName); - throw new Fault(new SecurityViolationException("Not authorized")); - } - return report; - } - -} diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebServiceRaw.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebServiceRaw.java deleted file mode 100644 index 90a2c7bbf7a..00000000000 --- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/ReportWebServiceRaw.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.report.impl; - -import java.io.PrintWriter; -import java.io.StringWriter; - -import javax.xml.namespace.QName; -import javax.xml.soap.Detail; -import javax.xml.soap.SOAPException; -import javax.xml.soap.SOAPFactory; -import javax.xml.soap.SOAPFault; -import javax.xml.transform.dom.DOMSource; -import javax.xml.ws.Holder; -import javax.xml.ws.Provider; -import javax.xml.ws.soap.SOAPFaultException; - -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.SerializationContext; -import com.evolveum.midpoint.prism.SerializationOptions; -import com.evolveum.midpoint.report.api.ReportPort; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType; -import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordListType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultMessage; -import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateAuditScriptResponseType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateAuditScriptType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateScriptResponseType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateScriptType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.ProcessReportResponseType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.ProcessReportType; - -@Service -public class ReportWebServiceRaw implements Provider { - - private static final Trace LOGGER = TraceManager.getTrace(ReportWebService.class); - - public static final String NS_SOAP11_ENV = "http://schemas.xmlsoap.org/soap/envelope/"; - public static final String NS_SOAP11_ENV_PREFIX = "SOAP-ENV"; - public static final QName SOAP11_FAULT = new QName(NS_SOAP11_ENV, "Fault"); - public static final QName SOAP11_FAULTCODE = new QName("", "faultcode"); - public static final String SOAP11_FAULTCODE_SERVER = NS_SOAP11_ENV_PREFIX + ":Server"; - public static final QName SOAP11_FAULTSTRING = new QName("", "faultstring"); - public static final QName SOAP11_FAULTACTOR = new QName("", "faultactor"); - public static final QName SOAP11_FAULT_DETAIL = new QName("", "detail"); - public static final String ACTOR = "TODO"; - - @Autowired(required = true) - private PrismContext prismContext; - - @Autowired(required = true) - private ReportWebService reportService; - - @Override - public DOMSource invoke(DOMSource request) { - try { - return invokeAllowingFaults(request); - } catch (FaultMessage faultMessage) { - try { - SOAPFactory factory = SOAPFactory.newInstance(); - SOAPFault soapFault = factory.createFault(); - soapFault.setFaultCode(SOAP11_FAULTCODE_SERVER); // todo here is a constant until we have a mechanism to determine the correct value (client / server) - soapFault.setFaultString(faultMessage.getMessage()); - Detail detail = soapFault.addDetail(); - serializeFaultMessage(detail, faultMessage); - // fault actor? - // stack trace of the outer exception (FaultMessage) is unimportant, because it is always created at one place - // todo consider providing stack trace of the inner exception - //Detail detail = soapFault.addDetail(); - //detail.setTextContent(getStackTraceAsString(faultMessage)); - throw new SOAPFaultException(soapFault); - } catch (SOAPException e) { - throw new RuntimeException("SOAP Exception: " + e.getMessage(), e); - } - } - } - - public DOMSource invokeAllowingFaults(DOMSource request) throws FaultMessage { - Node rootNode = request.getNode(); - Element rootElement; - if (rootNode instanceof Document) { - rootElement = ((Document) rootNode).getDocumentElement(); - } else if (rootNode instanceof Element) { - rootElement = (Element) rootNode; - } else { -// throw ws.createIllegalArgumentFault("Unexpected DOM node type: " + rootNode); - throw new FaultMessage("Unexpected DOM node type: " + rootNode); - } - - Object requestObject; - try { - requestObject = prismContext.parserFor(rootElement).parseRealValue(); - } catch (SchemaException e) { - throw new FaultMessage("Couldn't parse SOAP request body because of schema exception: " + e.getMessage()); -// throw ws.createIllegalArgumentFault("Couldn't parse SOAP request body because of schema exception: " + e.getMessage()); - } - - Node response; - Holder operationResultTypeHolder = new Holder<>(); - SerializationContext ctx= new SerializationContext(SerializationOptions.createSerializeReferenceNames()); - try { - if (requestObject instanceof EvaluateScriptType){ - EvaluateScriptType s = (EvaluateScriptType) requestObject; - ObjectListType olt = reportService.evaluateScript(s.getReportOid(), s.getScript(), s.getParameters()); - EvaluateScriptResponseType sr = new EvaluateScriptResponseType(); - sr.setObjectList(olt); - response = prismContext.domSerializer().context(ctx).serializeAnyData(sr, ReportPort.EVALUATE_SCRIPT_RESPONSE); - } else if (requestObject instanceof EvaluateAuditScriptType){ - EvaluateAuditScriptType s = (EvaluateAuditScriptType) requestObject; - AuditEventRecordListType olt = reportService.evaluateAuditScript(s.getReportOid(), s.getScript(), s.getParameters()); - EvaluateAuditScriptResponseType sr = new EvaluateAuditScriptResponseType(); - sr.setObjectList(olt); - response = prismContext.domSerializer().context(ctx).serializeAnyData(sr, ReportPort.EVALUATE_AUDIT_SCRIPT_RESPONSE); - } else if (requestObject instanceof ProcessReportType){ - ProcessReportType p = (ProcessReportType) requestObject; - ObjectListType olt = reportService.processReport(p.getReportOid(), p.getQuery(), p.getParameters(), p.getOptions()); - ProcessReportResponseType pr = new ProcessReportResponseType(); - pr.setObjectList(olt); - response = prismContext.domSerializer().context(ctx).serializeAnyData(pr, ReportPort.PROCESS_REPORT_RESPONSE); - } else { - throw new FaultMessage("Unsupported request type: " + requestObject); - } - } catch (SchemaException e) { - throwFault(e, operationResultTypeHolder.value); - // not reached - return null; - } - - // brutal hack for MID-2001 (serializing and parsing eliminates the problem!) - //String serialized = DOMUtil.printDom(response).toString(); - //LOGGER.trace("WEB SERVICE RESPONSE:\n{}", serialized); - //response = DOMUtil.parseDocument(serialized); - - return new DOMSource(response); - } - - private void serializeFaultMessage(Detail detail, FaultMessage faultMessage) { - prismContext.hacks() - .serializeFaultMessage(detail, faultMessage.getFaultInfo(), SchemaConstants.FAULT_MESSAGE_ELEMENT_NAME, LOGGER); - } - - private String getStackTraceAsString(FaultMessage faultMessage) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - faultMessage.printStackTrace(pw); - pw.close(); - return sw.toString(); - } - - private void throwFault(Exception ex, OperationResultType resultType) throws FaultMessage { - if (resultType != null) { - throw new FaultMessage(ex.getMessage()); -// ws.throwFault(ex, OperationResult.createOperationResult(resultType)); - } else { - throw new FaultMessage(ex.getMessage()); -// ws.throwFault(ex, null); - } - } - -} diff --git a/model/report-impl/src/main/resources/ctx-report.xml b/model/report-impl/src/main/resources/ctx-report.xml index fdd004babe2..698f46528c4 100644 --- a/model/report-impl/src/main/resources/ctx-report.xml +++ b/model/report-impl/src/main/resources/ctx-report.xml @@ -7,44 +7,15 @@ --> + default-lazy-init="false"> - - + + - - - - - - - - - - - - - - - - - - diff --git a/model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebService.java b/model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebService.java deleted file mode 100644 index b2a8ee7e7e2..00000000000 --- a/model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebService.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.report; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertNotNull; - -import org.apache.cxf.interceptor.Fault; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.ContextConfiguration; -import org.testng.annotations.Test; - -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.report.impl.ReportWebService; -import com.evolveum.midpoint.schema.SearchResultList; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ReportType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; -import com.evolveum.midpoint.xml.ns._public.report.report_3.RemoteReportParametersType; - -/** - * Basic report tests. - */ -@ContextConfiguration(locations = { "classpath:ctx-report-test-main.xml" }) -@DirtiesContext(classMode = ClassMode.AFTER_CLASS) -public class TestReportWebService extends AbstractReportIntegrationTest { - - @Autowired protected ReportWebService reportWebService; - - @Override - public void initSystem(Task initTask, OperationResult initResult) throws Exception { - super.initSystem(initTask, initResult); - - repoAddObjectFromFile(ROLE_READER_FILE, true, initResult); - repoAddObjectFromFile(USER_READER_FILE, true, initResult); - repoAddObjectFromFile(ROLE_RUNNER_FILE, true, initResult); - repoAddObjectFromFile(USER_RUNNER_FILE, true, initResult); - repoAddObjectFromFile(USER_READER_RUNNER_FILE, true, initResult); - - repoAddObjectFromFile(REPORT_USER_LIST_EXPRESSIONS_CSV_FILE, ReportType.class, initResult); - repoAddObjectFromFile(REPORT_USER_LIST_EXPRESSIONS_POISONOUS_QUERY_CSV_FILE, ReportType.class, initResult); - repoAddObjectFromFile(REPORT_USER_LIST_EXPRESSIONS_POISONOUS_FIELD_CSV_FILE, ReportType.class, initResult); - - // Let's make this more interesting by adding a couple of users - importObjectsFromFileNotRaw(USERS_MONKEY_ISLAND_FILE, initTask, initResult); - } - - @Test - public void test000Sanity() { - assertNotNull("No web service", reportWebService); - } - - @Test - public void test100ProcessReportUserList() throws Exception { - String query = createAllQueryString(UserType.class); - RemoteReportParametersType parameters = createReportParameters(); - - // WHEN - when(); - ObjectListType userList = reportWebService.processReport(REPORT_USER_LIST_EXPRESSIONS_CSV_OID, query, parameters, null); - - // THEN - then(); - displayValue("Returned user list (" + userList.getObject().size() + " objects)", userList); - - assertUserList(userList); - } - - @Test - public void test110ProcessReportUserListNoReportOid() { - String query = createAllQueryString(UserType.class); - RemoteReportParametersType parameters = createReportParameters(); - - try { - - // WHEN - when(); - reportWebService.processReport(null, query, parameters, null); - - assertNotReached(); - - } catch (Fault f) { - // THEN - then(); - displayException("Expected fault", f); - } - } - - @Test - public void test112ProcessReportUserListInvalidReportOid() { - String query = createAllQueryString(UserType.class); - RemoteReportParametersType parameters = createReportParameters(); - - try { - - // WHEN - when(); - reportWebService.processReport("l00n3y", query, parameters, null); - - assertNotReached(); - - } catch (Fault f) { - // THEN - then(); - displayException("Expected fault", f); - } - } - - /** - * MID-5463 - */ - @Test - public void test115ProcessReportUserListUnauthorizedReader() throws Exception { - login(USER_READER_USERNAME); - - String query = createAllQueryString(UserType.class); - RemoteReportParametersType parameters = createReportParameters(); - - try { - - // WHEN - when(); - reportWebService.processReport(REPORT_USER_LIST_EXPRESSIONS_CSV_OID, query, parameters, null); - - assertNotReached(); - - } catch (Fault f) { - // THEN - then(); - displayException("Expected fault", f); - } finally { - login(USER_ADMINISTRATOR_USERNAME); - } - } - - /** - * MID-5463 - */ - @Test - public void test116ProcessReportUserListUnauthorizedRunner() throws Exception { - login(USER_RUNNER_USERNAME); - - String query = createAllQueryString(UserType.class); - RemoteReportParametersType parameters = createReportParameters(); - - try { - - // WHEN - when(); - reportWebService.processReport(REPORT_USER_LIST_EXPRESSIONS_CSV_OID, query, parameters, null); - - assertNotReached(); - - } catch (Fault f) { - // THEN - then(); - displayException("Expected fault", f); - } finally { - login(USER_ADMINISTRATOR_USERNAME); - } - } - - /** - * MID-5463 - */ - @Test - public void test119ProcessReportUserListReaderRunner() throws Exception { - login(USER_READER_RUNNER_USERNAME); - - String query = createAllQueryString(UserType.class); - RemoteReportParametersType parameters = createReportParameters(); - - ObjectListType userList; - try { - - // WHEN - when(); - userList = reportWebService.processReport(REPORT_USER_LIST_EXPRESSIONS_CSV_OID, query, parameters, null); - - } finally { - login(USER_ADMINISTRATOR_USERNAME); - } - - // THEN - then(); - displayValue("Returned user list (" + userList.getObject().size() + " objects)", userList); - - assertUserList(userList); - } - - // TODO: test that violates safe profile - - private String createAllQueryString(Class type) { - return "" + type.getSimpleName() + ""; - } - - private RemoteReportParametersType createReportParameters() { - return new RemoteReportParametersType(); - } - - private void assertUserList(ObjectListType userList) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - Task task = getTestTask(); - OperationResult result = task.getResult(); - SearchResultList> currentUsers = modelService.searchObjects(UserType.class, null, null, task, result); - displayValue("Current users in midPoint (" + currentUsers.size() + " users)", currentUsers.toString()); - - assertEquals("Unexpected number of returned objects", currentUsers.size(), userList.getObject().size()); - } - -} diff --git a/model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebServiceSafe.java b/model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebServiceSafe.java deleted file mode 100644 index 0fb55a83e57..00000000000 --- a/model/report-impl/src/test/java/com/evolveum/midpoint/report/TestReportWebServiceSafe.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.report; - -import java.io.File; - -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.ContextConfiguration; - -/** - * Basic report tests. - */ -@ContextConfiguration(locations = { "classpath:ctx-report-test-main.xml" }) -@DirtiesContext(classMode = ClassMode.AFTER_CLASS) -public class TestReportWebServiceSafe extends TestReportWebService { - - @Override - protected File getSystemConfigurationFile() { - return SYSTEM_CONFIGURATION_SAFE_FILE; - } - -} diff --git a/model/report-impl/src/test/resources/ctx-report-test-main.xml b/model/report-impl/src/test/resources/ctx-report-test-main.xml index a861a824175..af931d1b9d8 100644 --- a/model/report-impl/src/test/resources/ctx-report-test-main.xml +++ b/model/report-impl/src/test/resources/ctx-report-test-main.xml @@ -8,24 +8,11 @@ --> + xsi:schemaLocation="http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" + default-lazy-init="false" default-autowire="byName"> - + @@ -38,7 +25,5 @@ - - diff --git a/model/report-impl/testng-integration.xml b/model/report-impl/testng-integration.xml index 611433639be..1710ba2421c 100644 --- a/model/report-impl/testng-integration.xml +++ b/model/report-impl/testng-integration.xml @@ -12,8 +12,6 @@ - - From ba3951930bd20d264d297381fdbf8df057e84010 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Thu, 16 Apr 2020 17:02:48 +0200 Subject: [PATCH 17/41] removed ModelWebService(Raw), related port and cleanup around --- .../midpoint/model/api/ModelPort.java | 39 -- model/model-common/pom.xml | 9 - .../FunctionExpressionEvaluatorFactory.java | 19 +- .../script/jsr223/Jsr223ScriptEvaluator.java | 58 +-- .../common/util/AbstractModelWebService.java | 83 ---- model/model-impl/pom.xml | 4 - .../midpoint/model/impl/ModelRestService.java | 190 ++++---- .../midpoint/model/impl/ModelWebService.java | 446 ------------------ .../model/impl/ModelWebServiceRaw.java | 212 --------- .../model/impl/scripting/PipelineData.java | 28 ++ .../security/MidpointPasswordValidator.java | 92 ---- .../src/main/resources/ctx-model.xml | 209 ++++---- .../intest/scripting/TestScriptingBasic.java | 16 +- pom.xml | 6 - 14 files changed, 229 insertions(+), 1182 deletions(-) delete mode 100644 model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelPort.java delete mode 100644 model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/AbstractModelWebService.java delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebServiceRaw.java delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointPasswordValidator.java diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelPort.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelPort.java deleted file mode 100644 index b9572e32ba0..00000000000 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelPort.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2010-2014 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.api; - -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ModelPortType; - -import javax.xml.namespace.QName; - -/** - * - * @author lazyman - * - */ -public interface ModelPort { - - String CLASS_NAME_WITH_DOT = ModelPortType.class.getName() + "."; - String GET_OBJECT = CLASS_NAME_WITH_DOT + "getObject"; - String SEARCH_OBJECTS = CLASS_NAME_WITH_DOT + "searchObjects"; - String EXECUTE_CHANGES = CLASS_NAME_WITH_DOT + "executeChanges"; - String LIST_ACCOUNT_SHADOW_OWNER = CLASS_NAME_WITH_DOT + "listAccountShadowOwner"; - String TEST_RESOURCE = CLASS_NAME_WITH_DOT + "testResource"; - String IMPORT_FROM_RESOURCE = CLASS_NAME_WITH_DOT + "importFromResource"; - String NOTIFY_CHANGE = CLASS_NAME_WITH_DOT + "notifyChange"; - String EXECUTE_SCRIPTS = CLASS_NAME_WITH_DOT + "executeScripts"; - - QName GET_OBJECT_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "getObjectResponse"); - QName SEARCH_OBJECTS_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "searchObjectsResponse"); - QName EXECUTE_CHANGES_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "executeChangesResponse"); - QName FIND_SHADOW_OWNER_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "findShadowOwnerResponse"); - QName TEST_RESOURCE_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "testResourceResponse"); - QName EXECUTE_SCRIPTS_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "executeScriptsResponse"); - QName IMPORT_FROM_RESOURCE_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "importFromResourceResponse"); - QName NOTIFY_CHANGE_RESPONSE = new QName(SchemaConstants.NS_MODEL_WS, "notifyChangeResponse"); -} diff --git a/model/model-common/pom.xml b/model/model-common/pom.xml index 9cdb91724fb..789f95e997f 100644 --- a/model/model-common/pom.xml +++ b/model/model-common/pom.xml @@ -70,11 +70,6 @@ model-api 4.2-SNAPSHOT - - com.evolveum.midpoint.repo - audit-api - 4.2-SNAPSHOT - com.evolveum.midpoint.repo repo-common @@ -120,10 +115,6 @@ org.apache.commons commons-configuration2 - - org.springframework.security - spring-security-core - org.springframework spring-context diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/FunctionExpressionEvaluatorFactory.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/FunctionExpressionEvaluatorFactory.java index 23095261f4e..d4373e5dc03 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/FunctionExpressionEvaluatorFactory.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/evaluator/FunctionExpressionEvaluatorFactory.java @@ -7,12 +7,9 @@ package com.evolveum.midpoint.model.common.expression.evaluator; import java.util.Collection; - import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; -import com.evolveum.midpoint.schema.cache.CacheConfigurationManager; -import com.evolveum.midpoint.task.api.Task; import org.apache.commons.lang.Validate; import com.evolveum.midpoint.prism.ItemDefinition; @@ -22,8 +19,10 @@ import com.evolveum.midpoint.repo.common.expression.AbstractObjectResolvableExpressionEvaluatorFactory; import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluator; import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; +import com.evolveum.midpoint.schema.cache.CacheConfigurationManager; import com.evolveum.midpoint.schema.expression.ExpressionProfile; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.FunctionExpressionEvaluatorType; @@ -33,7 +32,6 @@ * This is NOT autowired evaluator. * * @author semancik - * */ public class FunctionExpressionEvaluatorFactory extends AbstractObjectResolvableExpressionEvaluatorFactory { @@ -42,7 +40,8 @@ public class FunctionExpressionEvaluatorFactory extends AbstractObjectResolvable private final Protector protector; private final PrismContext prismContext; - public FunctionExpressionEvaluatorFactory(ExpressionFactory expressionFactory, Protector protector, PrismContext prismContext, + public FunctionExpressionEvaluatorFactory( + ExpressionFactory expressionFactory, Protector protector, PrismContext prismContext, CacheConfigurationManager cacheConfigurationManager) { super(expressionFactory, cacheConfigurationManager); this.protector = protector; @@ -58,18 +57,18 @@ public QName getElementName() { * @see com.evolveum.midpoint.common.expression.ExpressionEvaluatorFactory#createEvaluator(javax.xml.bind.JAXBElement, com.evolveum.midpoint.prism.PrismContext) */ @Override - public ExpressionEvaluator createEvaluator( + public ExpressionEvaluator createEvaluator( Collection> evaluatorElements, D outputDefinition, ExpressionProfile expressionProfile, ExpressionFactory factory, String contextDescription, Task task, OperationResult result) - throws SchemaException, ObjectNotFoundException { + throws SchemaException, ObjectNotFoundException { Validate.notNull(outputDefinition, "output definition must be specified for 'generate' expression evaluator"); if (evaluatorElements.size() > 1) { - throw new SchemaException("More than one evaluator specified in "+contextDescription); + throw new SchemaException("More than one evaluator specified in " + contextDescription); } JAXBElement evaluatorElement = evaluatorElements.iterator().next(); @@ -78,10 +77,10 @@ public ExpressionEvaluator evaluatorTypeObject = evaluatorElement.getValue(); } if (evaluatorTypeObject != null && !(evaluatorTypeObject instanceof FunctionExpressionEvaluatorType)) { - throw new SchemaException("Function expression evaluator cannot handle elements of type " + evaluatorTypeObject.getClass().getName()+" in "+contextDescription); + throw new SchemaException("Function expression evaluator cannot handle elements of type " + evaluatorTypeObject.getClass().getName() + " in " + contextDescription); } - FunctionExpressionEvaluatorType functionEvaluatorType = (FunctionExpressionEvaluatorType)evaluatorTypeObject; + FunctionExpressionEvaluatorType functionEvaluatorType = (FunctionExpressionEvaluatorType) evaluatorTypeObject; return new FunctionExpressionEvaluator(ELEMENT_NAME, functionEvaluatorType, outputDefinition, protector, getObjectResolver(), prismContext); } diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/jsr223/Jsr223ScriptEvaluator.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/jsr223/Jsr223ScriptEvaluator.java index abec1cc65ce..eb647f1cd92 100644 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/jsr223/Jsr223ScriptEvaluator.java +++ b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/expression/script/jsr223/Jsr223ScriptEvaluator.java @@ -6,79 +6,43 @@ */ package com.evolveum.midpoint.model.common.expression.script.jsr223; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; - -import javax.script.Bindings; -import javax.script.Compilable; -import javax.script.CompiledScript; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import javax.xml.namespace.QName; +import javax.script.*; import com.evolveum.midpoint.common.LocalizationService; -import com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary; import com.evolveum.midpoint.model.common.expression.script.AbstractCachingScriptEvaluator; -import com.evolveum.midpoint.model.common.expression.script.ScriptEvaluator; import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext; -import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.crypto.Protector; -import com.evolveum.midpoint.prism.xml.XsdTypeMapper; -import com.evolveum.midpoint.repo.common.ObjectResolver; import com.evolveum.midpoint.repo.common.expression.ExpressionSyntaxException; -import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; -import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; import com.evolveum.midpoint.schema.constants.MidPointConstants; -import com.evolveum.midpoint.schema.internals.InternalCounters; -import com.evolveum.midpoint.schema.internals.InternalMonitor; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.ExceptionUtil; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.util.exception.SystemException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExpressionEvaluatorType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExpressionReturnTypeType; +import com.evolveum.midpoint.util.exception.*; /** * Expression evaluator that is using javax.script (JSR-223) engine. - * + *

* This evaluator does not really support expression profiles. It has just one * global almighty compiler (ScriptEngine). * * @author Radovan Semancik - * */ -public class Jsr223ScriptEvaluator extends AbstractCachingScriptEvaluator { - - private static final Trace LOGGER = TraceManager.getTrace(Jsr223ScriptEvaluator.class); +public class Jsr223ScriptEvaluator extends AbstractCachingScriptEvaluator { private final ScriptEngine scriptEngine; - public Jsr223ScriptEvaluator(String engineName, PrismContext prismContext, Protector protector, - LocalizationService localizationService) { + public Jsr223ScriptEvaluator(String engineName, PrismContext prismContext, + Protector protector, LocalizationService localizationService) { super(prismContext, protector, localizationService); ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); scriptEngine = scriptEngineManager.getEngineByName(engineName); if (scriptEngine == null) { - throw new SystemException("The JSR-223 scripting engine for '"+engineName+"' was not found"); + throw new SystemException("The JSR-223 scripting engine for '" + engineName + "' was not found"); } } @Override protected CompiledScript compileScript(String codeString, ScriptExpressionEvaluationContext context) throws Exception { - return ((Compilable)scriptEngine).compile(codeString); + return ((Compilable) scriptEngine).compile(codeString); } @Override @@ -89,13 +53,12 @@ protected Object evaluateScript(CompiledScript compiledScript, ScriptExpressionE } private Bindings convertToBindings(ScriptExpressionEvaluationContext context) - throws ExpressionSyntaxException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + throws ExpressionSyntaxException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { Bindings bindings = scriptEngine.createBindings(); bindings.putAll(prepareScriptVariablesValueMap(context)); return bindings; } - // public Object evaluateReportScript(String codeString, ScriptExpressionEvaluationContext context) throws ExpressionEvaluationException, // ObjectNotFoundException, ExpressionSyntaxException, CommunicationException, ConfigurationException, SecurityViolationException { // @@ -126,7 +89,6 @@ private Bindings convertToBindings(ScriptExpressionEvaluationContext context) // return evalRawResult; // } - /* (non-Javadoc) * @see com.evolveum.midpoint.common.expression.ExpressionEvaluator#getLanguageName() */ diff --git a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/AbstractModelWebService.java b/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/AbstractModelWebService.java deleted file mode 100644 index f9b37f0374a..00000000000 --- a/model/model-common/src/main/java/com/evolveum/midpoint/model/common/util/AbstractModelWebService.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2014-2017 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.common.util; - -import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; - -import com.evolveum.midpoint.audit.api.AuditEventRecord; -import com.evolveum.midpoint.audit.api.AuditEventStage; -import com.evolveum.midpoint.audit.api.AuditEventType; -import com.evolveum.midpoint.audit.api.AuditService; -import com.evolveum.midpoint.model.api.ModelService; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.security.api.MidPointPrincipal; -import com.evolveum.midpoint.security.api.SecurityContextManager; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.task.api.TaskManager; -import com.evolveum.midpoint.util.exception.SystemException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; - -/** - * Abstract superclass that provides methods common to all web service implementations that - * use ModelService. - * - * @author Radovan Semancik - * - */ -public abstract class AbstractModelWebService { - - @Autowired protected ModelService modelService; - @Autowired protected TaskManager taskManager; - @Autowired protected AuditService auditService; - @Autowired protected PrismContext prismContext; - @Autowired protected SecurityContextManager securityContextManager; - - protected void setTaskOwner(Task task) { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null) { - throw new SystemException("Failed to get authentication object"); - } - FocusType focusType = ((MidPointPrincipal)(SecurityContextHolder.getContext().getAuthentication().getPrincipal())).getFocus(); - if (focusType == null) { - throw new SystemException("Failed to get user from authentication object"); - } - task.setOwner(focusType.asPrismObject()); - } - - protected Task createTaskInstance(String operationName) { - // TODO: better task initialization - Task task = taskManager.createTaskInstance(operationName); - setTaskOwner(task); - task.setChannel(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - return task; - } - - protected void auditLogin(Task task) { - AuditEventRecord record = new AuditEventRecord(AuditEventType.CREATE_SESSION, AuditEventStage.REQUEST); - record.setInitiatorAndLoginParameter(task.getOwner()); - record.setChannel(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - record.setTimestamp(System.currentTimeMillis()); - record.setOutcome(OperationResultStatus.SUCCESS); - auditService.audit(record, task); - } - - protected void auditLogout(Task task) { - AuditEventRecord record = new AuditEventRecord(AuditEventType.TERMINATE_SESSION, AuditEventStage.REQUEST); - record.setInitiatorAndLoginParameter(task.getOwner()); - record.setChannel(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - record.setTimestamp(System.currentTimeMillis()); - record.setOutcome(OperationResultStatus.SUCCESS); - auditService.audit(record, task); - } - -} diff --git a/model/model-impl/pom.xml b/model/model-impl/pom.xml index a9033f1a2de..894d4b7d0c9 100644 --- a/model/model-impl/pom.xml +++ b/model/model-impl/pom.xml @@ -265,10 +265,6 @@ spring-boot-starter-amqp runtime - - javax.xml.ws - jaxws-api - diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java index f8ed5b8c5df..e8a39025a32 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelRestService.java @@ -6,31 +6,51 @@ */ package com.evolveum.midpoint.model.impl; +import java.net.URI; +import java.util.Collection; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.*; +import javax.ws.rs.core.*; +import javax.ws.rs.core.Response.ResponseBuilder; +import javax.ws.rs.core.Response.Status; +import javax.xml.namespace.QName; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.Validate; +import org.apache.cxf.jaxrs.ext.MessageContext; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + import com.evolveum.midpoint.audit.api.AuditEventRecord; import com.evolveum.midpoint.audit.api.AuditEventStage; import com.evolveum.midpoint.audit.api.AuditEventType; import com.evolveum.midpoint.audit.api.AuditService; +import com.evolveum.midpoint.common.rest.Converter; +import com.evolveum.midpoint.common.rest.ConverterInterface; import com.evolveum.midpoint.model.api.*; import com.evolveum.midpoint.model.api.validator.ResourceValidator; import com.evolveum.midpoint.model.api.validator.Scope; import com.evolveum.midpoint.model.api.validator.ValidationResult; import com.evolveum.midpoint.model.common.SystemObjectCache; -import com.evolveum.midpoint.model.common.stringpolicy.ValuePolicyProcessor; -import com.evolveum.midpoint.common.rest.Converter; -import com.evolveum.midpoint.common.rest.ConverterInterface; import com.evolveum.midpoint.model.impl.rest.PATCH; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; import com.evolveum.midpoint.model.impl.security.SecurityHelper; import com.evolveum.midpoint.model.impl.util.RestServiceUtil; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemPathCollectionsUtil; import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.repo.api.CacheDispatcher; -import com.evolveum.midpoint.schema.*; +import com.evolveum.midpoint.schema.DefinitionProcessingOption; +import com.evolveum.midpoint.schema.DeltaConvertor; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.constants.MidPointConstants; import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.constants.SchemaConstants; @@ -48,36 +68,15 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ExecuteScriptOutputType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ExecuteScriptType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.*; import com.evolveum.prism.xml.ns._public.query_3.QueryType; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.Validate; -import org.apache.cxf.jaxrs.ext.MessageContext; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.*; -import javax.ws.rs.core.*; -import javax.ws.rs.core.Response.ResponseBuilder; -import javax.ws.rs.core.Response.Status; -import javax.xml.namespace.QName; -import java.net.URI; -import java.util.Collection; -import java.util.List; - /** * @author katkav * @author semancik */ @Service -@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) +@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public class ModelRestService { public static final String CLASS_DOT = ModelRestService.class.getName() + "."; @@ -102,10 +101,10 @@ public class ModelRestService { public static final String OPERATION_COMPARE = CLASS_DOT + "compare"; public static final String OPERATION_GET_LOG_FILE_CONTENT = CLASS_DOT + "getLogFileContent"; public static final String OPERATION_GET_LOG_FILE_SIZE = CLASS_DOT + "getLogFileSize"; - public static final String OPERATION_VALIDATE_VALUE = CLASS_DOT + "validateValue"; - public static final String OPERATION_VALIDATE_VALUE_RPC = CLASS_DOT + "validateValueRpc"; - public static final String OPERATION_GENERATE_VALUE = CLASS_DOT + "generateValue"; - public static final String OPERATION_GENERATE_VALUE_RPC = CLASS_DOT + "generateValueRpc"; + public static final String OPERATION_VALIDATE_VALUE = CLASS_DOT + "validateValue"; + public static final String OPERATION_VALIDATE_VALUE_RPC = CLASS_DOT + "validateValueRpc"; + public static final String OPERATION_GENERATE_VALUE = CLASS_DOT + "generateValue"; + public static final String OPERATION_GENERATE_VALUE_RPC = CLASS_DOT + "generateValueRpc"; public static final String OPERATION_EXECUTE_CREDENTIAL_RESET = CLASS_DOT + "executeCredentialReset"; public static final String OPERATION_EXECUTE_CLUSTER_EVENT = CLASS_DOT + "executeClusterCacheInvalidationEvent"; public static final String OPERATION_GET_LOCAL_SCHEDULER_INFORMATION = CLASS_DOT + "getLocalSchedulerInformation"; @@ -126,14 +125,10 @@ public class ModelRestService { @Autowired private ModelInteractionService modelInteraction; @Autowired private PrismContext prismContext; @Autowired private SecurityHelper securityHelper; - @Autowired private ValuePolicyProcessor policyProcessor; @Autowired private TaskManager taskManager; @Autowired private TaskService taskService; - @Autowired private Protector protector; @Autowired private ResourceValidator resourceValidator; - @Autowired private CacheDispatcher cacheDispatcher; - @Autowired private SystemObjectCache systemObjectCache; @Autowired private AuditService auditService; @@ -147,8 +142,8 @@ public ModelRestService() { @POST @Path("/{type}/{oid}/generate") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response generateValue(@PathParam("type") String type, @PathParam("oid") String oid, PolicyItemsDefinitionType policyItemsDefinition, @Context MessageContext mc) { @@ -174,8 +169,8 @@ public Response generateValue(@PathParam("type") String type, @POST @Path("/rpc/generate") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response generateValue(PolicyItemsDefinitionType policyItemsDefinition, @Context MessageContext mc) { @@ -188,7 +183,7 @@ public Response generateValue(PolicyItemsDefinitionType policyItemsDefinition, return response; } - private Response generateValue(PrismObject object, PolicyItemsDefinitionType policyItemsDefinition, Task task, OperationResult parentResult){ + private Response generateValue(PrismObject object, PolicyItemsDefinitionType policyItemsDefinition, Task task, OperationResult parentResult) { Response response; if (policyItemsDefinition == null) { response = createBadPolicyItemsDefinitionResponse("Policy items definition must not be null", parentResult); @@ -212,8 +207,8 @@ private Response generateValue(PrismObject object, Pol @POST @Path("/{type}/{oid}/validate") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response validateValue(@PathParam("type") String type, @PathParam("oid") String oid, PolicyItemsDefinitionType policyItemsDefinition, @Context MessageContext mc) { Task task = initRequest(mc); @@ -235,8 +230,8 @@ public Response validateValue(@PathParam("type") String type, @PathParam("oid") @POST @Path("/rpc/validate") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response validateValue(PolicyItemsDefinitionType policyItemsDefinition, @Context MessageContext mc) { Task task = initRequest(mc); @@ -263,24 +258,22 @@ private Response validateValue(PrismObject object, Htt return response; } + try { + modelInteraction.validateValue(object, policyItemsDefinition, task, parentResult); - try { - modelInteraction.validateValue(object, policyItemsDefinition, task, parentResult); - - parentResult.computeStatusIfUnknown(); - ResponseBuilder responseBuilder; - if (parentResult.isAcceptable()) { - response = RestServiceUtil.createResponse(Response.Status.OK, policyItemsDefinition, parentResult, true); - } else { - responseBuilder = Response.status(Status.CONFLICT).entity(parentResult); - response = responseBuilder.build(); - } - - } catch (Exception ex) { - parentResult.computeStatus(); - response = RestServiceUtil.handleException(parentResult, ex); + parentResult.computeStatusIfUnknown(); + ResponseBuilder responseBuilder; + if (parentResult.isAcceptable()) { + response = RestServiceUtil.createResponse(Response.Status.OK, policyItemsDefinition, parentResult, true); + } else { + responseBuilder = Response.status(Status.CONFLICT).entity(parentResult); + response = responseBuilder.build(); } + } catch (Exception ex) { + parentResult.computeStatus(); + response = RestServiceUtil.handleException(parentResult, ex); + } return response; } @@ -323,13 +316,13 @@ public Response getValuePolicyForUser(@PathParam("id") String oid, @Context Mess @GET @Path("/{type}/{id}") - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response getObject(@PathParam("type") String type, @PathParam("id") String id, @QueryParam("options") List options, @QueryParam("include") List include, @QueryParam("exclude") List exclude, @QueryParam("resolveNames") List resolveNames, - @Context MessageContext mc){ + @Context MessageContext mc) { LOGGER.debug("model rest service for get operation start"); Task task = initRequest(mc); @@ -347,7 +340,7 @@ public Response getObject(@PathParam("type") String type, @PathParam("id") Strin ObjectQuery query = prismContext.queryFor(NodeType.class) .item(NodeType.F_NODE_IDENTIFIER).eq(nodeId) .build(); - List> objects = model.searchObjects(NodeType.class, query, getOptions, task, parentResult); + List> objects = model.searchObjects(NodeType.class, query, getOptions, task, parentResult); if (objects.isEmpty()) { throw new ObjectNotFoundException("Current node (id " + nodeId + ") couldn't be found."); } else if (objects.size() > 1) { @@ -372,8 +365,8 @@ public Response getObject(@PathParam("type") String type, @PathParam("id") Strin @GET @Path("/self") - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - public Response getSelf(@Context MessageContext mc){ + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + public Response getSelf(@Context MessageContext mc) { LOGGER.debug("model rest service for get operation start"); Task task = initRequest(mc); @@ -394,12 +387,11 @@ public Response getSelf(@Context MessageContext mc){ return response; } - @POST @Path("/{type}") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response addObject(@PathParam("type") String type, PrismObject object, - @QueryParam("options") List options, + @QueryParam("options") List options, @Context UriInfo uriInfo, @Context MessageContext mc) { LOGGER.debug("model rest service for add operation start"); @@ -407,7 +399,7 @@ public Response addObject(@PathParam("type") String type, OperationResult parentResult = task.getResult().createSubresult(OPERATION_ADD_OBJECT); Class clazz = ObjectTypes.getClassFromRestType(type); - if (!object.getCompileTimeClass().equals(clazz)){ + if (!object.getCompileTimeClass().equals(clazz)) { finishRequest(task, mc.getHttpServletRequest()); parentResult.recordFatalError("Request to add object of type " + object.getCompileTimeClass().getSimpleName() + " to the collection of " + type); @@ -420,7 +412,7 @@ public Response addObject(@PathParam("type") String type, Response response; try { oid = model.addObject(object, modelExecuteOptions, task, parentResult); - LOGGER.debug("returned oid : {}", oid ); + LOGGER.debug("returned oid : {}", oid); if (oid != null) { URI resourceURI = uriInfo.getAbsolutePathBuilder().path(oid).build(oid); @@ -441,7 +433,7 @@ public Response addObject(@PathParam("type") String type, @GET @Path("/{type}") - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response searchObjectsByType(@PathParam("type") String type, @QueryParam("options") List options, @QueryParam("include") List include, @QueryParam("exclude") List exclude, @QueryParam("resolveNames") List resolveNames, @@ -487,10 +479,10 @@ private void validateIfRequested(PrismObject object, @PUT @Path("/{type}/{id}") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response addObject(@PathParam("type") String type, @PathParam("id") String id, PrismObject object, @QueryParam("options") List options, @Context UriInfo uriInfo, - @Context Request request, @Context MessageContext mc){ + @Context Request request, @Context MessageContext mc) { LOGGER.debug("model rest service for add operation start"); @@ -498,7 +490,7 @@ public Response addObject(@PathParam("type") String type, OperationResult parentResult = task.getResult().createSubresult(OPERATION_ADD_OBJECT); Class clazz = ObjectTypes.getClassFromRestType(type); - if (!object.getCompileTimeClass().equals(clazz)){ + if (!object.getCompileTimeClass().equals(clazz)) { finishRequest(task, mc.getHttpServletRequest()); parentResult.recordFatalError("Request to add object of type " + object.getCompileTimeClass().getSimpleName() @@ -509,7 +501,7 @@ public Response addObject(@PathParam("type") String type, ModelExecuteOptions modelExecuteOptions = ModelExecuteOptions.fromRestOptions(options); if (modelExecuteOptions == null) { modelExecuteOptions = ModelExecuteOptions.createOverwrite(); - } else if (!ModelExecuteOptions.isOverwrite(modelExecuteOptions)){ + } else if (!ModelExecuteOptions.isOverwrite(modelExecuteOptions)) { modelExecuteOptions.setOverwrite(Boolean.TRUE); } @@ -534,7 +526,7 @@ public Response addObject(@PathParam("type") String type, @DELETE @Path("/{type}/{id}") public Response deleteObject(@PathParam("type") String type, @PathParam("id") String id, - @QueryParam("options") List options, @Context MessageContext mc){ + @QueryParam("options") List options, @Context MessageContext mc) { LOGGER.debug("model rest service for delete operation start"); @@ -569,7 +561,7 @@ public Response deleteObject(@PathParam("type") String type, @PathParam("id") St @POST @Path("/{type}/{oid}") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response modifyObjectPost(@PathParam("type") String type, @PathParam("oid") String oid, ObjectModificationType modificationType, @QueryParam("options") List options, @Context MessageContext mc) { return modifyObjectPatch(type, oid, modificationType, options, mc); @@ -577,7 +569,7 @@ public Response modifyObjectPost(@PathParam("type") String type, @PathParam("oid @PATCH @Path("/{type}/{oid}") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response modifyObjectPatch(@PathParam("type") String type, @PathParam("oid") String oid, ObjectModificationType modificationType, @QueryParam("options") List options, @Context MessageContext mc) { @@ -605,7 +597,7 @@ public Response modifyObjectPatch(@PathParam("type") String type, @PathParam("oi @POST @Path("/notifyChange") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response notifyChange(ResourceObjectShadowChangeDescriptionType changeDescription, @Context UriInfo uriInfo, @Context MessageContext mc) { LOGGER.debug("model rest service for notify change operation start"); @@ -629,8 +621,8 @@ public Response notifyChange(ResourceObjectShadowChangeDescriptionType changeDes @GET @Path("/shadows/{oid}/owner") - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - public Response findShadowOwner(@PathParam("oid") String shadowOid, @Context MessageContext mc){ + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + public Response findShadowOwner(@PathParam("oid") String shadowOid, @Context MessageContext mc) { Task task = initRequest(mc); OperationResult parentResult = task.getResult().createSubresult(OPERATION_FIND_SHADOW_OWNER); @@ -650,7 +642,7 @@ public Response findShadowOwner(@PathParam("oid") String shadowOid, @Context Mes @POST @Path("/shadows/{oid}/import") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response importShadow(@PathParam("oid") String shadowOid, @Context MessageContext mc, @Context UriInfo uriInfo) { LOGGER.debug("model rest service for import shadow from resource operation start"); @@ -673,8 +665,8 @@ public Response importShadow(@PathParam("oid") String shadowOid, @Context Messag @POST @Path("/{type}/search") - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response searchObjects(@PathParam("type") String type, QueryType queryType, @QueryParam("options") List options, @QueryParam("include") List include, @@ -716,7 +708,7 @@ private void removeExcludes(PrismObject object, List Response compare(PrismObject clientObject, @QueryParam("readOptions") List restReadOptions, @QueryParam("compareOptions") List restCompareOptions, @@ -904,7 +895,7 @@ public Response compare(PrismObject clientObject, @GET @Path("/log/size") - @Produces({"text/plain"}) + @Produces({ "text/plain" }) public Response getLogFileSize(@Context MessageContext mc) { Task task = initRequest(mc); @@ -926,7 +917,7 @@ public Response getLogFileSize(@Context MessageContext mc) { @GET @Path("/log") - @Produces({"text/plain"}) + @Produces({ "text/plain" }) public Response getLog(@QueryParam("fromPosition") Long fromPosition, @QueryParam("maxSize") Long maxSize, @Context MessageContext mc) { Task task = initRequest(mc); @@ -956,8 +947,8 @@ public Response getLog(@QueryParam("fromPosition") Long fromPosition, @QueryPara @POST @Path("/users/{oid}/credential") - @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) - @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML}) + @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, RestServiceUtil.APPLICATION_YAML }) public Response executeCredentialReset(@PathParam("oid") String oid, ExecuteCredentialResetRequestType executeCredentialResetRequest, @Context MessageContext mc) { Task task = initRequest(mc); OperationResult result = task.getResult().createSubresult(OPERATION_EXECUTE_CREDENTIAL_RESET); @@ -976,12 +967,11 @@ public Response executeCredentialReset(@PathParam("oid") String oid, ExecuteCred finishRequest(task, mc.getHttpServletRequest()); return response; - } @GET @Path("/threads") - @Produces({"text/plain"}) + @Produces({ "text/plain" }) public Response getThreadsDump(@Context MessageContext mc) { Task task = initRequest(mc); @@ -1002,7 +992,7 @@ public Response getThreadsDump(@Context MessageContext mc) { @GET @Path("/tasks/threads") - @Produces({"text/plain"}) + @Produces({ "text/plain" }) public Response getRunningTasksThreadsDump(@Context MessageContext mc) { Task task = initRequest(mc); @@ -1023,7 +1013,7 @@ public Response getRunningTasksThreadsDump(@Context MessageContext mc) { @GET @Path("/tasks/{oid}/threads") - @Produces({"text/plain"}) + @Produces({ "text/plain" }) public Response getTaskThreadsDump(@PathParam("oid") String oid, @Context MessageContext mc) { Task task = initRequest(mc); OperationResult result = task.getResult().createSubresult(OPERATION_GET_TASK_THREADS_DUMP); @@ -1075,10 +1065,10 @@ public void auditEvent(HttpServletRequest request) { String name = null; if (principal instanceof MidPointPrincipal) { name = ((MidPointPrincipal) principal).getUsername(); - } else if (principal != null){ + } else if (principal != null) { return; } - PrismObject user = principal!= null ? ((MidPointPrincipal)principal).getFocus().asPrismObject() : null; + PrismObject user = principal != null ? ((MidPointPrincipal) principal).getFocus().asPrismObject() : null; Task task = taskManager.createTaskInstance(); task.setOwner(user); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java deleted file mode 100644 index cecb25e7720..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebService.java +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright (c) 2010-2017 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl; - -import com.evolveum.midpoint.model.api.*; -import com.evolveum.midpoint.model.common.util.AbstractModelWebService; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismProperty; -import com.evolveum.midpoint.prism.PrismReferenceValue; -import com.evolveum.midpoint.prism.PrismValue; -import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.schema.DeltaConvertor; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.ObjectDeltaOperation; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.schema.constants.ObjectTypes; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.MiscSchemaUtil; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.util.logging.LoggingUtils; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.*; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.*; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ExecuteScriptsResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ExecuteScriptsType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ModelPortType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.PipelineDataType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.PipelineItemType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionEvaluationOptionsType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionType; -import com.evolveum.prism.xml.ns._public.query_3.QueryType; -import org.apache.commons.lang.StringUtils; -import org.apache.cxf.interceptor.Fault; -import org.apache.wss4j.common.ext.WSSecurityException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.namespace.QName; -import javax.xml.ws.Holder; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * - * @author lazyman - * - */ -@Service -public class ModelWebService extends AbstractModelWebService implements ModelPortType, ModelPort { - - private static final Trace LOGGER = TraceManager.getTrace(ModelWebService.class); - - @Autowired(required = true) - private ModelCrudService model; - - @Autowired - private ScriptingService scriptingService; - - @Override - public void getObject(QName objectType, String oid, SelectorQualifiedGetOptionsType optionsType, - Holder objectHolder, Holder resultHolder) throws FaultMessage { - notNullArgument(objectType, "Object type must not be null."); - notEmptyArgument(oid, "Oid must not be null or empty."); - - Task task = createTaskInstance(GET_OBJECT); - auditLogin(task); - OperationResult operationResult = task.getResult(); - try { - Class objectClass = ObjectTypes.getObjectTypeFromTypeQName(objectType).getClassDefinition(); - Collection> options = MiscSchemaUtil.optionsTypeToOptions(optionsType, - prismContext); - PrismObject object = model.getObject(objectClass, oid, options, task, operationResult); - handleOperationResult(operationResult, resultHolder); - objectHolder.value = object.asObjectable(); - return; - } catch (Exception ex) { - LoggingUtils.logException(LOGGER, "# MODEL getObject() failed", ex); - throwFault(ex, operationResult); - } finally { - auditLogout(task); - } - } - - @Override - public void searchObjects(QName objectType, QueryType query, SelectorQualifiedGetOptionsType optionsType, - Holder objectListHolder, Holder result) throws FaultMessage { - notNullArgument(objectType, "Object type must not be null."); - - Task task = createTaskInstance(SEARCH_OBJECTS); - auditLogin(task); - OperationResult operationResult = task.getResult(); - try { - Class objectClass = ObjectTypes.getObjectTypeFromTypeQName(objectType).getClassDefinition(); - Collection> options = MiscSchemaUtil.optionsTypeToOptions(optionsType, - prismContext); - ObjectQuery q = prismContext.getQueryConverter().createObjectQuery(objectClass, query); - List> list = (List)model.searchObjects(objectClass, q, options, task, operationResult); - handleOperationResult(operationResult, result); - ObjectListType listType = new ObjectListType(); - for (PrismObject o : list) { - listType.getObject().add(o.asObjectable()); - } - objectListHolder.value = listType; - } catch (Exception ex) { - LoggingUtils.logException(LOGGER, "# MODEL searchObjects() failed", ex); - throwFault(ex, operationResult); - } finally { - auditLogout(task); - } - } - - @Override - public ObjectDeltaOperationListType executeChanges(ObjectDeltaListType deltaList, ModelExecuteOptionsType optionsType) throws FaultMessage { - notNullArgument(deltaList, "Object delta list must not be null."); - - Task task = createTaskInstance(EXECUTE_CHANGES); - auditLogin(task); - OperationResult operationResult = task.getResult(); - try { - Collection deltas = DeltaConvertor.createObjectDeltas(deltaList, prismContext); - for (ObjectDelta delta : deltas) { - prismContext.adopt(delta); - } - ModelExecuteOptions options = ModelExecuteOptions.fromModelExecutionOptionsType(optionsType); - Collection> objectDeltaOperations = modelService.executeChanges((Collection) deltas, options, task, operationResult); // brutally eliminating type-safety compiler barking - ObjectDeltaOperationListType retval = new ObjectDeltaOperationListType(); - for (ObjectDeltaOperation objectDeltaOperation : objectDeltaOperations) { - ObjectDeltaOperationType objectDeltaOperationType = DeltaConvertor.toObjectDeltaOperationType(objectDeltaOperation, null); - retval.getDeltaOperation().add(objectDeltaOperationType); - } - return retval; - } catch (Exception ex) { - LoggingUtils.logException(LOGGER, "# MODEL executeChanges() failed", ex); - throwFault(ex, operationResult); - // notreached - return null; - } finally { - auditLogout(task); - } - } - - @Override - public void findShadowOwner(String accountOid, Holder userHolder, Holder result) - throws FaultMessage { - notEmptyArgument(accountOid, "Account oid must not be null or empty."); - - Task task = createTaskInstance(LIST_ACCOUNT_SHADOW_OWNER); - auditLogin(task); - OperationResult operationResult = task.getResult(); - try { - PrismObject user = modelService.searchShadowOwner(accountOid, null, task, operationResult); - handleOperationResult(operationResult, result); - if (user != null && user.asObjectable() instanceof UserType) { - // The schema for findShadowOwner SOAP call requires the returned object is of UserType. - // SOAP interface will be removed anyway, so let's not try to resolve this more intelligently. - userHolder.value = (UserType) user.asObjectable(); - } - return; - } catch (Exception ex) { - LoggingUtils.logException(LOGGER, "# MODEL findShadowOwner() failed", ex); - throwFault(ex, operationResult); - } finally { - auditLogout(task); - } - } - - @Override - public OperationResultType testResource(String resourceOid) throws FaultMessage { - notEmptyArgument(resourceOid, "Resource oid must not be null or empty."); - - Task task = createTaskInstance(TEST_RESOURCE); - auditLogin(task); - try { - OperationResult testResult = modelService.testResource(resourceOid, task); - return handleOperationResult(testResult); - } catch (Exception ex) { - LoggingUtils.logException(LOGGER, "# MODEL testResource() failed", ex); - OperationResult faultResult = new OperationResult(TEST_RESOURCE); - faultResult.recordFatalError(ex); - throwFault(ex, faultResult); - // notreached - return null; - } finally { - auditLogout(task); - } - } - - @Override - public ExecuteScriptsResponseType executeScripts(ExecuteScriptsType parameters) throws FaultMessage { - Task task = createTaskInstance(EXECUTE_SCRIPTS); - auditLogin(task); - OperationResult result = task.getResult(); - try { - List> scriptsToExecute = parseScripts(parameters); - return doExecuteScripts(scriptsToExecute, parameters.getOptions(), task, result); - } catch (Exception ex) { - LoggingUtils.logException(LOGGER, "# MODEL executeScripts() failed", ex); - throwFault(ex, null); - // notreached - return null; - } finally { - auditLogout(task); - } - } - - private List> parseScripts(ExecuteScriptsType parameters) throws JAXBException, SchemaException { - List> scriptsToExecute = new ArrayList<>(); - if (parameters.getXmlScripts() != null) { - for (Object scriptAsObject : parameters.getXmlScripts().getAny()) { - if (scriptAsObject instanceof JAXBElement) { - scriptsToExecute.add((JAXBElement) scriptAsObject); - } else { - throw new IllegalArgumentException("Invalid script type: " + scriptAsObject.getClass()); - } - } - } else { - // here comes MSL script decoding (however with a quick hack to allow passing XML as text here) - String scriptsAsString = parameters.getMslScripts(); - if (scriptsAsString.startsWith(" expressionType = (PrismProperty) prismContext.parserFor(scriptsAsString).xml().parseItem(); - if (expressionType.size() != 1) { - throw new IllegalArgumentException("Unexpected number of scripting expressions at input: " + expressionType.size() + " (expected 1)"); - } - scriptsToExecute.add(expressionType.getAnyValue().toJaxbElement()); - } - } - return scriptsToExecute; - } - - private ExecuteScriptsResponseType doExecuteScripts(List> scriptsToExecute, ExecuteScriptsOptionsType options, Task task, OperationResult result) { - ExecuteScriptsResponseType response = new ExecuteScriptsResponseType(); - ScriptOutputsType outputs = new ScriptOutputsType(); - response.setOutputs(outputs); - - try { - for (JAXBElement script : scriptsToExecute) { - - Object scriptValue = script.getValue(); - if (!(scriptValue instanceof ScriptingExpressionType)) { - throw new SchemaException("Expected that scripts will be of type ScriptingExpressionType, but it was "+scriptValue.getClass().getName()); - } - - ScriptExecutionResult executionResult = scriptingService.evaluateExpression((ScriptingExpressionType) script.getValue(), task, result); - - SingleScriptOutputType output = new SingleScriptOutputType(); - outputs.getOutput().add(output); - - output.setTextOutput(executionResult.getConsoleOutput()); - if (options == null || options.getOutputFormat() == null || options.getOutputFormat() == OutputFormatType.XML) { - output.setDataOutput(prepareXmlData(executionResult.getDataOutput(), null)); - } else { - // temporarily we send serialized XML in the case of MSL output - PipelineDataType jaxbOutput = prepareXmlData(executionResult.getDataOutput(), null); - output.setMslData(prismContext.xmlSerializer().serializeAnyData(jaxbOutput, SchemaConstants.C_VALUE)); - } - } - result.computeStatusIfUnknown(); - } catch (ScriptExecutionException | JAXBException | SchemaException | RuntimeException | SecurityViolationException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | ConfigurationException e) { - result.recordFatalError(e.getMessage(), e); - LoggingUtils.logException(LOGGER, "Exception while executing script", e); - } - result.summarize(); - response.setResult(result.createOperationResultType()); - return response; - } - - public static PipelineDataType prepareXmlData(List output, - ScriptingExpressionEvaluationOptionsType options) throws JAXBException, SchemaException { - boolean hideResults = options != null && Boolean.TRUE.equals(options.isHideOperationResults()); - PipelineDataType rv = new PipelineDataType(); - if (output != null) { - for (PipelineItem item: output) { - PipelineItemType itemType = new PipelineItemType(); - PrismValue value = item.getValue(); - if (value instanceof PrismReferenceValue) { - // This is a bit of hack: value.getRealValue() would return unserializable object (PRV$1 - does not have type QName) - ObjectReferenceType ort = new ObjectReferenceType(); - ort.setupReferenceValue((PrismReferenceValue) value); - itemType.setValue(ort); - } else { - itemType.setValue(value.getRealValue()); // TODO - ok? - } - if (!hideResults) { - itemType.setResult(item.getResult().createOperationResultType()); - } - rv.getItem().add(itemType); - } - } - return rv; - } - - private void handleOperationResult(OperationResult result, Holder holder) { - result.recordSuccess(); - OperationResultType resultType = result.createOperationResultType(); - if (holder.value == null) { - holder.value = resultType; - } else { - holder.value.getPartialResults().add(resultType); - } - } - - private OperationResultType handleOperationResult(OperationResult result) { - result.recordSuccess(); - return result.createOperationResultType(); - } - - private void notNullResultHolder(Holder holder) throws FaultMessage { - notNullArgument(holder, "Holder must not be null."); - notNullArgument(holder.value, "Result type must not be null."); - } - - private void notNullHolder(Holder holder) throws FaultMessage { - notNullArgument(holder, "Holder must not be null."); - notNullArgument(holder.value, holder.getClass().getSimpleName() + " must not be null (in Holder)."); - } - - private void notEmptyArgument(String object, String message) throws FaultMessage { - if (StringUtils.isEmpty(object)) { - throw createIllegalArgumentFault(message); - } - } - - private void notNullArgument(Object object, String message) throws FaultMessage { - if (object == null) { - throw createIllegalArgumentFault(message); - } - } - - public FaultMessage createIllegalArgumentFault(String message) { - FaultType faultType = new IllegalArgumentFaultType(); - return new FaultMessage(message, faultType); - } - - public void throwFault(Throwable ex, OperationResult result) throws FaultMessage { - if (result != null) { - result.recordFatalError(ex.getMessage(), ex); - } - - FaultType faultType; - if (ex instanceof ObjectNotFoundException) { - faultType = new ObjectNotFoundFaultType(); - } else if (ex instanceof IllegalArgumentException) { - faultType = new IllegalArgumentFaultType(); - } else if (ex instanceof ObjectAlreadyExistsException) { - faultType = new ObjectAlreadyExistsFaultType(); - } else if (ex instanceof CommunicationException) { - faultType = new CommunicationFaultType(); - } else if (ex instanceof ConfigurationException) { - faultType = new ConfigurationFaultType(); - } else if (ex instanceof ExpressionEvaluationException) { - faultType = new SystemFaultType(); - } else if (ex instanceof SchemaException) { - faultType = new SchemaViolationFaultType(); - } else if (ex instanceof PolicyViolationException) { - faultType = new PolicyViolationFaultType(); - } else if (ex instanceof AuthorizationException) { - throw new Fault(new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION), - WSSecurityException.ErrorCode.FAILED_AUTHENTICATION.getQName()); - } else if (ex instanceof SecurityViolationException) { - throw new Fault(new WSSecurityException(WSSecurityException.ErrorCode.FAILURE), - WSSecurityException.ErrorCode.FAILURE.getQName()); - } else{ - faultType = new SystemFaultType(); - } - faultType.setMessage(ex.getMessage()); - if (result != null) { - faultType.setOperationResult(result.createOperationResultType()); - } - - FaultMessage fault = new FaultMessage(ex.getMessage(), faultType, ex); - LOGGER.trace("Throwing fault message type: {}", faultType.getClass(), fault); - throw fault; - } - - @Override - public TaskType importFromResource(String resourceOid, QName objectClass) - throws FaultMessage { - notEmptyArgument(resourceOid, "Resource oid must not be null or empty."); - notNullArgument(objectClass, "Object class must not be null."); - - Task task = createTaskInstance(IMPORT_FROM_RESOURCE); - auditLogin(task); - OperationResult operationResult = task.getResult(); - - try { - modelService.importFromResource(resourceOid, objectClass, task, operationResult); - operationResult.computeStatus(); - return handleTaskResult(task); - } catch (Exception ex) { - LoggingUtils.logException(LOGGER, "# MODEL importFromResource() failed", ex); - auditLogout(task); - throwFault(ex, operationResult); - // notreached - return null; - } - } - - @Override - public TaskType notifyChange(ResourceObjectShadowChangeDescriptionType changeDescription) - throws FaultMessage { - // TODO Auto-generated method stub - notNullArgument(changeDescription, "Change description must not be null"); - LOGGER.trace("notify change started"); - - Task task = createTaskInstance(NOTIFY_CHANGE); - OperationResult parentResult = task.getResult(); - - try { - modelService.notifyChange(changeDescription, task, parentResult); - } catch (ObjectNotFoundException | SchemaException | CommunicationException | ConfigurationException | - SecurityViolationException | ObjectAlreadyExistsException | ExpressionEvaluationException | - RuntimeException | Error | PolicyViolationException ex) { - LoggingUtils.logException(LOGGER, "# MODEL notifyChange() failed", ex); - auditLogout(task); - throwFault(ex, parentResult); - } - - - LOGGER.info("notify change ended."); - LOGGER.info("result of notify change: {}", parentResult.debugDump()); - return handleTaskResult(task); - } - - /** - * return appropriate form of taskType (and result) to - * return back to a web service caller. - * - * @param task - */ - private TaskType handleTaskResult(Task task) { - return task.getUpdatedTaskObject().asObjectable(); - } - -} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebServiceRaw.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebServiceRaw.java deleted file mode 100644 index 80bfeb1fc4b..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/ModelWebServiceRaw.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (c) 2010-2015 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl; - -import com.evolveum.midpoint.model.api.ModelPort; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.PrismSerializer; -import com.evolveum.midpoint.prism.SerializationOptions; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaOperationListType; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultMessage; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ExecuteChangesResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ExecuteChangesType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ExecuteScriptsResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ExecuteScriptsType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.FindShadowOwnerResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.FindShadowOwnerType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.GetObjectResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.GetObjectType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ImportFromResourceResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ImportFromResourceType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.NotifyChangeResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.NotifyChangeType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.SearchObjectsResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.SearchObjectsType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.TestResourceResponseType; -import com.evolveum.midpoint.xml.ns._public.model.model_3.TestResourceType; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import javax.xml.namespace.QName; -import javax.xml.soap.Detail; -import javax.xml.soap.SOAPException; -import javax.xml.soap.SOAPFactory; -import javax.xml.soap.SOAPFault; -import javax.xml.transform.dom.DOMSource; -import javax.xml.ws.Holder; -import javax.xml.ws.Provider; -import javax.xml.ws.soap.SOAPFaultException; - -/** - * - * @author mederly - * - */ -@Service -public class ModelWebServiceRaw implements Provider { - - private static final Trace LOGGER = TraceManager.getTrace(ModelWebServiceRaw.class); - - public static final String NS_SOAP11_ENV = "http://schemas.xmlsoap.org/soap/envelope/"; - public static final String NS_SOAP11_ENV_PREFIX = "SOAP-ENV"; - public static final QName SOAP11_FAULT = new QName(NS_SOAP11_ENV, "Fault"); - public static final QName SOAP11_FAULTCODE = new QName("", "faultcode"); - public static final String SOAP11_FAULTCODE_SERVER = NS_SOAP11_ENV_PREFIX + ":Server"; - public static final QName SOAP11_FAULTSTRING = new QName("", "faultstring"); - public static final QName SOAP11_FAULTACTOR = new QName("", "faultactor"); - public static final QName SOAP11_FAULT_DETAIL = new QName("", "detail"); - public static final String ACTOR = "TODO"; - - @Autowired - private ModelWebService ws; - - @Autowired - private PrismContext prismContext; - - @Override - public DOMSource invoke(DOMSource request) { - try { - return invokeAllowingFaults(request); - } catch (FaultMessage faultMessage) { - try { - SOAPFactory factory = SOAPFactory.newInstance(); - SOAPFault soapFault = factory.createFault(); - soapFault.setFaultCode(SOAP11_FAULTCODE_SERVER); // todo here is a constant until we have a mechanism to determine the correct value (client / server) - soapFault.setFaultString(faultMessage.getMessage()); - Detail detail = soapFault.addDetail(); - serializeFaultMessage(detail, faultMessage); - // fault actor? - // stack trace of the outer exception (FaultMessage) is unimportant, because it is always created at one place - // todo consider providing stack trace of the inner exception - //Detail detail = soapFault.addDetail(); - //detail.setTextContent(getStackTraceAsString(faultMessage)); - throw new SOAPFaultException(soapFault); - } catch (SOAPException e) { - throw new RuntimeException("SOAP Exception: " + e.getMessage(), e); - } - } - } - - public DOMSource invokeAllowingFaults(DOMSource request) throws FaultMessage { - Node rootNode = request.getNode(); - Element rootElement; - if (rootNode instanceof Document) { - rootElement = ((Document) rootNode).getDocumentElement(); - } else if (rootNode instanceof Element) { - rootElement = (Element) rootNode; - } else { - throw ws.createIllegalArgumentFault("Unexpected DOM node type: " + rootNode); - } - - Object requestObject; - try { - requestObject = prismContext.parserFor(rootElement).parseRealValue(); - } catch (SchemaException e) { - throw ws.createIllegalArgumentFault("Couldn't parse SOAP request body because of schema exception: " + e.getMessage()); - } - - Node response; - Holder operationResultTypeHolder = new Holder<>(); - try { - PrismSerializer serializer = prismContext.domSerializer() - .options(SerializationOptions.createSerializeReferenceNames()); - if (requestObject instanceof GetObjectType) { - GetObjectType g = (GetObjectType) requestObject; - Holder objectTypeHolder = new Holder<>(); - ws.getObject(g.getObjectType(), g.getOid(), g.getOptions(), objectTypeHolder, operationResultTypeHolder); - GetObjectResponseType gr = new GetObjectResponseType(); - gr.setObject(objectTypeHolder.value); - gr.setResult(operationResultTypeHolder.value); - response = serializer.serializeAnyData(gr, ModelPort.GET_OBJECT_RESPONSE); - } else if (requestObject instanceof SearchObjectsType) { - SearchObjectsType s = (SearchObjectsType) requestObject; - Holder objectListTypeHolder = new Holder<>(); - ws.searchObjects(s.getObjectType(), s.getQuery(), s.getOptions(), objectListTypeHolder, operationResultTypeHolder); - SearchObjectsResponseType sr = new SearchObjectsResponseType(); - sr.setObjectList(objectListTypeHolder.value); - sr.setResult(operationResultTypeHolder.value); - response = serializer.serializeAnyData(sr, ModelPort.SEARCH_OBJECTS_RESPONSE); - } else if (requestObject instanceof ExecuteChangesType) { - ExecuteChangesType e = (ExecuteChangesType) requestObject; - ObjectDeltaOperationListType objectDeltaOperationListType = ws.executeChanges(e.getDeltaList(), e.getOptions()); - ExecuteChangesResponseType er = new ExecuteChangesResponseType(); - er.setDeltaOperationList(objectDeltaOperationListType); - response = serializer.serializeAnyData(er, ModelPort.EXECUTE_CHANGES_RESPONSE); - } else if (requestObject instanceof FindShadowOwnerType) { - FindShadowOwnerType f = (FindShadowOwnerType) requestObject; - Holder userTypeHolder = new Holder<>(); - ws.findShadowOwner(f.getShadowOid(), userTypeHolder, operationResultTypeHolder); - FindShadowOwnerResponseType fsr = new FindShadowOwnerResponseType(); - fsr.setUser(userTypeHolder.value); - fsr.setResult(operationResultTypeHolder.value); - response = serializer.serializeAnyData(fsr, ModelPort.FIND_SHADOW_OWNER_RESPONSE); - } else if (requestObject instanceof TestResourceType) { - TestResourceType tr = (TestResourceType) requestObject; - OperationResultType operationResultType = ws.testResource(tr.getResourceOid()); - TestResourceResponseType trr = new TestResourceResponseType(); - trr.setResult(operationResultType); - response = serializer.serializeAnyData(trr, ModelPort.TEST_RESOURCE_RESPONSE); - } else if (requestObject instanceof ExecuteScriptsType) { - ExecuteScriptsType es = (ExecuteScriptsType) requestObject; - ExecuteScriptsResponseType esr = ws.executeScripts(es); - response = serializer.serializeAnyData(esr, ModelPort.EXECUTE_SCRIPTS_RESPONSE); - } else if (requestObject instanceof ImportFromResourceType) { - ImportFromResourceType ifr = (ImportFromResourceType) requestObject; - TaskType taskType = ws.importFromResource(ifr.getResourceOid(), ifr.getObjectClass()); - ImportFromResourceResponseType ifrr = new ImportFromResourceResponseType(); - ifrr.setTask(taskType); - response = serializer.serializeAnyData(ifrr, ModelPort.IMPORT_FROM_RESOURCE_RESPONSE); - } else if (requestObject instanceof NotifyChangeType) { - NotifyChangeType nc = (NotifyChangeType) requestObject; - TaskType taskType = ws.notifyChange(nc.getChangeDescription()); - NotifyChangeResponseType ncr = new NotifyChangeResponseType(); - ncr.setTask(taskType); - response = serializer.serializeAnyData(ncr, ModelPort.NOTIFY_CHANGE_RESPONSE); - } else { - throw ws.createIllegalArgumentFault("Unsupported request type: " + requestObject); - } - } catch (SchemaException e) { - throwFault(e, operationResultTypeHolder.value); - // not reached - return null; - } - - // brutal hack for MID-2001 (serializing and parsing eliminates the problem!) - //String serialized = DOMUtil.printDom(response).toString(); - //LOGGER.trace("WEB SERVICE RESPONSE:\n{}", serialized); - //response = DOMUtil.parseDocument(serialized); - - return new DOMSource(response); - } - - private void serializeFaultMessage(Detail detail, FaultMessage faultMessage) { - prismContext.hacks() - .serializeFaultMessage(detail, faultMessage.getFaultInfo(), SchemaConstants.FAULT_MESSAGE_ELEMENT_NAME, LOGGER); - } - - private void throwFault(Exception ex, OperationResultType resultType) throws FaultMessage { - if (resultType != null) { - ws.throwFault(ex, OperationResult.createOperationResult(resultType)); - } else { - ws.throwFault(ex, null); - } - } -} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java index bdc692dfb22..458983515b3 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java @@ -20,6 +20,9 @@ import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.PipelineDataType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.PipelineItemType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionEvaluationOptionsType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ValueListType; import com.evolveum.prism.xml.ns._public.query_3.QueryType; import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; @@ -198,4 +201,29 @@ public PipelineData cloneMutableState() { data.forEach(d -> rv.add(d.cloneMutableState())); return rv; } + + public static PipelineDataType prepareXmlData( + List output, ScriptingExpressionEvaluationOptionsType options) { + boolean hideResults = options != null && Boolean.TRUE.equals(options.isHideOperationResults()); + PipelineDataType rv = new PipelineDataType(); + if (output != null) { + for (PipelineItem item : output) { + PipelineItemType itemType = new PipelineItemType(); + PrismValue value = item.getValue(); + if (value instanceof PrismReferenceValue) { + // This is a bit of hack: value.getRealValue() would return unserializable object (PRV$1 - does not have type QName) + ObjectReferenceType ort = new ObjectReferenceType(); + ort.setupReferenceValue((PrismReferenceValue) value); + itemType.setValue(ort); + } else { + itemType.setValue(value.getRealValue()); + } + if (!hideResults) { + itemType.setResult(item.getResult().createOperationResultType()); + } + rv.getItem().add(itemType); + } + } + return rv; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointPasswordValidator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointPasswordValidator.java deleted file mode 100644 index eebb7c1d911..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/MidpointPasswordValidator.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl.security; - -import com.evolveum.midpoint.model.api.authentication.GuiProfiledPrincipalManager; -import com.evolveum.midpoint.model.api.authentication.GuiProfiledPrincipal; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.security.api.ConnectionEnvironment; -import com.evolveum.midpoint.security.api.MidPointPrincipal; -import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; - -import org.apache.wss4j.common.ext.WSSecurityException; -import org.apache.wss4j.dom.handler.RequestData; -import org.apache.wss4j.dom.message.token.UsernameToken; -import org.apache.wss4j.dom.validate.Credential; -import org.apache.wss4j.dom.validate.UsernameTokenValidator; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class MidpointPasswordValidator extends UsernameTokenValidator { - - @Autowired private PasswordAuthenticationEvaluatorImpl passwdEvaluator; - @Autowired private GuiProfiledPrincipalManager userService; - - @Override - public Credential validate(Credential credential, RequestData data) throws WSSecurityException { - try { - Credential credentialToReturn = super.validate(credential, data); - recordAuthenticationSuccess(credential); - return credentialToReturn; - } catch (WSSecurityException ex) { - recordAuthenticatonError(credential, ex); - throw ex; - } - } - - private void recordAuthenticationSuccess(Credential credential) throws WSSecurityException { - MidPointPrincipal principal = resolveMidpointPrincipal(credential); - ConnectionEnvironment connEnv = ConnectionEnvironment.create(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - passwdEvaluator.recordPasswordAuthenticationSuccess(principal, connEnv, resolvePassowrd(principal)); - } - - private void recordAuthenticatonError(Credential credential, WSSecurityException originEx) throws WSSecurityException { - - - MidPointPrincipal principal = resolveMidpointPrincipal(credential); - - PasswordType passwordType = resolvePassowrd(principal); - - ConnectionEnvironment connEnv = ConnectionEnvironment.create(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - - PasswordCredentialsPolicyType passwdPolicy = null; - - if (principal.getApplicableSecurityPolicy() != null) { - CredentialsPolicyType credentialsPolicyType = principal.getApplicableSecurityPolicy().getCredentials(); - passwdPolicy = credentialsPolicyType.getPassword(); - } - - passwdEvaluator.recordPasswordAuthenticationFailure(principal, connEnv, passwordType, passwdPolicy, originEx.getMessage()); - } - - private MidPointPrincipal resolveMidpointPrincipal(Credential credential) throws WSSecurityException { - UsernameToken usernameToken = credential.getUsernametoken(); - String username = usernameToken.getName(); - - GuiProfiledPrincipal principal = null; - try { - principal = userService.getPrincipal(username, UserType.class); - } catch (ObjectNotFoundException | SchemaException | CommunicationException | ConfigurationException | SecurityViolationException | ExpressionEvaluationException e) { - - throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION, e); - } - - return principal; - } - - private PasswordType resolvePassowrd(MidPointPrincipal principal) { - FocusType user = principal.getFocus(); - PasswordType passwordType = null; - if (user.getCredentials() != null) { - passwordType = user.getCredentials().getPassword(); - } - - return passwordType; - } -} diff --git a/model/model-impl/src/main/resources/ctx-model.xml b/model/model-impl/src/main/resources/ctx-model.xml index fe9c2343717..5c06ec66d76 100644 --- a/model/model-impl/src/main/resources/ctx-model.xml +++ b/model/model-impl/src/main/resources/ctx-model.xml @@ -8,24 +8,19 @@ --> + http://cxf.apache.org/schemas/jaxrs.xsd" + default-lazy-init="false" + default-autowire="byName"> @@ -35,15 +30,15 @@ - - - - + + + + - + @@ -51,13 +46,12 @@ - - + + - + @@ -65,34 +59,38 @@ - - - - + + + + + - - - + + + + - - - - + + + + + - - - + + + + - + @@ -116,8 +114,8 @@ - + @@ -125,8 +123,8 @@ - + @@ -135,8 +133,8 @@ - + @@ -145,54 +143,49 @@ - - - - - - - + class="com.evolveum.midpoint.model.common.expression.evaluator.AssignmentTargetSearchExpressionEvaluatorFactory"> + + + + + + + - - - - - - - + class="com.evolveum.midpoint.model.common.expression.evaluator.ReferenceSearchExpressionEvaluatorFactory"> + + + + + + + - - - - - - - + class="com.evolveum.midpoint.model.common.expression.evaluator.AssociationTargetSearchExpressionEvaluatorFactory"> + + + + + + + - - - - - - + class="com.evolveum.midpoint.model.common.expression.evaluator.AssociationFromLinkExpressionEvaluatorFactory"> + + + + + + - + @@ -207,8 +200,7 @@ - + @@ -216,8 +208,8 @@ - + @@ -302,37 +294,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java index 58e56d53e8f..ecf2aee10c3 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java @@ -14,13 +14,13 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; -import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; -import com.evolveum.midpoint.model.api.ScriptExecutionException; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; @@ -31,7 +31,7 @@ import com.evolveum.midpoint.common.LoggingConfigurationManager; import com.evolveum.midpoint.model.api.ModelPublicConstants; import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.model.impl.ModelWebService; +import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; @@ -49,7 +49,6 @@ import com.evolveum.midpoint.schema.internals.InternalMonitor; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.schema.util.ObjectQueryUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.LogfileTestTailer; import com.evolveum.midpoint.test.util.MidPointAsserts; @@ -1274,14 +1273,13 @@ private void assertAttributesFetched(List data) { } } - private void dumpOutput(ExecutionContext output, OperationResult result) throws JAXBException, SchemaException { + private void dumpOutput(ExecutionContext output, OperationResult result) throws SchemaException { displayDumpable("output", output.getFinalOutput()); displayValue("stdout", output.getConsoleOutput()); display(result); if (output.getFinalOutput() != null) { - PipelineDataType bean = ModelWebService.prepareXmlData(output.getFinalOutput().getData(), null); + PipelineDataType bean = PipelineData.prepareXmlData(output.getFinalOutput().getData(), null); displayValue("output in XML", prismContext.xmlSerializer().root(new QName("output")).serializeRealValue(bean)); } } - } diff --git a/pom.xml b/pom.xml index 13aef71209b..258bb11c1c1 100644 --- a/pom.xml +++ b/pom.xml @@ -198,7 +198,6 @@ 1.4.0 7.1.1 7.0 - 2.3.1 1.1.3 9.4.20.v20190813 3.6.2 @@ -216,11 +215,6 @@ javax.xml.soap-api ${javax.xml.soap.version} - - javax.xml.ws - jaxws-api - ${jaxws-api.version} - org.apache.geronimo.specs geronimo-ws-metadata_2.0_spec From a86994ec0b9c0b0750098aa534c3899e656680b5 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Sun, 3 May 2020 19:00:26 +0200 Subject: [PATCH 18/41] removed SpringAuthenticationJanitorInterceptor and WsFaultListener Will be moved to SOAP overlay example. Right now the first interceptor is not fully covered by overlay, authorizations need more work. --- ...pringAuthenticationJanitorInterceptor.java | 69 ------------------- .../model/impl/security/WsFaultListener.java | 68 ------------------ .../src/main/resources/ctx-model.xml | 36 +--------- 3 files changed, 1 insertion(+), 172 deletions(-) delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationJanitorInterceptor.java delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/WsFaultListener.java diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationJanitorInterceptor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationJanitorInterceptor.java deleted file mode 100644 index bb4345821cd..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationJanitorInterceptor.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2010-2014 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl.security; - -import org.apache.cxf.binding.soap.SoapMessage; -import org.apache.cxf.interceptor.Fault; -import org.apache.cxf.message.Message; -import org.apache.cxf.phase.Phase; -import org.apache.cxf.phase.PhaseInterceptor; -import org.springframework.security.core.context.SecurityContextHolder; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -/** - * Responsible to cleanup spring authentication object after we finished WS method call - */ -public class SpringAuthenticationJanitorInterceptor implements PhaseInterceptor { - - private String phase; - private Set before = new HashSet<>(); - private Set after = new HashSet<>(); - private String id; - - public SpringAuthenticationJanitorInterceptor() { - super(); - id = getClass().getName(); - phase = Phase.POST_INVOKE; - } - - @Override - public Set getAfter() { - return after; - } - - @Override - public Set getBefore() { - return before; - } - - @Override - public String getId() { - return id; - } - - @Override - public String getPhase() { - return phase; - } - - @Override - public Collection> getAdditionalInterceptors() { - return null; - } - - @Override - public void handleMessage(SoapMessage message) throws Fault { - SecurityContextHolder.getContext().setAuthentication(null); - } - - @Override - public void handleFault(SoapMessage message) { - } -} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/WsFaultListener.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/WsFaultListener.java deleted file mode 100644 index e10cd54f66d..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/WsFaultListener.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2015-2016 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl.security; - -import javax.xml.soap.SOAPMessage; - -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.security.api.ConnectionEnvironment; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; - -import org.apache.cxf.logging.FaultListener; -import org.apache.cxf.message.Message; -import org.apache.wss4j.common.ext.WSSecurityException; - -public class WsFaultListener implements FaultListener { - - private static final Trace LOGGER = TraceManager.getTrace(WsFaultListener.class); - - private SecurityHelper securityHelper; - - public WsFaultListener(SecurityHelper securityHelper) { - super(); - this.securityHelper = securityHelper; - } - - @Override - public boolean faultOccurred(Exception exception, String description, Message message) { - LOGGER.trace("Handling fault: {}: {} - {}-{}", new Object[]{exception, description, message, exception}); - Object audited = message.getContextualProperty(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME); - if (audited != null && ((Boolean)audited)) { - return true; - } - if (exception instanceof PasswordCallbackException) { - return true; - } - if (exception.getCause() instanceof PasswordCallbackException) { - return true; - } - if (exception.getCause() != null && exception.getCause().getCause() instanceof PasswordCallbackException) { - return true; - } - try { - String auditMessage = exception.getMessage(); - if (exception.getClass() != null) { - // Exception cause has much better message because CXF masks real messages in the SOAP faults. - auditMessage = exception.getCause().getMessage(); - } - SOAPMessage saajSoapMessage = message.getContent(SOAPMessage.class); - String username = securityHelper.getUsernameFromMessage(saajSoapMessage); - ConnectionEnvironment connEnv = ConnectionEnvironment.create(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - securityHelper.auditLoginFailure(username, null, connEnv, auditMessage); - } catch (WSSecurityException e) { - // Ignore - LOGGER.trace("Exception getting username from soap message (probably safe to ignore)", e); - } catch (Exception e) { - LOGGER.error("Error auditing SOAP fault: "+e.getMessage(), e); - // but otherwise ignore it - } - return true; - } - - -} diff --git a/model/model-impl/src/main/resources/ctx-model.xml b/model/model-impl/src/main/resources/ctx-model.xml index fb746c6ebb0..a60d9eef9cf 100644 --- a/model/model-impl/src/main/resources/ctx-model.xml +++ b/model/model-impl/src/main/resources/ctx-model.xml @@ -326,45 +326,11 @@ - - - - - - - - - - - + - - - - - - - - - - - - - - - From b3ce2638363da4b839bde57b1d61cbd17204b5ee Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Mon, 4 May 2020 12:34:24 +0200 Subject: [PATCH 19/41] prism-api/impl: removed Hacks#serializeFaultMessage + javax.xml.soap-api --- infra/prism-api/pom.xml | 4 --- .../com/evolveum/midpoint/prism/Hacks.java | 16 +++------ infra/prism-impl/pom.xml | 5 --- .../midpoint/prism/impl/HacksImpl.java | 33 +++++-------------- 4 files changed, 14 insertions(+), 44 deletions(-) diff --git a/infra/prism-api/pom.xml b/infra/prism-api/pom.xml index e30b0ea73f9..a7095521a21 100644 --- a/infra/prism-api/pom.xml +++ b/infra/prism-api/pom.xml @@ -58,10 +58,6 @@ org.jvnet.jaxb2_commons jaxb2-basics-runtime - - javax.xml.soap - javax.xml.soap-api - com.sun.activation javax.activation diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java index 99ee1dff363..aa45b8e66fb 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/Hacks.java @@ -7,28 +7,22 @@ package com.evolveum.midpoint.prism; -import com.evolveum.midpoint.prism.xnode.MapXNode; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.prism.xml.ns._public.types_3.ProtectedDataType; import com.google.common.annotations.VisibleForTesting; - import org.w3c.dom.Element; -import javax.xml.namespace.QName; -import javax.xml.soap.Detail; +import com.evolveum.midpoint.prism.xnode.MapXNode; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.prism.xml.ns._public.types_3.ProtectedDataType; /** * TEMPORARY. - * + *

* This interface belongs to a coursebook on Software Engineering as a horrific design example ;) - * + *

* Prism API and/or client code should be modified to get rid of these hacks. */ public interface Hacks { - void serializeFaultMessage(Detail detail, Object faultInfo, QName faultMessageElementName, Trace logger); - @VisibleForTesting void parseProtectedType(ProtectedDataType protectedType, MapXNode xmap, PrismContext prismContext, ParsingContext pc) throws SchemaException; diff --git a/infra/prism-impl/pom.xml b/infra/prism-impl/pom.xml index 6d707f84b46..dba103b4b05 100644 --- a/infra/prism-impl/pom.xml +++ b/infra/prism-impl/pom.xml @@ -125,11 +125,6 @@ jackson-dataformat-yaml - - javax.xml.soap - javax.xml.soap-api - - org.testng diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java index 618db62dff5..61212ad6e94 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/HacksImpl.java @@ -7,21 +7,23 @@ package com.evolveum.midpoint.prism.impl; +import javax.xml.namespace.QName; + +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Element; + import com.evolveum.midpoint.prism.Hacks; import com.evolveum.midpoint.prism.ParsingContext; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.impl.lex.dom.DomLexicalProcessor; import com.evolveum.midpoint.prism.impl.marshaller.XNodeProcessorUtil; -import com.evolveum.midpoint.prism.impl.xnode.*; +import com.evolveum.midpoint.prism.impl.xnode.ListXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.MapXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.PrimitiveXNodeImpl; +import com.evolveum.midpoint.prism.impl.xnode.XNodeImpl; import com.evolveum.midpoint.prism.xnode.*; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.prism.xml.ns._public.types_3.ProtectedDataType; -import org.jetbrains.annotations.NotNull; -import org.w3c.dom.Element; - -import javax.xml.namespace.QName; -import javax.xml.soap.Detail; /** * TEMPORARY @@ -34,23 +36,6 @@ public class HacksImpl implements Hacks, XNodeMutator { this.prismContext = prismContext; } - /** - * TODO rewrite this method using Prism API - */ - @Override - public void serializeFaultMessage(Detail detail, Object faultInfo, QName faultMessageElementName, Trace logger) { - try { - XNodeImpl faultMessageXnode = prismContext.getBeanMarshaller().marshall(faultInfo); - RootXNodeImpl xroot = new RootXNodeImpl(faultMessageElementName, faultMessageXnode); - xroot.setExplicitTypeDeclaration(true); - QName faultType = prismContext.getSchemaRegistry().determineTypeForClass(faultInfo.getClass()); - xroot.setTypeQName(faultType); - prismContext.getParserDom().serializeUnderElement(xroot, faultMessageElementName, detail); - } catch (SchemaException e) { - logger.error("Error serializing fault message (SOAP fault detail): {}", e.getMessage(), e); - } - } - @Override public void setPrimitiveXNodeValue(PrimitiveXNode node, T value, QName typeName) { ((PrimitiveXNodeImpl) node).setValue(value, typeName); From a98d3911687d28ff3578b15791e48be98e73322a Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 5 May 2020 12:05:31 +0200 Subject: [PATCH 20/41] SpringAuthenticationInjectorInterceptor and SOAP related deps gone SecurityHelper was also cleaned from stuff that was used by interceptors and such. --- model/model-impl/pom.xml | 20 - .../model/impl/security/SecurityHelper.java | 646 ++++++++---------- ...ringAuthenticationInjectorInterceptor.java | 244 ------- .../src/main/resources/ctx-model.xml | 44 +- 4 files changed, 303 insertions(+), 651 deletions(-) delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java diff --git a/model/model-impl/pom.xml b/model/model-impl/pom.xml index 894d4b7d0c9..38f6e5e9a23 100644 --- a/model/model-impl/pom.xml +++ b/model/model-impl/pom.xml @@ -142,10 +142,6 @@ 4.2-SNAPSHOT - - javax.xml.soap - javax.xml.soap-api - commons-io commons-io @@ -170,10 +166,6 @@ org.apache.cxf cxf-core - - org.apache.cxf - cxf-rt-ws-security - org.apache.cxf cxf-rt-frontend-jaxrs @@ -188,10 +180,6 @@ cxf-rt-rs-extension-providers runtime - - org.apache.cxf - cxf-rt-bindings-soap - org.apache.cxf cxf-rt-rs-client @@ -204,14 +192,6 @@ org.apache.wss4j wss4j-ws-security-common - - org.apache.wss4j - wss4j-ws-security-dom - - - org.apache.ws.xmlschema - xmlschema-core - org.jvnet.jaxb2_commons jaxb2-basics-runtime diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java index b9e4979d658..1fd054c9557 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SecurityHelper.java @@ -1,354 +1,292 @@ -/* - * Copyright (c) 2015-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl.security; - -import javax.xml.datatype.Duration; -import javax.xml.soap.SOAPMessage; - -import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; -import com.evolveum.midpoint.model.api.ModelAuditRecorder; -import com.evolveum.midpoint.model.impl.lens.LensContext; -import com.evolveum.midpoint.model.impl.lens.LensProjectionContext; -import com.evolveum.midpoint.model.impl.util.AuditHelper; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.security.api.HttpConnectionInformation; -import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; - -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; - -import org.apache.cxf.binding.soap.SoapMessage; -import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor; -import org.apache.wss4j.common.ext.WSSecurityException; -import org.apache.wss4j.dom.util.WSSecurityUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import com.evolveum.midpoint.audit.api.AuditEventRecord; -import com.evolveum.midpoint.audit.api.AuditEventStage; -import com.evolveum.midpoint.audit.api.AuditEventType; -import com.evolveum.midpoint.model.impl.ModelObjectResolver; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.xml.XmlTypeConverter; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.security.api.ConnectionEnvironment; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.task.api.TaskManager; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; - -/** - * @author semancik - */ -@Component -public class SecurityHelper implements ModelAuditRecorder { - - private static final Trace LOGGER = TraceManager.getTrace(SecurityHelper.class); - - public static final String CONTEXTUAL_PROPERTY_AUDITED_NAME = SecurityHelper.class.getName() + ".audited"; - - @Autowired private TaskManager taskManager; - @Autowired private AuditHelper auditHelper; - @Autowired private ModelObjectResolver objectResolver; - @Autowired private SecurityEnforcer securityEnforcer; - @Autowired private PrismContext prismContext; - - @Override - public void auditLoginSuccess(@NotNull FocusType user, @NotNull ConnectionEnvironment connEnv) { - auditLogin(user.getName().getOrig(), user, connEnv, OperationResultStatus.SUCCESS, null); - } - - public void auditLoginSuccess(@NotNull NodeType node, @NotNull ConnectionEnvironment connEnv) { - auditLogin(node.getName().getOrig(), null, connEnv, OperationResultStatus.SUCCESS, null); - } - - @Override - public void auditLoginFailure(@Nullable String username, @Nullable FocusType focus, @NotNull ConnectionEnvironment connEnv, String message) { - auditLogin(username, focus, connEnv, OperationResultStatus.FATAL_ERROR, message); - } - - private void auditLogin(@Nullable String username, @Nullable FocusType focus, @NotNull ConnectionEnvironment connEnv, @NotNull OperationResultStatus status, - @Nullable String message) { - Task task = taskManager.createTaskInstance(); - task.setChannel(connEnv.getChannel()); - - LOGGER.debug("Login {} username={}, channel={}: {}", - status == OperationResultStatus.SUCCESS ? "success" : "failure", username, - connEnv.getChannel(), message); - - AuditEventRecord record = new AuditEventRecord(AuditEventType.CREATE_SESSION, AuditEventStage.REQUEST); - record.setParameter(username); - if (focus != null ) { - record.setInitiator(focus.asPrismObject()); - } - record.setTimestamp(System.currentTimeMillis()); - record.setOutcome(status); - record.setMessage(message); - storeConnectionEnvironment(record, connEnv); - - auditHelper.audit(record, null, task, new OperationResult(SecurityHelper.class.getName() + ".auditLogin")); - } - - @Override - public void auditLogout(ConnectionEnvironment connEnv, Task task) { - AuditEventRecord record = new AuditEventRecord(AuditEventType.TERMINATE_SESSION, AuditEventStage.REQUEST); - record.setInitiatorAndLoginParameter(task.getOwner()); - record.setTimestamp(System.currentTimeMillis()); - record.setOutcome(OperationResultStatus.SUCCESS); - storeConnectionEnvironment(record, connEnv); - auditHelper.audit(record, null, task, new OperationResult(SecurityHelper.class.getName() + ".auditLogout")); - } - - private void storeConnectionEnvironment(AuditEventRecord record, ConnectionEnvironment connEnv) { - record.setChannel(connEnv.getChannel()); - record.setSessionIdentifier(connEnv.getSessionId()); - HttpConnectionInformation connInfo = connEnv.getConnectionInformation(); - if (connInfo != null) { - record.setRemoteHostAddress(connInfo.getRemoteHostAddress()); - record.setHostIdentifier(connInfo.getLocalHostName()); - } - } - - public String getUsernameFromMessage(SOAPMessage saajSoapMessage) throws WSSecurityException { - if (saajSoapMessage == null) { - return null; - } - Element securityHeader = WSSecurityUtil.getSecurityHeader(saajSoapMessage.getSOAPPart(), ""); - return getUsernameFromSecurityHeader(securityHeader); - } - - private String getUsernameFromSecurityHeader(Element securityHeader) { - if (securityHeader == null) { - return null; - } - - String username = ""; - NodeList list = securityHeader.getChildNodes(); - int len = list.getLength(); - Node elem; - for (int i = 0; i < len; i++) { - elem = list.item(i); - if (elem.getNodeType() != Node.ELEMENT_NODE) { - continue; - } - if ("UsernameToken".equals(elem.getLocalName())) { - NodeList nodes = elem.getChildNodes(); - int len2 = nodes.getLength(); - for (int j = 0; j < len2; j++) { - Node elem2 = nodes.item(j); - if ("Username".equals(elem2.getLocalName())) { - username = elem2.getTextContent(); - } - } - } - } - return username; - } - - public SOAPMessage getSOAPMessage(SoapMessage msg) { - SAAJInInterceptor.INSTANCE.handleMessage(msg); - return msg.getContent(SOAPMessage.class); - } - - /** - * Returns security policy applicable for the specified user. It looks for organization and global policies and takes into account - * deprecated properties and password policy references. The resulting security policy has all the (non-deprecated) properties set. - * If there is also referenced value policy, it is will be stored as "object" in the value policy reference inside the - * returned security policy. - */ - public SecurityPolicyType locateSecurityPolicy(PrismObject focus, PrismObject systemConfiguration, - Task task, OperationResult result) throws SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { - - SecurityPolicyType focusSecurityPolicy = locateFocusSecurityPolicy(focus, task, result); - if (focusSecurityPolicy != null) { - traceSecurityPolicy(focusSecurityPolicy, focus); - return focusSecurityPolicy; - } - - SecurityPolicyType globalSecurityPolicy = locateGlobalSecurityPolicy(focus, systemConfiguration, task, result); - if (globalSecurityPolicy != null) { - traceSecurityPolicy(globalSecurityPolicy, focus); - return globalSecurityPolicy; - } - - return null; - } - - public SecurityPolicyType locateFocusSecurityPolicy(PrismObject focus, Task task, - OperationResult result) throws SchemaException { - PrismObject orgSecurityPolicy = objectResolver.searchOrgTreeWidthFirstReference(focus, - o -> o.asObjectable().getSecurityPolicyRef(), "security policy", task, result); - LOGGER.trace("Found organization security policy: {}", orgSecurityPolicy); - if (orgSecurityPolicy != null) { - SecurityPolicyType orgSecurityPolicyType = orgSecurityPolicy.asObjectable(); - postProcessSecurityPolicy(orgSecurityPolicyType, task, result); - return orgSecurityPolicyType; - } else { - return null; - } - } - - public SecurityPolicyType locateGlobalSecurityPolicy(PrismObject focus, - PrismObject systemConfiguration, Task task, OperationResult result) - throws CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { - if (systemConfiguration != null) { - return resolveGlobalSecurityPolicy(focus, systemConfiguration.asObjectable(), task, result); - } else { - return null; - } - } - - public SecurityPolicyType locateProjectionSecurityPolicy(RefinedObjectClassDefinition structuralObjectClassDefinition, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - LOGGER.trace("Finishing loading of projection context: security policy"); - ObjectReferenceType securityPolicyRef = structuralObjectClassDefinition.getSecurityPolicyRef(); - if (securityPolicyRef == null || securityPolicyRef.getOid() == null) { - LOGGER.trace("Security policy not defined for the projection context."); - return loadProjectionLegacyPasswordPolicy(structuralObjectClassDefinition, task, result); - } - LOGGER.trace("Loading security policy {} from: {}", securityPolicyRef, structuralObjectClassDefinition); - SecurityPolicyType securityPolicy = objectResolver.resolve(securityPolicyRef, SecurityPolicyType.class, null, " projection security policy", task, result); - if (securityPolicy == null) { - LOGGER.debug("Security policy {} defined for the projection does not exist", securityPolicyRef); - return null; - } - postProcessSecurityPolicy(securityPolicy, task, result); - return securityPolicy; - } - - - private SecurityPolicyType loadProjectionLegacyPasswordPolicy(RefinedObjectClassDefinition structuralObjectClassDefinition, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - ObjectReferenceType passwordPolicyRef = structuralObjectClassDefinition.getPasswordPolicy(); - if (passwordPolicyRef == null || passwordPolicyRef.getOid() == null) { - LOGGER.trace("Legacy password policy not defined for the projection context."); - return null; - } - LOGGER.trace("Loading legacy password policy {} from: {}", passwordPolicyRef, structuralObjectClassDefinition); - ValuePolicyType passwordPolicy = objectResolver.resolve(passwordPolicyRef, - ValuePolicyType.class, null, " projection legacy password policy ", task, result); - if (passwordPolicy == null) { - LOGGER.debug("Legacy password policy {} defined for the projection does not exist", passwordPolicyRef); - return null; - } - ObjectReferenceType dummyPasswordPolicyRef = new ObjectReferenceType(); - dummyPasswordPolicyRef.asReferenceValue().setObject(passwordPolicy.asPrismObject()); - PrismObject securityPolicy = prismContext.createObject(SecurityPolicyType.class); - securityPolicy.asObjectable() - .beginCredentials() - .beginPassword() - .valuePolicyRef(dummyPasswordPolicyRef); - return securityPolicy.asObjectable(); - } - - - private SecurityPolicyType resolveGlobalSecurityPolicy(PrismObject focus, - SystemConfigurationType systemConfiguration, Task task, OperationResult result) - throws CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { - ObjectReferenceType globalSecurityPolicyRef = systemConfiguration.getGlobalSecurityPolicyRef(); - if (globalSecurityPolicyRef != null) { - try { - SecurityPolicyType globalSecurityPolicyType = objectResolver.resolve(globalSecurityPolicyRef, SecurityPolicyType.class, null, "global security policy reference in system configuration", task, result); - LOGGER.trace("Using global security policy: {}", globalSecurityPolicyType); - postProcessSecurityPolicy(globalSecurityPolicyType, task, result); - traceSecurityPolicy(globalSecurityPolicyType, focus); - return globalSecurityPolicyType; - } catch (ObjectNotFoundException | SchemaException e) { - LOGGER.error(e.getMessage(), e); - traceSecurityPolicy(null, focus); - return null; - } - } - - return null; - } - - private void traceSecurityPolicy(SecurityPolicyType securityPolicyType, PrismObject user) { - if (LOGGER.isTraceEnabled()) { - if (user != null) { - if (securityPolicyType == null) { - LOGGER.trace("Located security policy for {}: null", user); - } else { - LOGGER.trace("Located security policy for {}:\n{}", user, securityPolicyType.asPrismObject().debugDump(1)); - } - } else { - if (securityPolicyType == null) { - LOGGER.trace("Located global security policy null"); - } else { - LOGGER.trace("Located global security policy :\n{}", securityPolicyType.asPrismObject().debugDump(1)); - } - } - } - - } - - private void postProcessSecurityPolicy(SecurityPolicyType securityPolicyType, Task task, OperationResult result) { - CredentialsPolicyType creds = securityPolicyType.getCredentials(); - if (creds != null) { - PasswordCredentialsPolicyType passwd = creds.getPassword(); - if (passwd != null) { - postProcessPasswordCredentialPolicy(securityPolicyType, passwd, task, result); - } - for (NonceCredentialsPolicyType nonce: creds.getNonce()) { - postProcessCredentialPolicy(securityPolicyType, nonce, "nonce credential policy", task, result); - } - SecurityQuestionsCredentialsPolicyType securityQuestions = creds.getSecurityQuestions(); - if (securityQuestions != null) { - postProcessCredentialPolicy(securityPolicyType, securityQuestions, "security questions credential policy", task, result); - } - } - } - - private void postProcessPasswordCredentialPolicy(SecurityPolicyType securityPolicyType, PasswordCredentialsPolicyType passwd, Task task, OperationResult result) { - postProcessCredentialPolicy(securityPolicyType, passwd, "password credential policy", task, result); - } - - private ValuePolicyType postProcessCredentialPolicy(SecurityPolicyType securityPolicyType, CredentialPolicyType credPolicy, String credShortDesc, Task task, OperationResult result) { - ObjectReferenceType valuePolicyRef = credPolicy.getValuePolicyRef(); - if (valuePolicyRef == null) { - return null; - } - ValuePolicyType valuePolicyType; - try { - valuePolicyType = objectResolver.resolve(valuePolicyRef, ValuePolicyType.class, null, credShortDesc + " in " + securityPolicyType, task, result); - } catch (ObjectNotFoundException | SchemaException | CommunicationException | ConfigurationException | SecurityViolationException | ExpressionEvaluationException e) { - LOGGER.warn("{} {} referenced from {} was not found", credShortDesc, valuePolicyRef.getOid(), securityPolicyType); - return null; - } - valuePolicyRef.asReferenceValue().setObject(valuePolicyType.asPrismObject()); - return valuePolicyType; - } - - private SecurityPolicyType postProcessPasswordPolicy(ValuePolicyType passwordPolicyType) { - SecurityPolicyType securityPolicyType = new SecurityPolicyType(); - CredentialsPolicyType creds = new CredentialsPolicyType(); - PasswordCredentialsPolicyType passwd = new PasswordCredentialsPolicyType(); - ObjectReferenceType passwordPolicyRef = new ObjectReferenceType(); - passwordPolicyRef.asReferenceValue().setObject(passwordPolicyType.asPrismObject()); - passwd.setValuePolicyRef(passwordPolicyRef); - creds.setPassword(passwd); - securityPolicyType.setCredentials(creds); - return securityPolicyType; - } - - private Duration daysToDuration(int days) { - return XmlTypeConverter.createDuration((long) days * 1000 * 60 * 60 * 24); - } - - public SecurityEnforcer getSecurityEnforcer() { - return securityEnforcer; - } -} +/* + * Copyright (c) 2015-2019 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.model.impl.security; + +import javax.xml.datatype.Duration; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.audit.api.AuditEventRecord; +import com.evolveum.midpoint.audit.api.AuditEventStage; +import com.evolveum.midpoint.audit.api.AuditEventType; +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.model.api.ModelAuditRecorder; +import com.evolveum.midpoint.model.impl.ModelObjectResolver; +import com.evolveum.midpoint.model.impl.util.AuditHelper; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.result.OperationResultStatus; +import com.evolveum.midpoint.security.api.ConnectionEnvironment; +import com.evolveum.midpoint.security.api.HttpConnectionInformation; +import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.task.api.TaskManager; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +/** + * @author semancik + */ +@Component +public class SecurityHelper implements ModelAuditRecorder { + + private static final Trace LOGGER = TraceManager.getTrace(SecurityHelper.class); + + @Autowired private TaskManager taskManager; + @Autowired private AuditHelper auditHelper; + @Autowired private ModelObjectResolver objectResolver; + @Autowired private SecurityEnforcer securityEnforcer; + @Autowired private PrismContext prismContext; + + @Override + public void auditLoginSuccess(@NotNull FocusType user, @NotNull ConnectionEnvironment connEnv) { + auditLogin(user.getName().getOrig(), user, connEnv, OperationResultStatus.SUCCESS, null); + } + + public void auditLoginSuccess(@NotNull NodeType node, @NotNull ConnectionEnvironment connEnv) { + auditLogin(node.getName().getOrig(), null, connEnv, OperationResultStatus.SUCCESS, null); + } + + @Override + public void auditLoginFailure(@Nullable String username, @Nullable FocusType focus, @NotNull ConnectionEnvironment connEnv, String message) { + auditLogin(username, focus, connEnv, OperationResultStatus.FATAL_ERROR, message); + } + + private void auditLogin(@Nullable String username, @Nullable FocusType focus, @NotNull ConnectionEnvironment connEnv, @NotNull OperationResultStatus status, + @Nullable String message) { + Task task = taskManager.createTaskInstance(); + task.setChannel(connEnv.getChannel()); + + LOGGER.debug("Login {} username={}, channel={}: {}", + status == OperationResultStatus.SUCCESS ? "success" : "failure", username, + connEnv.getChannel(), message); + + AuditEventRecord record = new AuditEventRecord(AuditEventType.CREATE_SESSION, AuditEventStage.REQUEST); + record.setParameter(username); + if (focus != null) { + record.setInitiator(focus.asPrismObject()); + } + record.setTimestamp(System.currentTimeMillis()); + record.setOutcome(status); + record.setMessage(message); + storeConnectionEnvironment(record, connEnv); + + auditHelper.audit(record, null, task, new OperationResult(SecurityHelper.class.getName() + ".auditLogin")); + } + + @Override + public void auditLogout(ConnectionEnvironment connEnv, Task task) { + AuditEventRecord record = new AuditEventRecord(AuditEventType.TERMINATE_SESSION, AuditEventStage.REQUEST); + record.setInitiatorAndLoginParameter(task.getOwner()); + record.setTimestamp(System.currentTimeMillis()); + record.setOutcome(OperationResultStatus.SUCCESS); + storeConnectionEnvironment(record, connEnv); + auditHelper.audit(record, null, task, new OperationResult(SecurityHelper.class.getName() + ".auditLogout")); + } + + private void storeConnectionEnvironment(AuditEventRecord record, ConnectionEnvironment connEnv) { + record.setChannel(connEnv.getChannel()); + record.setSessionIdentifier(connEnv.getSessionId()); + HttpConnectionInformation connInfo = connEnv.getConnectionInformation(); + if (connInfo != null) { + record.setRemoteHostAddress(connInfo.getRemoteHostAddress()); + record.setHostIdentifier(connInfo.getLocalHostName()); + } + } + + /** + * Returns security policy applicable for the specified user. It looks for organization and global policies and takes into account + * deprecated properties and password policy references. The resulting security policy has all the (non-deprecated) properties set. + * If there is also referenced value policy, it is will be stored as "object" in the value policy reference inside the + * returned security policy. + */ + public SecurityPolicyType locateSecurityPolicy(PrismObject focus, PrismObject systemConfiguration, + Task task, OperationResult result) throws SchemaException, CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + + SecurityPolicyType focusSecurityPolicy = locateFocusSecurityPolicy(focus, task, result); + if (focusSecurityPolicy != null) { + traceSecurityPolicy(focusSecurityPolicy, focus); + return focusSecurityPolicy; + } + + SecurityPolicyType globalSecurityPolicy = locateGlobalSecurityPolicy(focus, systemConfiguration, task, result); + if (globalSecurityPolicy != null) { + traceSecurityPolicy(globalSecurityPolicy, focus); + return globalSecurityPolicy; + } + + return null; + } + + public SecurityPolicyType locateFocusSecurityPolicy(PrismObject focus, Task task, + OperationResult result) throws SchemaException { + PrismObject orgSecurityPolicy = objectResolver.searchOrgTreeWidthFirstReference(focus, + o -> o.asObjectable().getSecurityPolicyRef(), "security policy", task, result); + LOGGER.trace("Found organization security policy: {}", orgSecurityPolicy); + if (orgSecurityPolicy != null) { + SecurityPolicyType orgSecurityPolicyType = orgSecurityPolicy.asObjectable(); + postProcessSecurityPolicy(orgSecurityPolicyType, task, result); + return orgSecurityPolicyType; + } else { + return null; + } + } + + public SecurityPolicyType locateGlobalSecurityPolicy(PrismObject focus, + PrismObject systemConfiguration, Task task, OperationResult result) + throws CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + if (systemConfiguration != null) { + return resolveGlobalSecurityPolicy(focus, systemConfiguration.asObjectable(), task, result); + } else { + return null; + } + } + + public SecurityPolicyType locateProjectionSecurityPolicy(RefinedObjectClassDefinition structuralObjectClassDefinition, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + LOGGER.trace("Finishing loading of projection context: security policy"); + ObjectReferenceType securityPolicyRef = structuralObjectClassDefinition.getSecurityPolicyRef(); + if (securityPolicyRef == null || securityPolicyRef.getOid() == null) { + LOGGER.trace("Security policy not defined for the projection context."); + return loadProjectionLegacyPasswordPolicy(structuralObjectClassDefinition, task, result); + } + LOGGER.trace("Loading security policy {} from: {}", securityPolicyRef, structuralObjectClassDefinition); + SecurityPolicyType securityPolicy = objectResolver.resolve(securityPolicyRef, SecurityPolicyType.class, null, " projection security policy", task, result); + if (securityPolicy == null) { + LOGGER.debug("Security policy {} defined for the projection does not exist", securityPolicyRef); + return null; + } + postProcessSecurityPolicy(securityPolicy, task, result); + return securityPolicy; + } + + private SecurityPolicyType loadProjectionLegacyPasswordPolicy(RefinedObjectClassDefinition structuralObjectClassDefinition, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + ObjectReferenceType passwordPolicyRef = structuralObjectClassDefinition.getPasswordPolicy(); + if (passwordPolicyRef == null || passwordPolicyRef.getOid() == null) { + LOGGER.trace("Legacy password policy not defined for the projection context."); + return null; + } + LOGGER.trace("Loading legacy password policy {} from: {}", passwordPolicyRef, structuralObjectClassDefinition); + ValuePolicyType passwordPolicy = objectResolver.resolve(passwordPolicyRef, + ValuePolicyType.class, null, " projection legacy password policy ", task, result); + if (passwordPolicy == null) { + LOGGER.debug("Legacy password policy {} defined for the projection does not exist", passwordPolicyRef); + return null; + } + ObjectReferenceType dummyPasswordPolicyRef = new ObjectReferenceType(); + dummyPasswordPolicyRef.asReferenceValue().setObject(passwordPolicy.asPrismObject()); + PrismObject securityPolicy = prismContext.createObject(SecurityPolicyType.class); + securityPolicy.asObjectable() + .beginCredentials() + .beginPassword() + .valuePolicyRef(dummyPasswordPolicyRef); + return securityPolicy.asObjectable(); + } + + private SecurityPolicyType resolveGlobalSecurityPolicy(PrismObject focus, + SystemConfigurationType systemConfiguration, Task task, OperationResult result) + throws CommunicationException, ConfigurationException, SecurityViolationException, ExpressionEvaluationException { + ObjectReferenceType globalSecurityPolicyRef = systemConfiguration.getGlobalSecurityPolicyRef(); + if (globalSecurityPolicyRef != null) { + try { + SecurityPolicyType globalSecurityPolicyType = objectResolver.resolve(globalSecurityPolicyRef, SecurityPolicyType.class, null, "global security policy reference in system configuration", task, result); + LOGGER.trace("Using global security policy: {}", globalSecurityPolicyType); + postProcessSecurityPolicy(globalSecurityPolicyType, task, result); + traceSecurityPolicy(globalSecurityPolicyType, focus); + return globalSecurityPolicyType; + } catch (ObjectNotFoundException | SchemaException e) { + LOGGER.error(e.getMessage(), e); + traceSecurityPolicy(null, focus); + return null; + } + } + + return null; + } + + private void traceSecurityPolicy(SecurityPolicyType securityPolicyType, PrismObject user) { + if (LOGGER.isTraceEnabled()) { + if (user != null) { + if (securityPolicyType == null) { + LOGGER.trace("Located security policy for {}: null", user); + } else { + LOGGER.trace("Located security policy for {}:\n{}", user, securityPolicyType.asPrismObject().debugDump(1)); + } + } else { + if (securityPolicyType == null) { + LOGGER.trace("Located global security policy null"); + } else { + LOGGER.trace("Located global security policy :\n{}", securityPolicyType.asPrismObject().debugDump(1)); + } + } + } + + } + + private void postProcessSecurityPolicy(SecurityPolicyType securityPolicyType, Task task, OperationResult result) { + CredentialsPolicyType creds = securityPolicyType.getCredentials(); + if (creds != null) { + PasswordCredentialsPolicyType passwd = creds.getPassword(); + if (passwd != null) { + postProcessPasswordCredentialPolicy(securityPolicyType, passwd, task, result); + } + for (NonceCredentialsPolicyType nonce : creds.getNonce()) { + postProcessCredentialPolicy(securityPolicyType, nonce, "nonce credential policy", task, result); + } + SecurityQuestionsCredentialsPolicyType securityQuestions = creds.getSecurityQuestions(); + if (securityQuestions != null) { + postProcessCredentialPolicy(securityPolicyType, securityQuestions, "security questions credential policy", task, result); + } + } + } + + private void postProcessPasswordCredentialPolicy(SecurityPolicyType securityPolicyType, PasswordCredentialsPolicyType passwd, Task task, OperationResult result) { + postProcessCredentialPolicy(securityPolicyType, passwd, "password credential policy", task, result); + } + + private ValuePolicyType postProcessCredentialPolicy(SecurityPolicyType securityPolicyType, CredentialPolicyType credPolicy, String credShortDesc, Task task, OperationResult result) { + ObjectReferenceType valuePolicyRef = credPolicy.getValuePolicyRef(); + if (valuePolicyRef == null) { + return null; + } + ValuePolicyType valuePolicyType; + try { + valuePolicyType = objectResolver.resolve(valuePolicyRef, ValuePolicyType.class, null, credShortDesc + " in " + securityPolicyType, task, result); + } catch (ObjectNotFoundException | SchemaException | CommunicationException | ConfigurationException | SecurityViolationException | ExpressionEvaluationException e) { + LOGGER.warn("{} {} referenced from {} was not found", credShortDesc, valuePolicyRef.getOid(), securityPolicyType); + return null; + } + valuePolicyRef.asReferenceValue().setObject(valuePolicyType.asPrismObject()); + return valuePolicyType; + } + + private SecurityPolicyType postProcessPasswordPolicy(ValuePolicyType passwordPolicyType) { + SecurityPolicyType securityPolicyType = new SecurityPolicyType(); + CredentialsPolicyType creds = new CredentialsPolicyType(); + PasswordCredentialsPolicyType passwd = new PasswordCredentialsPolicyType(); + ObjectReferenceType passwordPolicyRef = new ObjectReferenceType(); + passwordPolicyRef.asReferenceValue().setObject(passwordPolicyType.asPrismObject()); + passwd.setValuePolicyRef(passwordPolicyRef); + creds.setPassword(passwd); + securityPolicyType.setCredentials(creds); + return securityPolicyType; + } + + private Duration daysToDuration(int days) { + return XmlTypeConverter.createDuration((long) days * 1000 * 60 * 60 * 24); + } + + public SecurityEnforcer getSecurityEnforcer() { + return securityEnforcer; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java deleted file mode 100644 index 70624446519..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/security/SpringAuthenticationInjectorInterceptor.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2010-2018 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl.security; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import javax.xml.namespace.QName; -import javax.xml.soap.SOAPException; -import javax.xml.soap.SOAPMessage; - -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; - -import org.apache.commons.lang.StringUtils; -import org.apache.cxf.binding.soap.SoapMessage; -import org.apache.cxf.interceptor.Fault; -import org.apache.cxf.message.Message; -import org.apache.cxf.phase.Phase; -import org.apache.cxf.phase.PhaseInterceptor; -import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor; -import org.apache.ws.commons.schema.utils.DOMUtil; -import org.apache.wss4j.common.ext.WSSecurityException; -import org.apache.wss4j.common.ext.WSSecurityException.ErrorCode; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; - -import com.evolveum.midpoint.model.api.authentication.GuiProfiledPrincipalManager; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.security.api.AuthorizationConstants; -import com.evolveum.midpoint.security.api.ConnectionEnvironment; -import com.evolveum.midpoint.security.api.MidPointPrincipal; -import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters; -import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.task.api.TaskManager; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; - -/** - * Responsible to inject Spring authentication object before we call WS method - */ -public class SpringAuthenticationInjectorInterceptor implements PhaseInterceptor { - - private static final String OPERATION_AUTHORIZATION = SpringAuthenticationInjectorInterceptor.class.getName() + ".authorization"; - - private static final Trace LOGGER = TraceManager.getTrace(SpringAuthenticationInjectorInterceptor.class); - - private String phase; - private Set before = new HashSet<>(); - private Set after = new HashSet<>(); - private String id; - - private GuiProfiledPrincipalManager guiProfiledPrincipalManager; - private SecurityEnforcer securityEnforcer; - private SecurityHelper securityHelper; - private TaskManager taskManager; - - public SpringAuthenticationInjectorInterceptor(GuiProfiledPrincipalManager guiProfiledPrincipalManager, - SecurityEnforcer securityEnforcer, SecurityHelper securityHelper, - TaskManager taskManager) { - super(); - this.guiProfiledPrincipalManager = guiProfiledPrincipalManager; - this.securityEnforcer = securityEnforcer; - this.securityHelper = securityHelper; - this.taskManager = taskManager; - id = getClass().getName(); - phase = Phase.PRE_PROTOCOL; - getAfter().add(WSS4JInInterceptor.class.getName()); - } - - @Override - public Set getAfter() { - return after; - } - - @Override - public Set getBefore() { - return before; - } - - @Override - public String getId() { - return id; - } - - @Override - public String getPhase() { - return phase; - } - - @Override - public Collection> getAdditionalInterceptors() { - return null; - } - - @Override - public void handleMessage(SoapMessage message) throws Fault { - //Note: in constructor we have specified that we will be called after we have been successfully authenticated the user through WS-Security - //Now we will only set the Spring Authentication object based on the user found in the header - LOGGER.trace("Intercepted message: {}", message); - SOAPMessage saajSoapMessage = securityHelper.getSOAPMessage(message); - if (saajSoapMessage == null) { - LOGGER.error("No soap message in handler"); - throw createFault(WSSecurityException.ErrorCode.FAILURE); - } - ConnectionEnvironment connEnv = ConnectionEnvironment.create(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - String username = null; - try { - username = securityHelper.getUsernameFromMessage(saajSoapMessage); - LOGGER.trace("Attempt to authenticate user '{}'", username); - - if (StringUtils.isBlank(username)) { - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, null, connEnv, "Empty username"); - throw createFault(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION); - } - - MidPointPrincipal principal = null; - try { - principal = guiProfiledPrincipalManager.getPrincipal(username, UserType.class); - } catch (SchemaException e) { - handlePrincipalException(message, username, connEnv, "Schema error", e); - } catch (CommunicationException e) { - handlePrincipalException(message, username, connEnv, "Communication error", e); - } catch (ConfigurationException e) { - handlePrincipalException(message, username, connEnv, "Configuration error", e); - } catch (SecurityViolationException e) { - handlePrincipalException(message, username, connEnv, "Security violation", e); - } catch (ExpressionEvaluationException e) { - handlePrincipalException(message, username, connEnv, "Expression error", e); - } - LOGGER.trace("Principal: {}", principal); - if (principal == null) { - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, null, connEnv, "No user"); - throw createFault(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION); - } - - // Account validity and credentials and all this stuff should be already checked - // in the password callback - - Authentication authentication = new UsernamePasswordAuthenticationToken(principal, null); - SecurityContextHolder.getContext().setAuthentication(authentication); - - String operationName; - try { - operationName = DOMUtil.getFirstChildElement(saajSoapMessage.getSOAPBody()).getLocalName(); - } catch (SOAPException e) { - LOGGER.debug("Access to web service denied for user '{}': SOAP error: {}", - username, e.getMessage(), e); - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, principal.getFocus(), connEnv, "SOAP error: "+e.getMessage()); - throw new Fault(e); - } - - // AUTHORIZATION - - Task task = taskManager.createTaskInstance(OPERATION_AUTHORIZATION); - OperationResult result = task.getResult(); - - boolean isAuthorized; - try { - isAuthorized = securityEnforcer.isAuthorized(AuthorizationConstants.AUTZ_WS_ALL_URL, AuthorizationPhaseType.REQUEST, AuthorizationParameters.EMPTY, null, task, result); - LOGGER.trace("Determined authorization for web service access (action: {}): {}", AuthorizationConstants.AUTZ_WS_ALL_URL, isAuthorized); - } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | ConfigurationException | SecurityViolationException e) { - LOGGER.debug("Access to web service denied for user '{}': internal error: {}", - username, e.getMessage(), e); - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, principal.getFocus(), connEnv, "Schema error: "+e.getMessage()); - throw createFault(WSSecurityException.ErrorCode.FAILURE); - } - if (!isAuthorized) { - String action = QNameUtil.qNameToUri(new QName(AuthorizationConstants.NS_AUTHORIZATION_WS, operationName)); - try { - isAuthorized = securityEnforcer.isAuthorized(action, AuthorizationPhaseType.REQUEST, AuthorizationParameters.EMPTY, null, task, result); - LOGGER.trace("Determined authorization for web service operation {} (action: {}): {}", operationName, action, isAuthorized); - } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | ConfigurationException | SecurityViolationException e) { - LOGGER.debug("Access to web service denied for user '{}': schema error: {}", - username, e.getMessage(), e); - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, principal.getFocus(), connEnv, "Internal error: "+e.getMessage()); - throw createFault(WSSecurityException.ErrorCode.FAILURE); - } - } - if (!isAuthorized) { - LOGGER.debug("Access to web service denied for user '{}': not authorized", username); - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, principal.getFocus(), connEnv, "Not authorized"); - throw createFault(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION); - } - - } catch (WSSecurityException e) { - LOGGER.debug("Access to web service denied for user '{}': security exception: {}", - username, e.getMessage(), e); - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, null, connEnv, "Security exception: "+e.getMessage()); - throw new Fault(e, e.getFaultCode()); - } catch (ObjectNotFoundException e) { - LOGGER.debug("Access to web service denied for user '{}': object not found: {}", - username, e.getMessage(), e); - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, null, connEnv, "No user"); - throw createFault(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION); - } - - // Avoid auditing login attempt again if the operation fails on internal authorization - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - - LOGGER.debug("Access to web service allowed for user '{}'", username); - } - - private void handlePrincipalException(SoapMessage message, String username, ConnectionEnvironment connEnv, String errorDesc, Exception e) { - LOGGER.debug("Access to web service denied for user '{}': {}: {}", - username, errorDesc, e.getMessage(), e); - message.put(SecurityHelper.CONTEXTUAL_PROPERTY_AUDITED_NAME, true); - securityHelper.auditLoginFailure(username, null, connEnv, errorDesc + ": " + e.getMessage()); - throw new Fault(e); - } - - private Fault createFault(ErrorCode code) { - return new Fault(new WSSecurityException(code), code.getQName()); - } - - @Override - public void handleFault(SoapMessage message) { - // Nothing to do - } - -} diff --git a/model/model-impl/src/main/resources/ctx-model.xml b/model/model-impl/src/main/resources/ctx-model.xml index a60d9eef9cf..c367c974e66 100644 --- a/model/model-impl/src/main/resources/ctx-model.xml +++ b/model/model-impl/src/main/resources/ctx-model.xml @@ -12,13 +12,11 @@ xmlns:context="http://www.springframework.org/schema/context" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-3.0.xsd - - - http://cxf.apache.org/jaxrs - http://cxf.apache.org/schemas/jaxrs.xsd" + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd + http://cxf.apache.org/jaxrs + http://cxf.apache.org/schemas/jaxrs.xsd" default-lazy-init="false" default-autowire="byName"> @@ -41,9 +39,7 @@ class="com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary"> - - - + - - - + - - - - - - + + - - - + @@ -211,9 +199,7 @@ class="com.evolveum.midpoint.model.common.expression.functions.FunctionLibrary"> - - - + @@ -325,12 +311,4 @@ - - - - - - - - From 332104097df90ee792498e3be7792363db6a3f08 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 6 May 2020 09:33:16 +0200 Subject: [PATCH 21/41] TestConsistencyMechanism: modelWeb call replaced by modelService This affected only test001TestConnectionOpenDJ. modelWeb (WS impl) was removed completely. --- .../story/TestConsistencyMechanism.java | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConsistencyMechanism.java b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConsistencyMechanism.java index 63c5e7083ff..718e3a088e0 100644 --- a/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConsistencyMechanism.java +++ b/testing/story/src/test/java/com/evolveum/midpoint/testing/story/TestConsistencyMechanism.java @@ -10,7 +10,6 @@ import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext; import static com.evolveum.midpoint.test.IntegrationTestTools.assertNoRepoThreadLocalCache; -import static com.evolveum.midpoint.test.IntegrationTestTools.displayJaxb; import java.io.File; import java.io.IOException; @@ -261,8 +260,6 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti repoAddObjectFromFile(USER_TEMPLATE_FILENAME, initResult); assumeAssignmentPolicy(AssignmentPolicyEnforcementType.POSITIVE); - -// DebugUtil.setDetailedDebugDump(true); } /** @@ -288,7 +285,6 @@ public void stopResources() { */ @Test public void test000Integrity() throws Exception { - assertNotNull(modelWeb); assertNotNull(modelService); assertNotNull(repositoryService); assertNotNull(taskManager); @@ -326,22 +322,15 @@ public void test000Integrity() throws Exception { */ @Test public void test001TestConnectionOpenDJ() throws Exception { - Task task = taskManager.createTaskInstance(); - // GIVEN - + given(); assertNoRepoThreadLocalCache(); - // WHEN - OperationResultType result = modelWeb.testResource(RESOURCE_OPENDJ_OID); - - // THEN + when(); + OperationResult result = modelService.testResource(RESOURCE_OPENDJ_OID, getTestTask()); + then(); assertNoRepoThreadLocalCache(); - - displayJaxb("testResource result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("testResource has failed", result); - OperationResult opResult = createOperationResult(); PrismObject resourceOpenDjRepo = repositoryService.getObject(ResourceType.class, @@ -357,11 +346,11 @@ public void test001TestConnectionOpenDJ() throws Exception { assertNotNull("Resource schema was not generated", resourceOpenDjXsdSchemaElement); PrismObject openDjResourceProvisioninig = provisioningService.getObject( - ResourceType.class, RESOURCE_OPENDJ_OID, null, task, opResult); + ResourceType.class, RESOURCE_OPENDJ_OID, null, getTestTask(), opResult); display("Initialized OpenDJ resource resource (provisioning)", openDjResourceProvisioninig); PrismObject openDjResourceModel = provisioningService.getObject(ResourceType.class, - RESOURCE_OPENDJ_OID, null, task, opResult); + RESOURCE_OPENDJ_OID, null, getTestTask(), opResult); display("Initialized OpenDJ resource OpenDJ resource (model)", openDjResourceModel); checkOpenDjResource(resourceTypeOpenDjrepo, "repository"); @@ -373,8 +362,6 @@ public void test001TestConnectionOpenDJ() throws Exception { checkOpenDjResource(openDjResourceProvisioninig.asObjectable(), "provisioning"); checkOpenDjResource(openDjResourceModel.asObjectable(), "model"); - // TODO: model web - } /** From fd2394f09990ce74a984884225522789d995f715 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 6 May 2020 09:34:36 +0200 Subject: [PATCH 22/41] testing/sanity removed completely, should be covered by other tests This also used now-gone modelWeb massively. --- testing/pom.xml | 1 - testing/sanity/.gitignore | 1 - testing/sanity/pom.xml | 260 -- .../testing/sanity/ModelClientUtil.java | 200 - .../midpoint/testing/sanity/TestSanity.java | 3912 ----------------- .../test/resources/ctx-sanity-test-main.xml | 43 - .../src/test/resources/ctx-sanity-test.xml | 32 - .../src/test/resources/logback-test.xml | 46 - .../test/resources/repo/connector-broken.xml | 28 - .../test/resources/repo/resource-broken.xml | 40 - .../test/resources/repo/resource-derby.xml | 48 - .../test/resources/repo/resource-dummy.xml | 322 -- .../resources/repo/resource-opendj-legacy.xml | 707 --- .../test/resources/repo/resource-opendj.xml | 670 --- .../src/test/resources/repo/role-captain.xml | 85 - .../src/test/resources/repo/role-judge.xml | 31 - .../src/test/resources/repo/role-pirate.xml | 42 - .../src/test/resources/repo/role-sailor.xml | 27 - .../test/resources/repo/role-superuser.xml | 15 - .../repo/sample-configuration-object.xml | 42 - .../resources/repo/system-configuration.xml | 34 - .../repo/task-derby-reconciliation.xml | 38 - .../task-opendj-reconciliation-legacy.xml | 41 - .../repo/task-opendj-reconciliation.xml | 39 - .../repo/task-opendj-sync-legacy.xml | 42 - .../test/resources/repo/task-opendj-sync.xml | 38 - .../resources/repo/task-user-recompute.xml | 32 - .../resources/repo/user-administrator.xml | 46 - .../sanity/src/test/resources/repo/user-e.xml | 24 - .../src/test/resources/repo/user-guybrush.xml | 30 - .../src/test/resources/repo/user-jack.xml | 42 - .../src/test/resources/repo/user-template.xml | 43 - .../request/account-angelika-legacy.xml | 27 - .../resources/request/account-angelika.xml | 27 - .../request/account-modify-attrs.xml | 39 - .../request/account-modify-bad-path.xml | 22 - ...ccount-modify-roomnumber-explicit-type.xml | 24 - .../request/account-modify-roomnumber.xml | 22 - .../src/test/resources/request/angelika.ldif | 16 - .../src/test/resources/request/e-create.ldif | 15 - .../src/test/resources/request/elaine.ldif | 16 - .../test/resources/request/gibbs-modify.ldif | 5 - .../src/test/resources/request/herman.ldif | 18 - .../user-modify-activation-disable.xml | 24 - .../request/user-modify-activation-enable.xml | 24 - .../request/user-modify-add-account-derby.xml | 37 - .../user-modify-add-account-legacy.xml | 40 - .../request/user-modify-add-account.xml | 40 - .../user-modify-add-role-captain-1.xml | 31 - .../user-modify-add-role-captain-2.xml | 31 - .../request/user-modify-add-role-judge.xml | 25 - .../request/user-modify-add-role-pirate.xml | 25 - .../user-modify-delete-role-captain-1.xml | 31 - .../user-modify-delete-role-captain-2.xml | 31 - .../user-modify-delete-role-pirate.xml | 25 - .../request/user-modify-fullname-locality.xml | 36 - .../request/user-modify-givenname.xml | 21 - .../resources/request/user-modify-name.xml | 23 - .../request/user-modify-password.xml | 26 - .../test/resources/request/will-modify.ldif | 5 - .../request/will-without-location.ldif | 14 - .../src/test/resources/request/will.ldif | 15 - .../resources/schema/extension-piracy.xsd | 27 - .../resources/schema/extension-whatever.xsd | 55 - testing/sanity/testng-integration.xml | 16 - 65 files changed, 7834 deletions(-) delete mode 100644 testing/sanity/.gitignore delete mode 100644 testing/sanity/pom.xml delete mode 100644 testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/ModelClientUtil.java delete mode 100644 testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/TestSanity.java delete mode 100644 testing/sanity/src/test/resources/ctx-sanity-test-main.xml delete mode 100644 testing/sanity/src/test/resources/ctx-sanity-test.xml delete mode 100644 testing/sanity/src/test/resources/logback-test.xml delete mode 100644 testing/sanity/src/test/resources/repo/connector-broken.xml delete mode 100644 testing/sanity/src/test/resources/repo/resource-broken.xml delete mode 100644 testing/sanity/src/test/resources/repo/resource-derby.xml delete mode 100644 testing/sanity/src/test/resources/repo/resource-dummy.xml delete mode 100644 testing/sanity/src/test/resources/repo/resource-opendj-legacy.xml delete mode 100644 testing/sanity/src/test/resources/repo/resource-opendj.xml delete mode 100644 testing/sanity/src/test/resources/repo/role-captain.xml delete mode 100644 testing/sanity/src/test/resources/repo/role-judge.xml delete mode 100644 testing/sanity/src/test/resources/repo/role-pirate.xml delete mode 100644 testing/sanity/src/test/resources/repo/role-sailor.xml delete mode 100644 testing/sanity/src/test/resources/repo/role-superuser.xml delete mode 100644 testing/sanity/src/test/resources/repo/sample-configuration-object.xml delete mode 100644 testing/sanity/src/test/resources/repo/system-configuration.xml delete mode 100644 testing/sanity/src/test/resources/repo/task-derby-reconciliation.xml delete mode 100644 testing/sanity/src/test/resources/repo/task-opendj-reconciliation-legacy.xml delete mode 100644 testing/sanity/src/test/resources/repo/task-opendj-reconciliation.xml delete mode 100644 testing/sanity/src/test/resources/repo/task-opendj-sync-legacy.xml delete mode 100644 testing/sanity/src/test/resources/repo/task-opendj-sync.xml delete mode 100644 testing/sanity/src/test/resources/repo/task-user-recompute.xml delete mode 100644 testing/sanity/src/test/resources/repo/user-administrator.xml delete mode 100644 testing/sanity/src/test/resources/repo/user-e.xml delete mode 100644 testing/sanity/src/test/resources/repo/user-guybrush.xml delete mode 100644 testing/sanity/src/test/resources/repo/user-jack.xml delete mode 100644 testing/sanity/src/test/resources/repo/user-template.xml delete mode 100644 testing/sanity/src/test/resources/request/account-angelika-legacy.xml delete mode 100644 testing/sanity/src/test/resources/request/account-angelika.xml delete mode 100644 testing/sanity/src/test/resources/request/account-modify-attrs.xml delete mode 100644 testing/sanity/src/test/resources/request/account-modify-bad-path.xml delete mode 100644 testing/sanity/src/test/resources/request/account-modify-roomnumber-explicit-type.xml delete mode 100644 testing/sanity/src/test/resources/request/account-modify-roomnumber.xml delete mode 100644 testing/sanity/src/test/resources/request/angelika.ldif delete mode 100644 testing/sanity/src/test/resources/request/e-create.ldif delete mode 100644 testing/sanity/src/test/resources/request/elaine.ldif delete mode 100644 testing/sanity/src/test/resources/request/gibbs-modify.ldif delete mode 100644 testing/sanity/src/test/resources/request/herman.ldif delete mode 100644 testing/sanity/src/test/resources/request/user-modify-activation-disable.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-activation-enable.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-add-account-derby.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-add-account-legacy.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-add-account.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-add-role-captain-1.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-add-role-captain-2.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-add-role-judge.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-add-role-pirate.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-delete-role-captain-1.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-delete-role-captain-2.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-delete-role-pirate.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-fullname-locality.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-givenname.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-name.xml delete mode 100644 testing/sanity/src/test/resources/request/user-modify-password.xml delete mode 100644 testing/sanity/src/test/resources/request/will-modify.ldif delete mode 100644 testing/sanity/src/test/resources/request/will-without-location.ldif delete mode 100644 testing/sanity/src/test/resources/request/will.ldif delete mode 100644 testing/sanity/src/test/resources/schema/extension-piracy.xsd delete mode 100644 testing/sanity/src/test/resources/schema/extension-whatever.xsd delete mode 100644 testing/sanity/testng-integration.xml diff --git a/testing/pom.xml b/testing/pom.xml index fc4f8e00dd1..744e5ace8e0 100644 --- a/testing/pom.xml +++ b/testing/pom.xml @@ -22,7 +22,6 @@ midPoint Testing Infrastructure - sanity - - - 4.0.0 - - - testing - com.evolveum.midpoint.testing - 4.2-SNAPSHOT - - - sanity - - midPoint Testing - Sanity - - - - com.evolveum.midpoint.infra - util - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.infra - prism-api - 4.2-SNAPSHOT - - - com.evolveum.midpoint.infra - prism-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.infra - schema - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.infra - common - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.repo - repo-api - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.repo - task-api - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.model - model-api - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.model - model-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.model - model-test - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.provisioning - provisioning-api - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.provisioning - provisioning-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.repo - task-quartz-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.repo - audit-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.repo - security-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.repo - security-enforcer-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.repo - repo-test-util - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.model - report-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.model - notifications-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint.model - workflow-impl - 4.2-SNAPSHOT - test - - - - commons-lang - commons-lang - - - commons-io - commons-io - test - - - org.forgerock.opendj - opendj - test - - - org.springframework - spring-beans - - - javax.xml.bind - jaxb-api - test - - - javax.xml.ws - jaxws-api - - - - - org.testng - testng - test - - - com.evolveum.midpoint.tools - test-ng - test - - - com.evolveum.polygon - connector-ldap - test - - - com.evolveum.polygon - connector-databasetable - test - - - com.evolveum.midpoint.infra - test-util - test - - - com.evolveum.midpoint.repo - repo-sql-impl - 4.2-SNAPSHOT - test - - - com.evolveum.midpoint - midpoint-localization - ${project.version} - test - - - com.evolveum.midpoint.repo - repo-sql-impl-test - 4.2-SNAPSHOT - test - - - org.springframework - spring-test - test - - - com.evolveum.midpoint.repo - system-init - 4.2-SNAPSHOT - test - - - javax.servlet - javax.servlet-api - test - - - org.springframework - spring-aspects - test - - - org.springframework - spring-aop - test - - - - - - - maven-surefire-plugin - - - true - - - - maven-failsafe-plugin - - alphabetical - - - - - diff --git a/testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/ModelClientUtil.java b/testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/ModelClientUtil.java deleted file mode 100644 index e1bdff5925c..00000000000 --- a/testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/ModelClientUtil.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2010-2014 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.testing.sanity; - -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaOperationListType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectDeltaOperationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; -import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; -import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; -import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.Validate; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.namespace.QName; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -/** - * @author Radovan Semancik - * - */ -public class ModelClientUtil { - - // XML constants - public static final String NS_COMMON = "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - public static final QName COMMON_PATH = new QName(NS_COMMON, "path"); - public static final QName COMMON_VALUE = new QName(NS_COMMON, "value"); - public static final QName COMMON_GIVEN_NAME = new QName(NS_COMMON, "givenName"); - public static final QName COMMON_ASSIGNMENT = new QName(NS_COMMON, "assignment"); - - public static final String NS_TYPES = "http://prism.evolveum.com/xml/ns/public/types-3"; - private static final QName TYPES_POLYSTRING_ORIG = new QName(NS_TYPES, "orig"); - public static final QName TYPES_CLEAR_VALUE = new QName(NS_TYPES, "clearValue"); - - private static final DocumentBuilder domDocumentBuilder; - - public static JAXBContext instantiateJaxbContext() throws JAXBException { - return JAXBContext.newInstance("com.evolveum.midpoint.xml.ns._public.common.api_types_3:" + - "com.evolveum.midpoint.xml.ns._public.common.common_3:" + - "com.evolveum.midpoint.xml.ns._public.common.fault_3:" + - "com.evolveum.midpoint.xml.ns._public.connector.icf_1.connector_schema_3:" + - "com.evolveum.midpoint.xml.ns._public.connector.icf_1.resource_schema_3:" + - "com.evolveum.midpoint.xml.ns._public.resource.capabilities_3:" + - "com.evolveum.prism.xml.ns._public.annotation_3:" + - "com.evolveum.prism.xml.ns._public.query_3:" + - "com.evolveum.prism.xml.ns._public.types_3:" + - "org.w3._2000._09.xmldsig:" + - "org.w3._2001._04.xmlenc"); - } - - public static Element createPathElement(String stringPath, Document doc) { - String pathDeclaration = "declare default namespace '" + NS_COMMON + "'; " + stringPath; - return createTextElement(COMMON_PATH, pathDeclaration, doc); - } - - public static ItemPathType createItemPathType(String stringPath, PrismContext prismContext) { - String pathDeclaration = "declare default namespace '" + NS_COMMON + "'; " + stringPath; - ItemPathType itemPathType = prismContext.itemPathParser().asItemPathType(pathDeclaration); - return itemPathType; - } - -// public static SearchFilterType parseSearchFilterType(String filterClauseAsXml) throws IOException, SAXException { -// Element filterClauseAsElement = parseElement(filterClauseAsXml); -// SearchFilterType searchFilterType = new SearchFilterType(); -// searchFilterType.setFilterClause(filterClauseAsElement); -// return searchFilterType; -// } - - public static PolyStringType createPolyStringType(String string, Document doc) { - PolyStringType polyStringType = new PolyStringType(); - polyStringType.setOrig(string); - return polyStringType; - } - - public static Element createTextElement(QName qname, String value, Document doc) { - Element element = doc.createElementNS(qname.getNamespaceURI(), qname.getLocalPart()); - element.setTextContent(value); - return element; - } - - public static CredentialsType createPasswordCredentials(String password) { - CredentialsType credentialsType = new CredentialsType(); - credentialsType.setPassword(createPasswordType(password)); - return credentialsType; - } - - public static PasswordType createPasswordType(String password) { - PasswordType passwordType = new PasswordType(); - passwordType.setValue(createProtectedString(password)); - return passwordType; - } - - public static ProtectedStringType createProtectedString(String clearValue) { - ProtectedStringType protectedString = new ProtectedStringType(); - protectedString.setClearValue(clearValue); - return protectedString; } - - public static JAXBElement toJaxbElement(QName name, T value) { - return new JAXBElement<>(name, (Class) value.getClass(), value); - } - - public static Document getDocumnent() { - return domDocumentBuilder.newDocument(); - } - - public static String getTypeUri(Class type) { -// QName typeQName = JAXBUtil.getTypeQName(type); -// String typeUri = QNameUtil.qNameToUri(typeQName); - String typeUri = NS_COMMON + "#" + type.getSimpleName(); - return typeUri; - } - - public static QName getTypeQName(Class type) { - QName typeQName = new QName(NS_COMMON, type.getSimpleName()); - return typeQName; - } - - public static Element parseElement(String stringXml) throws SAXException, IOException { - Document document = domDocumentBuilder.parse(IOUtils.toInputStream(stringXml, StandardCharsets.UTF_8)); - return getFirstChildElement(document); - } - - public static Element getFirstChildElement(Node parent) { - if (parent == null || parent.getChildNodes() == null) { - return null; - } - - NodeList nodes = parent.getChildNodes(); - for (int i = 0; i < nodes.getLength(); i++) { - Node child = nodes.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - return (Element) child; - } - } - - return null; - } - - /** - * Retrieves OID created by model Web Service from the returned list of ObjectDeltaOperations. - * - * @param operationListType result of the model web service executeChanges call - * @param originalDelta original request used to find corresponding ObjectDeltaOperationType instance. Must be of ADD type. - * @return OID if found - * - * PRELIMINARY IMPLEMENTATION. Currently the first returned ADD delta with the same object type as original delta is returned. - */ - public static String getOidFromDeltaOperationList(ObjectDeltaOperationListType operationListType, ObjectDeltaType originalDelta) { - Validate.notNull(operationListType); - Validate.notNull(originalDelta); - if (originalDelta.getChangeType() != ChangeTypeType.ADD) { - throw new IllegalArgumentException("Original delta is not of ADD type"); - } - if (originalDelta.getObjectToAdd() == null) { - throw new IllegalArgumentException("Original delta contains no object-to-be-added"); - } - for (ObjectDeltaOperationType operationType : operationListType.getDeltaOperation()) { - ObjectDeltaType objectDeltaType = operationType.getObjectDelta(); - if (objectDeltaType.getChangeType() == ChangeTypeType.ADD && - objectDeltaType.getObjectToAdd() != null) { - ObjectType objectAdded = (ObjectType) objectDeltaType.getObjectToAdd(); - if (objectAdded.getClass().equals(originalDelta.getObjectToAdd().getClass())) { - return objectAdded.getOid(); - } - } - } - return null; - } - - static { - try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - domDocumentBuilder = factory.newDocumentBuilder(); - } catch (ParserConfigurationException ex) { - throw new IllegalStateException("Error creating XML document " + ex.getMessage()); - } - } - -} diff --git a/testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/TestSanity.java b/testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/TestSanity.java deleted file mode 100644 index a017567e1b8..00000000000 --- a/testing/sanity/src/test/java/com/evolveum/midpoint/testing/sanity/TestSanity.java +++ /dev/null @@ -1,3912 +0,0 @@ -/* - * Copyright (c) 2010-2017 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.testing.sanity; - -import static org.testng.AssertJUnit.*; - -import static com.evolveum.midpoint.prism.util.PrismAsserts.assertEqualsPolyString; -import static com.evolveum.midpoint.prism.util.PrismAsserts.assertParentConsistency; -import static com.evolveum.midpoint.test.IntegrationTestTools.*; - -import java.io.File; -import java.io.IOException; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.*; -import javax.xml.bind.JAXBException; -import javax.xml.namespace.QName; -import javax.xml.ws.Holder; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.Validate; -import org.opends.server.core.ModifyOperation; -import org.opends.server.protocols.internal.InternalSearchOperation; -import org.opends.server.types.ModificationType; -import org.opends.server.types.*; -import org.opends.server.util.ChangeRecordEntry; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.ContextConfiguration; -import org.testng.AssertJUnit; -import org.testng.annotations.AfterClass; -import org.testng.annotations.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition; -import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; -import com.evolveum.midpoint.common.refinery.RefinedResourceSchema; -import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl; -import com.evolveum.midpoint.model.test.AbstractModelIntegrationTest; -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.crypto.EncryptionException; -import com.evolveum.midpoint.prism.delta.ChangeType; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.prism.delta.PropertyDelta; -import com.evolveum.midpoint.prism.match.MatchingRule; -import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; -import com.evolveum.midpoint.prism.path.ItemName; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.prism.schema.SchemaRegistry; -import com.evolveum.midpoint.prism.util.PrismAsserts; -import com.evolveum.midpoint.prism.util.PrismTestUtil; -import com.evolveum.midpoint.prism.xnode.MapXNode; -import com.evolveum.midpoint.prism.xnode.XNode; -import com.evolveum.midpoint.schema.CapabilityUtil; -import com.evolveum.midpoint.schema.DeltaConvertor; -import com.evolveum.midpoint.schema.ResultHandler; -import com.evolveum.midpoint.schema.constants.ObjectTypes; -import com.evolveum.midpoint.schema.constants.SchemaConstants; -import com.evolveum.midpoint.schema.processor.ObjectClassComplexTypeDefinition; -import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition; -import com.evolveum.midpoint.schema.processor.ResourceSchema; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.schema.util.*; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.task.api.TaskExecutionStatus; -import com.evolveum.midpoint.test.Checker; -import com.evolveum.midpoint.test.IntegrationTestTools; -import com.evolveum.midpoint.test.ObjectChecker; -import com.evolveum.midpoint.test.ldap.OpenDJController; -import com.evolveum.midpoint.test.util.DerbyController; -import com.evolveum.midpoint.test.util.TestUtil; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.JAXBUtil; -import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaListType; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaOperationListType; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultMessage; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultType; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.ObjectAlreadyExistsFaultType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationCapabilityType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CredentialsCapabilityType; -import com.evolveum.prism.xml.ns._public.query_3.QueryType; -import com.evolveum.prism.xml.ns._public.types_3.*; - -/** - * Sanity test suite. - *

- * It tests the very basic representative test cases. It does not try to be - * complete. It rather should be quick to execute and pass through the most - * representative cases. It should test all the system components except for - * GUI. Therefore the test cases are selected to pass through most of the - * components. - *

- * It is using embedded H2 repository and embedded OpenDJ instance as a testing - * resource. The repository is instantiated from the Spring context in the - * same way as all other components. OpenDJ instance is started explicitly using - * BeforeClass method. Appropriate resource definition to reach the OpenDJ - * instance is provided in the test data and is inserted in the repository as - * part of test initialization. - * - * @author Radovan Semancik - */ -@ContextConfiguration(locations = { "classpath:ctx-sanity-test-main.xml" }) -@DirtiesContext(classMode = ClassMode.AFTER_CLASS) -public class TestSanity extends AbstractModelIntegrationTest { - - private static final String REPO_DIR_NAME = "src/test/resources/repo/"; - private static final String REQUEST_DIR_NAME = "src/test/resources/request/"; - private static final File REQUEST_DIR = new File(REQUEST_DIR_NAME); - - private static final String SYSTEM_CONFIGURATION_FILENAME = REPO_DIR_NAME + "system-configuration.xml"; - private static final String SYSTEM_CONFIGURATION_OID = "00000000-0000-0000-0000-000000000001"; - - private static final String ROLE_SUPERUSER_FILENAME = REPO_DIR_NAME + "role-superuser.xml"; - private static final String ROLE_SUPERUSER_OID = "00000000-0000-0000-0000-000000000004"; - - private static final String RESOURCE_OPENDJ_FILENAME = REPO_DIR_NAME + "resource-opendj.xml"; - private static final String RESOURCE_OPENDJ_OID = "ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff"; - private static final String RESOURCE_OPENDJ_NS = "http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff"; - protected static final QName RESOURCE_OPENDJ_ACCOUNT_OBJECTCLASS = new QName(RESOURCE_OPENDJ_NS, "inetOrgPerson"); - private static final String RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME = "entryUUID"; - private static final String RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME = "dn"; - - private static final String RESOURCE_DERBY_FILENAME = REPO_DIR_NAME + "resource-derby.xml"; - private static final String RESOURCE_DERBY_OID = "ef2bc95b-76e0-59e2-86d6-999902d3abab"; - - private static final String RESOURCE_BROKEN_FILENAME = REPO_DIR_NAME + "resource-broken.xml"; - private static final String RESOURCE_BROKEN_OID = "ef2bc95b-76e0-59e2-ffff-ffffffffffff"; - - private static final String RESOURCE_DUMMY_FILENAME = REPO_DIR_NAME + "resource-dummy.xml"; - private static final String RESOURCE_DUMMY_OID = "10000000-0000-0000-0000-000000000004"; - - private static final String CONNECTOR_LDAP_NAMESPACE = "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.polygon.connector-ldap/com.evolveum.polygon.connector.ldap.LdapConnector"; - private static final String CONNECTOR_DBTABLE_NAMESPACE = "http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/com.evolveum.polygon.connector-databasetable/org.identityconnectors.databasetable.DatabaseTableConnector"; - - private static final String CONNECTOR_BROKEN_FILENAME = REPO_DIR_NAME + "connector-broken.xml"; - private static final String CONNECTOR_BROKEN_OID = "cccccccc-76e0-59e2-ffff-ffffffffffff"; - - private static final String TASK_OPENDJ_SYNC_FILENAME = REPO_DIR_NAME + "task-opendj-sync.xml"; - private static final String TASK_OPENDJ_SYNC_OID = "91919191-76e0-59e2-86d6-3d4f02d3ffff"; - - private static final String TASK_USER_RECOMPUTE_FILENAME = REPO_DIR_NAME + "task-user-recompute.xml"; - private static final String TASK_USER_RECOMPUTE_OID = "91919191-76e0-59e2-86d6-3d4f02d3aaaa"; - - private static final String TASK_OPENDJ_RECON_FILENAME = REPO_DIR_NAME + "task-opendj-reconciliation.xml"; - private static final String TASK_OPENDJ_RECON_OID = "91919191-76e0-59e2-86d6-3d4f02d30000"; - - private static final String SAMPLE_CONFIGURATION_OBJECT_FILENAME = REPO_DIR_NAME + "sample-configuration-object.xml"; - private static final String SAMPLE_CONFIGURATION_OBJECT_OID = "c0c010c0-d34d-b33f-f00d-999111111111"; - - private static final String USER_TEMPLATE_FILENAME = REPO_DIR_NAME + "user-template.xml"; - private static final String USER_TEMPLATE_OID = "c0c010c0-d34d-b33f-f00d-777111111111"; - - private static final String USER_ADMINISTRATOR_FILENAME = REPO_DIR_NAME + "user-administrator.xml"; - private static final String USER_ADMINISTRATOR_NAME = "administrator"; - private static final String USER_ADMINISTRATOR_OID = "00000000-0000-0000-0000-000000000002"; - - private static final String USER_JACK_FILENAME = REPO_DIR_NAME + "user-jack.xml"; - private static final File USER_JACK_FILE = new File(USER_JACK_FILENAME); - private static final String USER_JACK_OID = "c0c010c0-d34d-b33f-f00d-111111111111"; - private static final String USER_JACK_LDAP_UID = "jack"; - private static final String USER_JACK_LDAP_DN = "uid=" + USER_JACK_LDAP_UID - + "," + OPENDJ_PEOPLE_SUFFIX; - - private static final String USER_GUYBRUSH_FILENAME = REPO_DIR_NAME + "user-guybrush.xml"; - private static final File USER_GUYBRUSH_FILE = new File(USER_GUYBRUSH_FILENAME); - private static final String USER_GUYBRUSH_OID = "c0c010c0-d34d-b33f-f00d-111111111222"; - private static final String USER_GUYBRUSH_USERNAME = "guybrush"; - private static final String USER_GUYBRUSH_LDAP_UID = "guybrush"; - private static final String USER_GUYBRUSH_LDAP_DN = "uid=" + USER_GUYBRUSH_LDAP_UID - + "," + OPENDJ_PEOPLE_SUFFIX; - - private static final String USER_E_LINK_ACTION_FILENAME = REPO_DIR_NAME + "user-e.xml"; - private static final File USER_E_LINK_ACTION_FILE = new File(USER_E_LINK_ACTION_FILENAME); - private static final String LDIF_E_FILENAME_LINK = "src/test/resources/request/e-create.ldif"; - - private static final String ROLE_PIRATE_FILENAME = REPO_DIR_NAME + "role-pirate.xml"; - private static final String ROLE_PIRATE_OID = "12345678-d34d-b33f-f00d-987987987988"; - - private static final String ROLE_SAILOR_FILENAME = REPO_DIR_NAME + "role-sailor.xml"; - private static final String ROLE_SAILOR_OID = "12345678-d34d-b33f-f00d-987955553535"; - - private static final String ROLE_CAPTAIN_FILENAME = REPO_DIR_NAME + "role-captain.xml"; - private static final String ROLE_CAPTAIN_OID = "12345678-d34d-b33f-f00d-987987cccccc"; - - private static final String ROLE_JUDGE_FILENAME = REPO_DIR_NAME + "role-judge.xml"; - private static final String ROLE_JUDGE_OID = "12345111-1111-2222-1111-121212111111"; - - private static final String REQUEST_USER_MODIFY_ADD_ACCOUNT_OPENDJ_FILENAME = REQUEST_DIR_NAME + "user-modify-add-account.xml"; - - private static final String REQUEST_USER_MODIFY_ADD_ACCOUNT_DERBY_FILENAME = REQUEST_DIR_NAME + "user-modify-add-account-derby.xml"; - private static final String USER_JACK_DERBY_LOGIN = "jsparrow"; - - private static final String REQUEST_USER_MODIFY_FULLNAME_LOCALITY_FILENAME = REQUEST_DIR_NAME + "user-modify-fullname-locality.xml"; - private static final String REQUEST_USER_MODIFY_GIVENNAME_FILENAME = REQUEST_DIR_NAME + "user-modify-givenname.xml"; - private static final String REQUEST_USER_MODIFY_PASSWORD_FILENAME = REQUEST_DIR_NAME + "user-modify-password.xml"; - private static final String REQUEST_USER_MODIFY_ACTIVATION_DISABLE_FILENAME = REQUEST_DIR_NAME + "user-modify-activation-disable.xml"; - private static final String REQUEST_USER_MODIFY_ACTIVATION_ENABLE_FILENAME = REQUEST_DIR_NAME + "user-modify-activation-enable.xml"; - private static final String REQUEST_USER_MODIFY_NAME_FILENAME = REQUEST_DIR_NAME + "user-modify-name.xml"; - - private static final String REQUEST_USER_MODIFY_ADD_ROLE_PIRATE_FILENAME = REQUEST_DIR_NAME + "user-modify-add-role-pirate.xml"; - private static final String REQUEST_USER_MODIFY_ADD_ROLE_CAPTAIN_1_FILENAME = REQUEST_DIR_NAME + "user-modify-add-role-captain-1.xml"; - private static final String REQUEST_USER_MODIFY_ADD_ROLE_CAPTAIN_2_FILENAME = REQUEST_DIR_NAME + "user-modify-add-role-captain-2.xml"; - private static final String REQUEST_USER_MODIFY_ADD_ROLE_JUDGE_FILENAME = REQUEST_DIR_NAME + "user-modify-add-role-judge.xml"; - private static final String REQUEST_USER_MODIFY_DELETE_ROLE_PIRATE_FILENAME = REQUEST_DIR_NAME + "user-modify-delete-role-pirate.xml"; - private static final String REQUEST_USER_MODIFY_DELETE_ROLE_CAPTAIN_1_FILENAME = REQUEST_DIR_NAME + "user-modify-delete-role-captain-1.xml"; - private static final String REQUEST_USER_MODIFY_DELETE_ROLE_CAPTAIN_2_FILENAME = REQUEST_DIR_NAME + "user-modify-delete-role-captain-2.xml"; - - private static final File REQUEST_ACCOUNT_MODIFY_ATTRS_FILE = new File(REQUEST_DIR, "account-modify-attrs.xml"); - private static final File REQUEST_ACCOUNT_MODIFY_ROOM_NUMBER_FILE = new File(REQUEST_DIR, "account-modify-roomnumber.xml"); - private static final File REQUEST_ACCOUNT_MODIFY_ROOM_NUMBER_EXPLICIT_TYPE_FILE = new File(REQUEST_DIR, "account-modify-roomnumber-explicit-type.xml"); - private static final File REQUEST_ACCOUNT_MODIFY_BAD_PATH_FILE = new File(REQUEST_DIR, "account-modify-bad-path.xml"); - - private static final String LDIF_WILL_FILENAME = REQUEST_DIR_NAME + "will.ldif"; - private static final File LDIF_WILL_MODIFY_FILE = new File(REQUEST_DIR_NAME, "will-modify.ldif"); - private static final String LDIF_WILL_WITHOUT_LOCATION_FILENAME = REQUEST_DIR_NAME + "will-without-location.ldif"; - private static final String WILL_NAME = "wturner"; - - private static final String LDIF_ANGELIKA_FILENAME = REQUEST_DIR_NAME + "angelika.ldif"; - private static final String ANGELIKA_NAME = "angelika"; - - private static final String ACCOUNT_ANGELIKA_FILENAME = REQUEST_DIR_NAME + "account-angelika.xml"; - - private static final String LDIF_ELAINE_FILENAME = REQUEST_DIR_NAME + "elaine.ldif"; - private static final String ELAINE_NAME = "elaine"; - - private static final File LDIF_GIBBS_MODIFY_FILE = new File(REQUEST_DIR_NAME, "gibbs-modify.ldif"); - - private static final String LDIF_HERMAN_FILENAME = REQUEST_DIR_NAME + "herman.ldif"; - - private static final String NS_MY = "http://whatever.com/my"; - private static final ItemName MY_SHIP_STATE = new ItemName(NS_MY, "shipState"); - private static final ItemName MY_DEAD = new ItemName(NS_MY, "dead"); - - private static final long WAIT_FOR_LOOP_SLEEP_MILLIS = 1000; - - /** - * Unmarshalled resource definition to reach the embedded OpenDJ instance. - * Used for convenience - the tests method may find it handy. - */ - private static ResourceType resourceTypeOpenDjrepo; - private static ResourceType resourceDerby; - private static String accountShadowOidOpendj; - private static String accountShadowOidDerby; - private static String accountShadowOidGuybrushOpendj; - private static String accountGuybrushOpendjEntryUuuid = null; - private static String originalJacksLdapPassword; - private static String lastJacksLdapPassword = null; - - private int lastSyncToken; - - @Autowired - private MatchingRuleRegistry matchingRuleRegistry; - - // This will get called from the superclass to init the repository - // It will be called only once - public void initSystem(Task initTask, OperationResult initResult) throws Exception { - logger.trace("initSystem"); - try { - super.initSystem(initTask, initResult); - - repoAddObjectFromFile(ROLE_SUPERUSER_FILENAME, initResult); - repoAddObjectFromFile(USER_ADMINISTRATOR_FILENAME, initResult); - - // This should discover the connectors - logger.trace("initSystem: trying modelService.postInit()"); - modelService.postInit(initResult); - logger.trace("initSystem: modelService.postInit() done"); - - login(USER_ADMINISTRATOR_NAME); - - // We need to add config after calling postInit() so it will not be applied. - // we want original logging configuration from the test logback config file, not - // the one from the system config. - repoAddObjectFromFile(SYSTEM_CONFIGURATION_FILENAME, initResult); - - // Add broken connector before importing resources - repoAddObjectFromFile(CONNECTOR_BROKEN_FILENAME, initResult); - - // Need to import instead of add, so the (dynamic) connector reference - // will be resolved - // correctly - importObjectFromFile(RESOURCE_OPENDJ_FILENAME, initResult); - importObjectFromFile(RESOURCE_BROKEN_FILENAME, initResult); - - repoAddObjectFromFile(SAMPLE_CONFIGURATION_OBJECT_FILENAME, initResult); - repoAddObjectFromFile(USER_TEMPLATE_FILENAME, initResult); - repoAddObjectFromFile(ROLE_SAILOR_FILENAME, initResult); - repoAddObjectFromFile(ROLE_PIRATE_FILENAME, initResult); - repoAddObjectFromFile(ROLE_CAPTAIN_FILENAME, initResult); - repoAddObjectFromFile(ROLE_JUDGE_FILENAME, initResult); - } catch (Exception ex) { - logger.error("erro: {}", ex); - throw ex; - } - } - - /** - * Initialize embedded OpenDJ instance Note: this is not in the abstract - * superclass so individual tests may avoid starting OpenDJ. - */ - @Override - public void startResources() throws Exception { - openDJController.startCleanServer(); - derbyController.startCleanServer(); - } - - /** - * Shutdown embedded OpenDJ instance Note: this is not in the abstract - * superclass so individual tests may avoid starting OpenDJ. - */ - @AfterClass - public static void stopResources() throws Exception { - openDJController.stop(); - derbyController.stop(); - } - - /** - * Test integrity of the test setup. - * - * @throws SchemaException - * @throws ObjectNotFoundException - * @throws CommunicationException - */ - @Test - public void test000Integrity() throws Exception { - assertNotNull(modelWeb); - assertNotNull(modelService); - assertNotNull(repositoryService); - assertNotNull(taskManager); - - assertNotNull(prismContext); - SchemaRegistry schemaRegistry = prismContext.getSchemaRegistry(); - assertNotNull(schemaRegistry); - // This is defined in extra schema. So this effectively checks whether the extra schema was loaded - PrismPropertyDefinition shipStateDefinition = schemaRegistry.findPropertyDefinitionByElementName(MY_SHIP_STATE); - assertNotNull("No my:shipState definition", shipStateDefinition); - assertEquals("Wrong maxOccurs in my:shipState definition", 1, shipStateDefinition.getMaxOccurs()); - - assertNoRepoThreadLocalCache(); - - Task task = taskManager.createTaskInstance(TestSanity.class.getName() + ".test000Integrity"); - OperationResult result = task.getResult(); - - // Check if OpenDJ resource was imported correctly - - PrismObject openDjResource = repositoryService.getObject(ResourceType.class, RESOURCE_OPENDJ_OID, null, result); - display("Imported OpenDJ resource (repository)", openDjResource); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, openDjResource.getOid()); - - assertNoRepoThreadLocalCache(); - - String ldapConnectorOid = openDjResource.asObjectable().getConnectorRef().getOid(); - PrismObject ldapConnector = repositoryService.getObject(ConnectorType.class, ldapConnectorOid, null, result); - display("LDAP Connector: ", ldapConnector); - - // TODO: test if OpenDJ and Derby are running - - repositoryService.getObject(GenericObjectType.class, SAMPLE_CONFIGURATION_OBJECT_OID, null, result); - } - - /** - * Repeat self-test when we have all the dependencies on the classpath. - */ - @Test - public void test001SelfTests() { - // GIVEN - Task task = getTestTask(); - - // WHEN - OperationResult repositorySelfTestResult = modelDiagnosticService.repositorySelfTest(task); - - // THEN - assertSuccess("Repository self test", repositorySelfTestResult); - - // WHEN - OperationResult provisioningSelfTestResult = modelDiagnosticService.provisioningSelfTest(task); - - // THEN - display("Repository self test result", provisioningSelfTestResult); - // There may be warning about illegal key size on some platforms. As far as it is warning and not error we are OK - // the system will fall back to a interoperable key size - if (provisioningSelfTestResult.getStatus() != OperationResultStatus.SUCCESS && provisioningSelfTestResult.getStatus() != OperationResultStatus.WARNING) { - AssertJUnit.fail("Provisioning self-test failed: " + provisioningSelfTestResult); - } - } - - /** - * Test the testResource method. Expect a complete success for now. - */ - @Test - public void test001TestConnectionOpenDJ() throws Exception { - // GIVEN - try { - assertNoRepoThreadLocalCache(); - - // WHEN - OperationResultType result = modelWeb.testResource(RESOURCE_OPENDJ_OID); - - // THEN - - assertNoRepoThreadLocalCache(); - - displayJaxb("testResource result:", result, SchemaConstants.C_RESULT); - - TestUtil.assertSuccess("testResource has failed", result); - - OperationResult opResult = new OperationResult(TestSanity.class.getName() + ".test001TestConnectionOpenDJ"); - - PrismObject resourceOpenDjRepo = repositoryService.getObject(ResourceType.class, RESOURCE_OPENDJ_OID, null, opResult); - resourceTypeOpenDjrepo = resourceOpenDjRepo.asObjectable(); - - assertNoRepoThreadLocalCache(); - assertEquals(RESOURCE_OPENDJ_OID, resourceTypeOpenDjrepo.getOid()); - display("Initialized OpenDJ resource (respository)", resourceTypeOpenDjrepo); - assertNotNull("Resource schema was not generated", resourceTypeOpenDjrepo.getSchema()); - Element resourceOpenDjXsdSchemaElement = ResourceTypeUtil.getResourceXsdSchema(resourceTypeOpenDjrepo); - assertNotNull("Resource schema was not generated", resourceOpenDjXsdSchemaElement); - - PrismObject openDjResourceProvisioninig = provisioningService.getObject(ResourceType.class, RESOURCE_OPENDJ_OID, - null, null, opResult); - display("Initialized OpenDJ resource resource (provisioning)", openDjResourceProvisioninig); - - PrismObject openDjResourceModel = provisioningService.getObject(ResourceType.class, RESOURCE_OPENDJ_OID, null, null, opResult); - display("Initialized OpenDJ resource OpenDJ resource (model)", openDjResourceModel); - - checkOpenDjResource(resourceTypeOpenDjrepo, "repository"); - - System.out.println("------------------------------------------------------------------"); - displayValue("OpenDJ resource schema (repo XML)", DOMUtil.serializeDOMToString(ResourceTypeUtil.getResourceXsdSchema(resourceOpenDjRepo))); - System.out.println("------------------------------------------------------------------"); - - checkOpenDjResource(openDjResourceProvisioninig.asObjectable(), "provisioning"); - checkOpenDjResource(openDjResourceModel.asObjectable(), "model"); - // TODO: model web - } catch (Exception ex) { - logger.info("exception: " + ex); - throw ex; - } - - } - - private void checkRepoOpenDjResource() throws ObjectNotFoundException, SchemaException { - OperationResult result = new OperationResult(TestSanity.class.getName() + ".checkRepoOpenDjResource"); - PrismObject resource = repositoryService.getObject(ResourceType.class, RESOURCE_OPENDJ_OID, null, result); - checkOpenDjResource(resource.asObjectable(), "repository"); - } - - /** - * Checks if the resource is internally consistent, if it has everything it should have. - */ - private void checkOpenDjResource(ResourceType resource, String source) throws SchemaException { - assertNotNull("Resource from " + source + " is null", resource); - ObjectReferenceType connectorRefType = resource.getConnectorRef(); - assertNotNull("Resource from " + source + " has null connectorRef", connectorRefType); - assertFalse("Resource from " + source + " has no OID in connectorRef", StringUtils.isBlank(connectorRefType.getOid())); - assertNotNull("Resource from " + source + " has null description in connectorRef", connectorRefType.getDescription()); - assertNotNull("Resource from " + source + " has null filter in connectorRef", connectorRefType.getFilter()); - assertNotNull("Resource from " + source + " has null filter element in connectorRef", connectorRefType.getFilter().getFilterClauseXNode()); - assertNotNull("Resource from " + source + " has null configuration", resource.getConnectorConfiguration()); - assertNotNull("Resource from " + source + " has null schema", resource.getSchema()); - checkOpenDjSchema(resource, source); - assertNotNull("Resource from " + source + " has null schemahandling", resource.getSchemaHandling()); - checkOpenDjSchemaHandling(resource, source); - if (!source.equals("repository")) { - // This is generated on the fly in provisioning - assertNotNull("Resource from " + source + " has null nativeCapabilities", resource.getCapabilities().getNative()); - assertFalse("Resource from " + source + " has empty nativeCapabilities", - resource.getCapabilities().getNative().getAny().isEmpty()); - } - assertNotNull("Resource from " + source + " has null configured capabilities", resource.getCapabilities().getConfigured()); - assertFalse("Resource from " + source + " has empty capabilities", resource.getCapabilities().getConfigured().getAny().isEmpty()); - assertNotNull("Resource from " + source + " has null synchronization", resource.getSynchronization()); - checkOpenDjConfiguration(resource.asPrismObject(), source); - } - - private void checkOpenDjSchema(ResourceType resource, String source) throws SchemaException { - ResourceSchema schema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); - ObjectClassComplexTypeDefinition accountDefinition = schema.findObjectClassDefinition(RESOURCE_OPENDJ_ACCOUNT_OBJECTCLASS); - assertNotNull("Schema does not define any account (resource from " + source + ")", accountDefinition); - Collection identifiers = accountDefinition.getPrimaryIdentifiers(); - assertFalse("No account identifiers (resource from " + source + ")", identifiers == null || identifiers.isEmpty()); - // TODO: check for naming attributes and display names, etc - - ActivationCapabilityType capActivation = ResourceTypeUtil.getEffectiveCapability(resource, ActivationCapabilityType.class); - if (capActivation != null && capActivation.getStatus() != null && capActivation.getStatus().getAttribute() != null) { - // There is simulated activation capability, check if the attribute is in schema. - QName enableAttrName = capActivation.getStatus().getAttribute(); - ResourceAttributeDefinition enableAttrDef = accountDefinition.findAttributeDefinition(enableAttrName); - displayDumpable("Simulated activation attribute definition", enableAttrDef); - assertNotNull("No definition for enable attribute " + enableAttrName + " in account (resource from " + source + ")", enableAttrDef); - assertTrue("Enable attribute " + enableAttrName + " is not ignored (resource from " + source + ")", enableAttrDef.isIgnored()); - } - } - - private void checkOpenDjSchemaHandling(ResourceType resource, String source) { - SchemaHandlingType schemaHandling = resource.getSchemaHandling(); - for (ResourceObjectTypeDefinitionType resObjectTypeDef : schemaHandling.getObjectType()) { - if (resObjectTypeDef.getKind() == ShadowKindType.ACCOUNT) { - String name = resObjectTypeDef.getIntent(); - assertNotNull("Resource " + resource + " from " + source + " has an schemaHandlig account definition without intent", name); - assertNotNull("Account type " + name + " in " + resource + " from " + source + " does not have object class", resObjectTypeDef.getObjectClass()); - } - if (resObjectTypeDef.getKind() == ShadowKindType.ENTITLEMENT) { - String name = resObjectTypeDef.getIntent(); - assertNotNull("Resource " + resource + " from " + source + " has an schemaHandlig entitlement definition without intent", name); - assertNotNull("Entitlement type " + name + " in " + resource + " from " + source + " does not have object class", resObjectTypeDef.getObjectClass()); - } - } - } - - private void checkOpenDjConfiguration(PrismObject resource, String source) { - checkOpenResourceConfiguration(resource, CONNECTOR_LDAP_NAMESPACE, "bindPassword", 8, source); - } - - private void checkOpenResourceConfiguration(PrismObject resource, String connectorNamespace, String credentialsPropertyName, - int numConfigProps, String source) { - PrismContainer configurationContainer = resource.findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); - assertNotNull("No configuration container in " + resource + " from " + source, configurationContainer); - PrismContainer configPropsContainer = configurationContainer.findContainer(SchemaTestConstants.ICFC_CONFIGURATION_PROPERTIES); - assertNotNull("No configuration properties container in " + resource + " from " + source, configPropsContainer); - Collection> configProps = configPropsContainer.getValue().getItems(); - assertEquals("Wrong number of config properties in " + resource + " from " + source, numConfigProps, configProps.size()); - PrismProperty credentialsProp = configPropsContainer.findProperty(new ItemName(connectorNamespace, credentialsPropertyName)); - if (credentialsProp == null) { - // The is the heisenbug we are looking for. Just dump the entire damn thing. - displayValue("Configuration with the heisenbug", configurationContainer.debugDump()); - } - assertNotNull("No " + credentialsPropertyName + " property in " + resource + " from " + source, credentialsProp); - assertEquals("Wrong number of " + credentialsPropertyName + " property value in " + resource + " from " + source, 1, credentialsProp.getValues().size()); - PrismPropertyValue credentialsPropertyValue = credentialsProp.getValues().iterator().next(); - assertNotNull("No " + credentialsPropertyName + " property value in " + resource + " from " + source, credentialsPropertyValue); - if (credentialsPropertyValue.isRaw()) { - Object rawElement = credentialsPropertyValue.getRawElement(); - assertTrue("Wrong element class " + rawElement.getClass() + " in " + resource + " from " + source, rawElement instanceof MapXNode); -// Element rawDomElement = (Element)rawElement; - MapXNode xmap = (MapXNode) rawElement; - try { - ProtectedStringType protectedType = new ProtectedStringType(); - prismContext.hacks().parseProtectedType(protectedType, xmap, prismContext, prismContext.getDefaultParsingContext()); - // display("LDAP credentials raw element", DOMUtil.serializeDOMToString(rawDomElement)); -// assertEquals("Wrong credentials element namespace in "+resource+" from "+source, connectorNamespace, rawDomElement.getNamespaceURI()); -// assertEquals("Wrong credentials element local name in "+resource+" from "+source, credentialsPropertyName, rawDomElement.getLocalName()); -// Element encryptedDataElement = DOMUtil.getChildElement(rawDomElement, new QName(DOMUtil.NS_XML_ENC, "EncryptedData")); - EncryptedDataType encryptedDataType = protectedType.getEncryptedDataType(); - assertNotNull("No EncryptedData element", encryptedDataType); - } catch (SchemaException ex) { - throw new IllegalArgumentException(ex); - } -// assertEquals("Wrong EncryptedData element namespace in "+resource+" from "+source, DOMUtil.NS_XML_ENC, encryptedDataType.getNamespaceURI()); -// assertEquals("Wrong EncryptedData element local name in "+resource+" from "+source, "EncryptedData", encryptedDataType.getLocalName()); - } else { - Object credentials = credentialsPropertyValue.getValue(); - assertTrue("Wrong type of credentials configuration property in " + resource + " from " + source + ": " + credentials.getClass(), credentials instanceof ProtectedStringType); - ProtectedStringType credentialsPs = (ProtectedStringType) credentials; - EncryptedDataType encryptedData = credentialsPs.getEncryptedDataType(); - assertNotNull("No EncryptedData element", encryptedData); - } - - } - - @Test - public void test002AddDerbyResource() throws Exception { - // GIVEN - OperationResult result = createOperationResult(); - - checkRepoOpenDjResource(); - assertNoRepoThreadLocalCache(); - - PrismObject resource = PrismTestUtil.parseObject(new File(RESOURCE_DERBY_FILENAME)); - assertParentConsistency(resource); - fillInConnectorRef(resource, IntegrationTestTools.DBTABLE_CONNECTOR_TYPE, result); - - OperationResultType resultType = new OperationResultType(); - Holder resultHolder = new Holder<>(resultType); - Holder oidHolder = new Holder<>(); - - display("Adding Derby Resource", resource); - - // WHEN - addObjectViaModelWS(resource.asObjectable(), null, oidHolder, resultHolder); - - // THEN - // Check if Derby resource was imported correctly - PrismObject derbyResource = repositoryService.getObject(ResourceType.class, RESOURCE_DERBY_OID, null, result); - AssertJUnit.assertEquals(RESOURCE_DERBY_OID, derbyResource.getOid()); - - assertNoRepoThreadLocalCache(); - - String dbConnectorOid = derbyResource.asObjectable().getConnectorRef().getOid(); - PrismObject dbConnector = repositoryService.getObject(ConnectorType.class, dbConnectorOid, null, result); - display("DB Connector: ", dbConnector); - - // Check if password was encrypted during import - // via JAXB - Object configurationPropertiesElement = JAXBUtil.findElement(derbyResource.asObjectable().getConnectorConfiguration().getAny(), - new QName(dbConnector.asObjectable().getNamespace(), "configurationProperties")); - Object passwordElement = JAXBUtil.findElement(JAXBUtil.listChildElements(configurationPropertiesElement), - new QName(dbConnector.asObjectable().getNamespace(), "password")); - System.out.println("Password element: " + passwordElement); - - // via prisms - PrismContainerValue configurationProperties = derbyResource.findContainer( - ItemPath.create( - ResourceType.F_CONNECTOR_CONFIGURATION, - new QName("configurationProperties"))) - .getValue(); - PrismProperty password = configurationProperties.findProperty(new ItemName(dbConnector.asObjectable().getNamespace(), "password")); - System.out.println("Password property: " + password); - } - - private void addObjectViaModelWS(ObjectType objectType, ModelExecuteOptionsType options, Holder oidHolder, Holder resultHolder) throws FaultMessage { - ObjectDeltaListType deltaList = new ObjectDeltaListType(); - ObjectDeltaType objectDelta = new ObjectDeltaType(); - objectDelta.setObjectToAdd(objectType); - QName type = objectType.asPrismObject().getDefinition().getTypeName(); - objectDelta.setObjectType(type); - objectDelta.setChangeType(ChangeTypeType.ADD); - deltaList.getDelta().add(objectDelta); - ObjectDeltaOperationListType objectDeltaOperationListType = modelWeb.executeChanges(deltaList, options); - ObjectDeltaOperationType objectDeltaOperationType = getOdoFromDeltaOperationList(objectDeltaOperationListType, objectDelta); - resultHolder.value = objectDeltaOperationType.getExecutionResult(); - oidHolder.value = ((ObjectType) objectDeltaOperationType.getObjectDelta().getObjectToAdd()).getOid(); - } - - // ugly hack... - private static ObjectDeltaOperationType getOdoFromDeltaOperationList(ObjectDeltaOperationListType operationListType, ObjectDeltaType originalDelta) { - Validate.notNull(operationListType); - Validate.notNull(originalDelta); - for (ObjectDeltaOperationType operationType : operationListType.getDeltaOperation()) { - ObjectDeltaType objectDeltaType = operationType.getObjectDelta(); - if (originalDelta.getChangeType() == ChangeTypeType.ADD) { - if (objectDeltaType.getChangeType() == originalDelta.getChangeType() && - objectDeltaType.getObjectToAdd() != null) { - ObjectType objectAdded = (ObjectType) objectDeltaType.getObjectToAdd(); - if (objectAdded.getClass().equals(originalDelta.getObjectToAdd().getClass())) { - return operationType; - } - } - } else { - if (objectDeltaType.getChangeType() == originalDelta.getChangeType() && - originalDelta.getOid().equals(objectDeltaType.getOid())) { - return operationType; - } - } - } - throw new IllegalStateException("No suitable ObjectDeltaOperationType found"); - } - - private void checkRepoDerbyResource() throws ObjectNotFoundException, SchemaException { - OperationResult result = new OperationResult(TestSanity.class.getName() + ".checkRepoDerbyResource"); - PrismObject resource = repositoryService.getObject(ResourceType.class, RESOURCE_DERBY_OID, null, result); - checkDerbyResource(resource, "repository"); - } - - private void checkDerbyResource(PrismObject resource, String source) { - checkDerbyConfiguration(resource, source); - } - - private void checkDerbyConfiguration(PrismObject resource, String source) { - checkOpenResourceConfiguration(resource, CONNECTOR_DBTABLE_NAMESPACE, "password", 10, source); - } - - /** - * Test the testResource method. Expect a complete success for now. - */ - @Test - public void test003TestConnectionDerby() throws Exception { - // GIVEN - - checkRepoDerbyResource(); - assertNoRepoThreadLocalCache(); - - // WHEN - OperationResultType result = modelWeb.testResource(RESOURCE_DERBY_OID); - - // THEN - - assertNoRepoThreadLocalCache(); - displayJaxb("testResource result:", result, SchemaConstants.C_RESULT); - - TestUtil.assertSuccess("testResource has failed", result.getPartialResults().get(0)); - - OperationResult opResult = new OperationResult(TestSanity.class.getName() + ".test002TestConnectionDerby"); - - PrismObject rObject = repositoryService.getObject(ResourceType.class, RESOURCE_DERBY_OID, null, opResult); - resourceDerby = rObject.asObjectable(); - checkDerbyResource(rObject, "repository(after test)"); - - assertNoRepoThreadLocalCache(); - assertEquals(RESOURCE_DERBY_OID, resourceDerby.getOid()); - display("Initialized Derby resource (respository)", resourceDerby); - assertNotNull("Resource schema was not generated", resourceDerby.getSchema()); - Element resourceDerbyXsdSchemaElement = ResourceTypeUtil.getResourceXsdSchema(resourceDerby); - assertNotNull("Resource schema was not generated", resourceDerbyXsdSchemaElement); - - PrismObject derbyResourceProvisioninig = provisioningService.getObject(ResourceType.class, RESOURCE_DERBY_OID, - null, null, opResult); - display("Initialized Derby resource (provisioning)", derbyResourceProvisioninig); - - PrismObject derbyResourceModel = provisioningService.getObject(ResourceType.class, RESOURCE_DERBY_OID, - null, null, opResult); - display("Initialized Derby resource (model)", derbyResourceModel); - - // TODO: check -// checkOpenDjResource(resourceOpenDj,"repository"); -// checkOpenDjResource(openDjResourceProvisioninig,"provisioning"); -// checkOpenDjResource(openDjResourceModel,"model"); - // TODO: model web - - } - - @Test - public void test004Capabilities() - throws ObjectNotFoundException, SchemaException, FaultMessage { - // GIVEN - - checkRepoOpenDjResource(); - - assertNoRepoThreadLocalCache(); - - Holder resultHolder = new Holder<>(); - Holder objectHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - - // WHEN - modelWeb.getObject(ObjectTypes.RESOURCE.getTypeQName(), RESOURCE_OPENDJ_OID, - options, objectHolder, resultHolder); - - ResourceType resource = (ResourceType) objectHolder.value; - - // THEN - display("Resource", resource); - - assertNoRepoThreadLocalCache(); - - CapabilityCollectionType nativeCapabilities = resource.getCapabilities().getNative(); - List capabilities = nativeCapabilities.getAny(); - assertFalse("Empty capabilities returned", capabilities.isEmpty()); - - for (Object capability : nativeCapabilities.getAny()) { - System.out.println("Native Capability: " + CapabilityUtil.getCapabilityDisplayName(capability) + " : " + capability); - } - - if (resource.getCapabilities() != null) { - for (Object capability : resource.getCapabilities().getConfigured().getAny()) { - System.out.println("Configured Capability: " + CapabilityUtil.getCapabilityDisplayName(capability) + " : " + capability); - } - } - - List effectiveCapabilities = ResourceTypeUtil.getEffectiveCapabilities(resource); - for (Object capability : effectiveCapabilities) { - System.out.println("Efective Capability: " + CapabilityUtil.getCapabilityDisplayName(capability) + " : " + capability); - } - - CredentialsCapabilityType capCred = CapabilityUtil.getCapability(capabilities, CredentialsCapabilityType.class); - assertNotNull("password capability not present", capCred.getPassword()); - // Connector cannot do activation, this should be null - ActivationCapabilityType capAct = CapabilityUtil.getCapability(capabilities, ActivationCapabilityType.class); - assertNull("Found activation capability while not expecting it", capAct); - - capCred = ResourceTypeUtil.getEffectiveCapability(resource, CredentialsCapabilityType.class); - assertNotNull("password capability not found", capCred.getPassword()); - // Although connector does not support activation, the resource specifies a way how to simulate it. - // Therefore the following should succeed - capAct = ResourceTypeUtil.getEffectiveCapability(resource, ActivationCapabilityType.class); - assertNotNull("activation capability not found", capAct); - - } - - @Test - public void test005resolveConnectorRef() throws Exception { - PrismObject resource = PrismTestUtil.parseObject(new File(RESOURCE_DUMMY_FILENAME)); - - ModelExecuteOptionsType options = new ModelExecuteOptionsType(); - options.setIsImport(Boolean.TRUE); - addObjectViaModelWS(resource.asObjectable(), options, new Holder<>(), new Holder<>()); - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(ResourceType.class, RESOURCE_DUMMY_OID, null, repoResult); - assertNotNull(uObject); - - ResourceType resourceType = uObject.asObjectable(); - assertNotNull("Reference on the connector must not be null in resource.", resourceType.getConnectorRef()); - assertNotNull("Missing oid reference on the connector", resourceType.getConnectorRef().getOid()); - - } - - @Test - public void test006reimportResourceDummy() throws Exception { - //get object from repo (with version set and try to add it - it should be re-added, without error) - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject resource = repositoryService.getObject(ResourceType.class, RESOURCE_DUMMY_OID, null, repoResult); - assertNotNull(resource); - - ModelExecuteOptionsType options = new ModelExecuteOptionsType(); - options.setOverwrite(Boolean.TRUE); - options.setIsImport(Boolean.TRUE); - addObjectViaModelWS(resource.asObjectable(), options, new Holder<>(), new Holder<>()); - - //TODO: add some asserts - - //parse object from file again and try to add it - this should fail, becasue the same object already exists) - resource = PrismTestUtil.parseObject(new File(RESOURCE_DUMMY_FILENAME)); - - try { - Holder resultHolder = new Holder<>(); - options = new ModelExecuteOptionsType(); - options.setIsImport(Boolean.TRUE); - addObjectViaModelWS(resource.asObjectable(), options, new Holder<>(), - resultHolder); - - OperationResultType result = resultHolder.value; - TestUtil.assertFailure(result); - - fail("Expected object already exists exception, but haven't got one."); - } catch (FaultMessage ex) { - logger.info("fault {}", ex.getFaultInfo(), ex.getCause()); - if (ex.getFaultInfo() instanceof ObjectAlreadyExistsFaultType) { - // this is OK, we expect this - } else { - fail("Expected object already exists exception, but got: " + ex.getFaultInfo()); - } - - } - -// ResourceType resourceType = uObject.asObjectable(); -// assertNotNull("Reference on the connector must not be null in resource.",resourceType.getConnectorRef()); -// assertNotNull("Missing oid reference on the connector",resourceType.getConnectorRef().getOid()); - - } - - /** - * Attempt to add new user. It is only added to the repository, so check if - * it is in the repository after the operation. - */ - @Test - public void test010AddUser() throws Exception { - // GIVEN - checkRepoOpenDjResource(); - assertNoRepoThreadLocalCache(); - - PrismObject user = PrismTestUtil.parseObject(USER_JACK_FILE); - UserType userType = user.asObjectable(); - assertParentConsistency(user); - - // Encrypt Jack's password - protector.encrypt(userType.getCredentials().getPassword().getValue()); - assertParentConsistency(user); - - OperationResultType result = new OperationResultType(); - Holder resultHolder = new Holder<>(result); - Holder oidHolder = new Holder<>(); - - display("Adding user object", userType); - - // WHEN - addObjectViaModelWS(userType, null, oidHolder, resultHolder); - - // THEN - - assertNoRepoThreadLocalCache(); - displayJaxb("addObject result:", resultHolder.value, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("addObject has failed", resultHolder.value); - -// AssertJUnit.assertEquals(USER_JACK_OID, oid); - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - - repoResult.computeStatus(); - display("repository.getObject result", repoResult); - TestUtil.assertSuccess("getObject has failed", repoResult); - AssertJUnit.assertEquals(USER_JACK_OID, repoUser.getOid()); - assertEqualsPolyString("fullName", userType.getFullName(), repoUser.getFullName()); - - // TODO: better checks - } - - /** - * Add account to user. This should result in account provisioning. Check if - * that happens in repo and in LDAP. - */ - @Test - public void test013AddOpenDjAccountToUser() throws Exception { - try { - // GIVEN - checkRepoOpenDjResource(); - assertNoRepoThreadLocalCache(); - - // IMPORTANT! SWITCHING OFF ASSIGNMENT ENFORCEMENT HERE! - setAssignmentEnforcement(AssignmentPolicyEnforcementType.NONE); - // This is not redundant. It checks that the previous command set the policy correctly - assertSyncSettingsAssignmentPolicyEnforcement(AssignmentPolicyEnforcementType.NONE); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ADD_ACCOUNT_OPENDJ_FILENAME, ObjectDeltaType.class); - - // WHEN - when(); - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - then(); - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - display("User (repository)", repoUser); - - List accountRefs = repoUserType.getLinkRef(); - assertEquals("No accountRefs", 1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - accountShadowOidOpendj = accountRef.getOid(); - assertFalse(accountShadowOidOpendj.isEmpty()); - - // Check if shadow was created in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, - null, repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - display("Shadow (repository)", repoShadow); - assertNotNull(repoShadowType); - assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - assertNotNull("Shadow stored in repository has no name", repoShadowType.getName()); - // Check the "name" property, it should be set to DN, not entryUUID - assertEquals("Wrong name property", USER_JACK_LDAP_DN.toLowerCase(), repoShadowType.getName().getOrig().toLowerCase()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - String uid = checkRepoShadow(repoShadow); - - // check if account was created in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(uid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "jack"); - OpenDJController.assertAttribute(entry, "givenName", "Jack"); - OpenDJController.assertAttribute(entry, "sn", "Sparrow"); - OpenDJController.assertAttribute(entry, "cn", "Jack Sparrow"); - OpenDJController.assertAttribute(entry, "displayName", "Jack Sparrow"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Black Pearl"); - - assertTrue("LDAP account is not enabled", openDJController.isAccountEnabled(entry)); - - originalJacksLdapPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword was not set on create", originalJacksLdapPassword); - System.out.println("password after create: " + originalJacksLdapPassword); - - // Use getObject to test fetch of complete shadow - - assertNoRepoThreadLocalCache(); - - Holder resultHolder = new Holder<>(); - Holder objectHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - - // WHEN - modelWeb.getObject(ObjectTypes.SHADOW.getTypeQName(), accountShadowOidOpendj, - options, objectHolder, resultHolder); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("getObject result", resultHolder.value, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("getObject has failed", resultHolder.value); - - ShadowType modelShadow = (ShadowType) objectHolder.value; - display("Shadow (model)", modelShadow); - - AssertJUnit.assertNotNull(modelShadow); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, modelShadow.getResourceRef().getOid()); - - assertAttributeNotNull(modelShadow, getOpenDjPrimaryIdentifierQName()); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "uid", "jack"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "givenName", "Jack"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "sn", "Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "cn", "Jack Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "displayName", "Jack Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "l", "Black Pearl"); - assertNull("carLicense attribute sneaked to LDAP", OpenDJController.getAttributeValue(entry, "carLicense")); - assertNull("postalAddress attribute sneaked to LDAP", OpenDJController.getAttributeValue(entry, "postalAddress")); - - assertNotNull("Activation is null (model)", modelShadow.getActivation()); - assertEquals("Wrong administrativeStatus in the shadow (model)", ActivationStatusType.ENABLED, modelShadow.getActivation().getAdministrativeStatus()); - } catch (Exception ex) { - logger.info("ERROR", ex); - throw ex; - } - - } - - private OperationResultType modifyObjectViaModelWS(ObjectDeltaType objectChange) throws FaultMessage { - ObjectDeltaListType deltaList = new ObjectDeltaListType(); - deltaList.getDelta().add(objectChange); - ObjectDeltaOperationListType list = modelWeb.executeChanges(deltaList, null); - return getOdoFromDeltaOperationList(list, objectChange).getExecutionResult(); - } - - /** - * Add Derby account to user. This should result in account provisioning. Check if - * that happens in repo and in Derby. - */ - @Test - public void test014AddDerbyAccountToUser() throws IOException, JAXBException, FaultMessage, - ObjectNotFoundException, SchemaException, SQLException { - // GIVEN - - checkRepoDerbyResource(); - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ADD_ACCOUNT_DERBY_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUser.getLinkRef(); - // OpenDJ account was added in previous test, hence 2 accounts - assertEquals(2, accountRefs.size()); - - ObjectReferenceType accountRef = null; - for (ObjectReferenceType ref : accountRefs) { - if (!ref.getOid().equals(accountShadowOidOpendj)) { - accountRef = ref; - } - } - - accountShadowOidDerby = accountRef.getOid(); - assertFalse(accountShadowOidDerby.isEmpty()); - - // Check if shadow was created in the repo - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidDerby, - null, repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("addObject has failed", repoResult); - display("Shadow (repository)", repoShadowType); - assertNotNull(repoShadowType); - assertEquals(RESOURCE_DERBY_OID, repoShadowType.getResourceRef().getOid()); - - // Check the "name" property, it should be set to DN, not entryUUID - assertEquals("Wrong name property", PrismTestUtil.createPolyStringType(USER_JACK_DERBY_LOGIN), - repoShadowType.getName()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - String uid = checkRepoShadow(repoShadow); - - // check if account was created in DB Table - - Statement stmt = derbyController.getExecutedStatementWhereLoginName(uid); - ResultSet rs = stmt.getResultSet(); - - System.out.println("RS: " + rs); - - assertTrue("No records found for login name " + uid, rs.next()); - assertEquals(USER_JACK_DERBY_LOGIN, rs.getString(DerbyController.COLUMN_LOGIN)); - assertEquals("Cpt. Jack Sparrow", rs.getString(DerbyController.COLUMN_FULL_NAME)); - // TODO: check password - //assertEquals("3lizab3th",rs.getString(DerbyController.COLUMN_PASSWORD)); - System.out.println("Password: " + rs.getString(DerbyController.COLUMN_PASSWORD)); - - assertFalse("Too many records found for login name " + uid, rs.next()); - rs.close(); - stmt.close(); - - // Use getObject to test fetch of complete shadow - - assertNoRepoThreadLocalCache(); - - Holder resultHolder = new Holder<>(); - Holder objectHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - - // WHEN - modelWeb.getObject(ObjectTypes.SHADOW.getTypeQName(), accountShadowOidDerby, - options, objectHolder, resultHolder); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("getObject result", resultHolder.value, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("getObject has failed", resultHolder.value); - - ShadowType modelShadow = (ShadowType) objectHolder.value; - display("Shadow (model)", modelShadow); - - AssertJUnit.assertNotNull(modelShadow); - AssertJUnit.assertEquals(RESOURCE_DERBY_OID, modelShadow.getResourceRef().getOid()); - - assertAttribute(modelShadow, SchemaConstants.ICFS_UID, USER_JACK_DERBY_LOGIN); - assertAttribute(modelShadow, SchemaConstants.ICFS_NAME, USER_JACK_DERBY_LOGIN); - assertAttribute(resourceDerby, modelShadow, "FULL_NAME", "Cpt. Jack Sparrow"); - - } - - @Test - public void test015AccountOwner() throws FaultMessage, ObjectNotFoundException, SchemaException { - // GIVEN - checkRepoOpenDjResource(); - assertNoRepoThreadLocalCache(); - - Holder resultHolder = new Holder<>(); - Holder userHolder = new Holder<>(); - - // WHEN - - modelWeb.findShadowOwner(accountShadowOidOpendj, userHolder, resultHolder); - - // THEN - - display("listAccountShadowOwner result", resultHolder.value); - TestUtil.assertSuccess("listAccountShadowOwner has failed (result)", resultHolder.value); - UserType user = userHolder.value; - assertNotNull("No owner", user); - assertEquals(USER_JACK_OID, user.getOid()); - - System.out.println("Account " + accountShadowOidOpendj + " has owner " + ObjectTypeUtil.toShortString(user)); - } - - @Test - public void test016ProvisioningSearchAccountsIterative() throws Exception { - // GIVEN - OperationResult result = new OperationResult(TestSanity.class.getName() + ".test016ProvisioningSearchAccountsIterative"); - - RefinedResourceSchema refinedSchema = RefinedResourceSchemaImpl.getRefinedSchema(resourceTypeOpenDjrepo, prismContext); - final RefinedObjectClassDefinition refinedAccountDefinition = refinedSchema.getDefaultRefinedDefinition(ShadowKindType.ACCOUNT); - - QName objectClass = refinedAccountDefinition.getObjectClassDefinition().getTypeName(); - ObjectQuery q = ObjectQueryUtil.createResourceAndObjectClassQuery(resourceTypeOpenDjrepo.getOid(), objectClass, prismContext); - - final Collection objects = new HashSet<>(); - final MatchingRule caseIgnoreMatchingRule = matchingRuleRegistry.getMatchingRule(PrismConstants.STRING_IGNORE_CASE_MATCHING_RULE_NAME, DOMUtil.XSD_STRING); - ResultHandler handler = new ResultHandler() { - - @Override - public boolean handle(PrismObject prismObject, OperationResult parentResult) { - ObjectType objectType = prismObject.asObjectable(); - objects.add(objectType); - - display("Found object", objectType); - - assertTrue(objectType instanceof ShadowType); - ShadowType shadow = (ShadowType) objectType; - assertNotNull(shadow.getOid()); - assertNotNull(shadow.getName()); - assertEquals(RESOURCE_OPENDJ_ACCOUNT_OBJECTCLASS, shadow.getObjectClass()); - assertEquals(RESOURCE_OPENDJ_OID, shadow.getResourceRef().getOid()); - String icfUid = getAttributeValue(shadow, getOpenDjPrimaryIdentifierQName()); - assertNotNull("No ICF UID", icfUid); - String icfName = getNormalizedAttributeValue(shadow, refinedAccountDefinition, getOpenDjSecondaryIdentifierQName()); - assertNotNull("No ICF NAME", icfName); - try { - PrismAsserts.assertEquals("Wrong shadow name", caseIgnoreMatchingRule, shadow.getName().getOrig(), icfName); - } catch (SchemaException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - assertNotNull("Missing LDAP uid", getAttributeValue(shadow, new QName(ResourceTypeUtil.getResourceNamespace(resourceTypeOpenDjrepo), "uid"))); - assertNotNull("Missing LDAP cn", getAttributeValue(shadow, new QName(ResourceTypeUtil.getResourceNamespace(resourceTypeOpenDjrepo), "cn"))); - assertNotNull("Missing LDAP sn", getAttributeValue(shadow, new QName(ResourceTypeUtil.getResourceNamespace(resourceTypeOpenDjrepo), "sn"))); - assertNotNull("Missing activation", shadow.getActivation()); - assertNotNull("Missing activation status", shadow.getActivation().getAdministrativeStatus()); - return true; - } - }; - - // WHEN - - provisioningService.searchObjectsIterative(ShadowType.class, q, null, handler, null, result); - - // THEN - - displayValue("Count", objects.size()); - } - - /** - * We are going to modify the user. As the user has an account, the user - * changes should be also applied to the account (by schemaHandling). - */ - @Test - public void test020ModifyUser() throws Exception { - // GIVEN - - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_FULLNAME_LOCALITY_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - display("repository user", repoUser); - - PrismAsserts.assertEqualsPolyString("wrong value for fullName", "Cpt. Jack Sparrow", repoUserType.getFullName()); - PrismAsserts.assertEqualsPolyString("wrong value for locality", "somewhere", repoUserType.getLocality()); - assertEquals("wrong value for employeeNumber", "1", repoUserType.getEmployeeNumber()); - - // Check if appropriate accountRef is still there - - List accountRefs = repoUserType.getLinkRef(); - assertEquals(2, accountRefs.size()); - for (ObjectReferenceType accountRef : accountRefs) { - assertTrue("No OID in " + accountRef + " in " + repoUserType, - accountRef.getOid().equals(accountShadowOidOpendj) || - accountRef.getOid().equals(accountShadowOidDerby)); - - } - - // Check if shadow is still in the repo and that it is untouched - - repoResult = new OperationResult("getObject"); - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - display("repository shadow", repoShadow); - AssertJUnit.assertNotNull(repoShadow); - ShadowType repoShadowType = repoShadow.asObjectable(); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - - String uid = checkRepoShadow(repoShadow); - - // Check if LDAP account was updated - assertOpenDJAccountJack(uid, "jack"); - } - - private Entry assertOpenDJAccountJack(String entryUuid, String uid) throws DirectoryException { - Entry entry = openDJController.searchAndAssertByEntryUuid(entryUuid); - return assertOpenDJAccountJack(entry, uid); - } - - private Entry assertOpenDJAccountJack(Entry entry, String uid) throws DirectoryException { - return assertOpenDJAccountJack(entry, uid, "Jack"); - } - - private Entry assertOpenDJAccountJack(Entry entry, String uid, String givenName) throws DirectoryException { - display(entry); - - OpenDJController.assertDn(entry, "uid=" + uid + ",ou=people,dc=example,dc=com"); - OpenDJController.assertAttribute(entry, "uid", uid); - if (givenName == null) { - OpenDJController.assertAttribute(entry, "givenName"); - } else { - OpenDJController.assertAttribute(entry, "givenName", givenName); - } - OpenDJController.assertAttribute(entry, "sn", "Sparrow"); - // These two should be assigned from the User modification by - // schemaHandling - OpenDJController.assertAttribute(entry, "cn", "Cpt. Jack Sparrow"); - OpenDJController.assertAttribute(entry, "displayName", "Cpt. Jack Sparrow"); - // This will get translated from "somewhere" to this (outbound - // expression in schemeHandling) -> this is not more supported...we - // don't support complex run-time properties. the value will be - // evaluated from outbound expression - OpenDJController.assertAttribute(entry, "l", "somewhere"); - OpenDJController.assertAttribute(entry, "postalAddress", "Number 1"); - - return entry; - } - - /** - * We are going to change user's password. As the user has an account, the password change - * should be also applied to the account (by schemaHandling). - */ - @Test - public void test022ChangeUserPassword() throws Exception { - // GIVEN - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_PASSWORD_FILENAME, ObjectDeltaType.class); - - System.out.println("In modification: " + objectChange.getItemDelta().get(0).getValue().get(0)); - assertNoRepoThreadLocalCache(); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertUserPasswordChange("butUnd3dM4yT4lkAL0t", result); - } - - /** - * Similar to previous test just the request is constructed a bit differently. - */ - @Test - public void test023ChangeUserPasswordJAXB() throws Exception { - // GIVEN - final String NEW_PASSWORD = "abandonSHIP"; - Document doc = ModelClientUtil.getDocumnent(); - - ObjectDeltaType userDelta = new ObjectDeltaType(); - userDelta.setOid(USER_JACK_OID); - userDelta.setChangeType(ChangeTypeType.MODIFY); - userDelta.setObjectType(UserType.COMPLEX_TYPE); - - ItemDeltaType passwordDelta = new ItemDeltaType(); - passwordDelta.setModificationType(ModificationTypeType.REPLACE); - passwordDelta.setPath(ModelClientUtil.createItemPathType("credentials/password/value", prismContext)); - ProtectedStringType pass = new ProtectedStringType(); - pass.setClearValue(NEW_PASSWORD); - XNode passValue = prismContext.xnodeSerializer().root(new QName("dummy")).serializeRealValue(pass).getSubnode(); - System.out.println("PASSWORD VALUE: " + passValue.debugDump()); - RawType passwordValue = new RawType(passValue, prismContext); - passwordDelta.getValue().add(passwordValue); - userDelta.getItemDelta().add(passwordDelta); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(userDelta); - - // THEN - assertUserPasswordChange(NEW_PASSWORD, result); - } - - private void assertUserPasswordChange(String expectedUserPassword, OperationResultType result) throws ObjectNotFoundException, SchemaException, DirectoryException, EncryptionException { - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - display("repository user", repoUser); - - // Check if nothing else was modified - PrismAsserts.assertEqualsPolyString("wrong repo fullName", "Cpt. Jack Sparrow", repoUserType.getFullName()); - PrismAsserts.assertEqualsPolyString("wrong repo locality", "somewhere", repoUserType.getLocality()); - - // Check if appropriate accountRef is still there - assertLinks(repoUser, 2); - assertLinked(repoUser, accountShadowOidOpendj); - assertLinked(repoUser, accountShadowOidDerby); - - assertPassword(repoUser, expectedUserPassword); - - // Check if shadow is still in the repo and that it is untouched - repoResult = new OperationResult("getObject"); - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - display("repository shadow", repoShadow); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - AssertJUnit.assertNotNull(repoShadowType); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - String uid = checkRepoShadow(repoShadow); - - // Check if LDAP account was updated - Entry entry = assertOpenDJAccountJack(uid, "jack"); - - String ldapPasswordAfter = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull(ldapPasswordAfter); - - displayValue("LDAP password after change", ldapPasswordAfter); - - assertFalse("No change in password (original)", ldapPasswordAfter.equals(originalJacksLdapPassword)); - if (lastJacksLdapPassword != null) { - assertFalse("No change in password (last)", ldapPasswordAfter.equals(lastJacksLdapPassword)); - } - lastJacksLdapPassword = ldapPasswordAfter; - } - - @Test - public void test027ModifyAccountDj() throws Exception { - testModifyAccountDjRoomNumber(REQUEST_ACCOUNT_MODIFY_ROOM_NUMBER_FILE, "quarterdeck"); - } - - @Test - public void test028ModifyAccountDjExplicitType() throws Exception { - testModifyAccountDjRoomNumber(REQUEST_ACCOUNT_MODIFY_ROOM_NUMBER_EXPLICIT_TYPE_FILE, "upperdeck"); - } - - public void testModifyAccountDjRoomNumber(File reqFile, String expectedVal) throws Exception { - // GIVEN - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile(reqFile, ObjectDeltaType.class); - objectChange.setOid(accountShadowOidOpendj); - - // WHEN - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - display("repository shadow", repoShadow); - AssertJUnit.assertNotNull(repoShadow); - ShadowType repoShadowType = repoShadow.asObjectable(); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - - String uid = checkRepoShadow(repoShadow); - - // Check if LDAP account was updated - Entry jackLdapEntry = assertOpenDJAccountJack(uid, "jack"); - OpenDJController.assertAttribute(jackLdapEntry, "roomNumber", expectedVal); - } - - @Test - public void test029ModifyAccountDjBadPath() throws Exception { - // GIVEN - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_ACCOUNT_MODIFY_BAD_PATH_FILE, ObjectDeltaType.class); - objectChange.setOid(accountShadowOidOpendj); - - OperationResultType result; - try { - // WHEN - result = modifyObjectViaModelWS(objectChange); - - AssertJUnit.fail("Unexpected success"); - } catch (FaultMessage f) { - // this is expected - FaultType faultInfo = f.getFaultInfo(); - result = faultInfo.getOperationResult(); - } - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertFailure(result); - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - display("repository shadow", repoShadow); - AssertJUnit.assertNotNull(repoShadow); - ShadowType repoShadowType = repoShadow.asObjectable(); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - - String uid = checkRepoShadow(repoShadow); - - // Check if LDAP account was updated - Entry jackLdapEntry = assertOpenDJAccountJack(uid, "jack"); - OpenDJController.assertAttribute(jackLdapEntry, "roomNumber", "upperdeck"); - } - - /** - * Try to disable user. As the user has an account, the account should be disabled as well. - */ - @Test - public void test030DisableUser() throws Exception { - // GIVEN - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ACTIVATION_DISABLE_FILENAME, ObjectDeltaType.class); - - Entry entry = openDJController.searchByUid("jack"); - assertOpenDJAccountJack(entry, "jack"); - - String pwpAccountDisabled = OpenDJController.getAttributeValue(entry, "ds-pwp-account-disabled"); - displayValue("ds-pwp-account-disabled before change", pwpAccountDisabled); - assertTrue("LDAP account is not enabled (precondition)", openDJController.isAccountEnabled(entry)); - - assertNoRepoThreadLocalCache(); - - // WHEN - when(); - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - then(); - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - display("repository user", repoUser); - UserType repoUserType = repoUser.asObjectable(); - - // Check if nothing else was modified - assertEqualsPolyString("wrong repo fullName", "Cpt. Jack Sparrow", repoUserType.getFullName()); - assertEqualsPolyString("wrong repo locality", "somewhere", repoUserType.getLocality()); - - // Check if appropriate accountRef is still there - List accountRefs = repoUserType.getLinkRef(); - assertEquals(2, accountRefs.size()); - for (ObjectReferenceType accountRef : accountRefs) { - assertTrue("No OID in " + accountRef + " in " + repoUserType, - accountRef.getOid().equals(accountShadowOidOpendj) || - accountRef.getOid().equals(accountShadowOidDerby)); - } - - // Check if shadow is still in the repo and that it is untouched - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - display("repo shadow", repoShadow); - ShadowType repoShadowType = repoShadow.asObjectable(); - - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - AssertJUnit.assertNotNull(repoShadowType); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - String uid = checkRepoShadow(repoShadow); - - // Check if LDAP account was updated - - entry = openDJController.searchAndAssertByEntryUuid(uid); - assertOpenDJAccountJack(entry, "jack"); - - pwpAccountDisabled = OpenDJController.getAttributeValue(entry, "ds-pwp-account-disabled"); - displayValue("ds-pwp-account-disabled after change", pwpAccountDisabled); - assertFalse("LDAP account was not disabled", openDJController.isAccountEnabled(entry)); - - // Use getObject to test fetch of complete shadow - - Holder resultHolder = new Holder<>(); - Holder objectHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - assertNoRepoThreadLocalCache(); - - // WHEN - when(); - modelWeb.getObject(ObjectTypes.SHADOW.getTypeQName(), accountShadowOidOpendj, - options, objectHolder, resultHolder); - - // THEN - then(); - assertNoRepoThreadLocalCache(); - displayJaxb("getObject result", resultHolder.value, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("getObject has failed", resultHolder.value); - - ShadowType modelShadow = (ShadowType) objectHolder.value; - display("Shadow (model)", modelShadow); - - AssertJUnit.assertNotNull(modelShadow); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, modelShadow.getResourceRef().getOid()); - - assertAttributeNotNull(modelShadow, getOpenDjPrimaryIdentifierQName()); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "uid", "jack"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "givenName", "Jack"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "sn", "Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "cn", "Cpt. Jack Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "displayName", "Cpt. Jack Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "l", "somewhere"); - - assertNotNull("The account activation is null in the shadow", modelShadow.getActivation()); - assertNotNull("The account activation status was not present in shadow", modelShadow.getActivation().getAdministrativeStatus()); - assertEquals("The account was not disabled in the shadow", ActivationStatusType.DISABLED, modelShadow.getActivation().getAdministrativeStatus()); - - } - - /** - * Try to enable user after it has been disabled. As the user has an account, the account should be enabled as well. - */ - @Test - public void test031EnableUser() throws Exception { - // GIVEN - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ACTIVATION_ENABLE_FILENAME, ObjectDeltaType.class); - assertNoRepoThreadLocalCache(); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject uObject = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - display("repo user", repoUser); - - // Check if nothing else was modified - PrismAsserts.assertEqualsPolyString("wrong repo fullName", "Cpt. Jack Sparrow", repoUser.getFullName()); - PrismAsserts.assertEqualsPolyString("wrong repo locality", "somewhere", repoUser.getLocality()); - - // Check if appropriate accountRef is still there - List accountRefs = repoUser.getLinkRef(); - assertEquals(2, accountRefs.size()); - for (ObjectReferenceType accountRef : accountRefs) { - assertTrue("No OID in " + accountRef + " in " + repoUser, - accountRef.getOid().equals(accountShadowOidOpendj) || - accountRef.getOid().equals(accountShadowOidDerby)); - } - - // Check if shadow is still in the repo and that it is untouched - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, - null, repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - display("repo shadow", repoShadowType); - AssertJUnit.assertNotNull(repoShadowType); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - String uid = checkRepoShadow(repoShadow); - - // Use getObject to test fetch of complete shadow - - Holder resultHolder = new Holder<>(); - Holder objectHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - assertNoRepoThreadLocalCache(); - - // WHEN - modelWeb.getObject(ObjectTypes.SHADOW.getTypeQName(), accountShadowOidOpendj, - options, objectHolder, resultHolder); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("getObject result", resultHolder.value, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("getObject has failed", resultHolder.value); - - ShadowType modelShadow = (ShadowType) objectHolder.value; - display("Shadow (model)", modelShadow); - - AssertJUnit.assertNotNull(modelShadow); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, modelShadow.getResourceRef().getOid()); - - assertAttributeNotNull(modelShadow, getOpenDjPrimaryIdentifierQName()); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "uid", "jack"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "givenName", "Jack"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "sn", "Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "cn", "Cpt. Jack Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "displayName", "Cpt. Jack Sparrow"); - assertAttribute(resourceTypeOpenDjrepo, modelShadow, "l", "somewhere"); - - assertNotNull("The account activation is null in the shadow", modelShadow.getActivation()); - assertNotNull("The account activation status was not present in shadow", modelShadow.getActivation().getAdministrativeStatus()); - assertEquals("The account was not enabled in the shadow", ActivationStatusType.ENABLED, modelShadow.getActivation().getAdministrativeStatus()); - - // Check if LDAP account was updated - - Entry entry = openDJController.searchAndAssertByEntryUuid(uid); - assertOpenDJAccountJack(entry, "jack"); - - // The value of ds-pwp-account-disabled should have been removed - String pwpAccountDisabled = OpenDJController.getAttributeValue(entry, "ds-pwp-account-disabled"); - System.out.println("ds-pwp-account-disabled after change: " + pwpAccountDisabled); - assertTrue("LDAP account was not enabled", openDJController.isAccountEnabled(entry)); - } - - /** - * Unlink account by removing the accountRef from the user. - * The account will not be deleted, just the association to user will be broken. - */ - @Test - public void test040UnlinkDerbyAccountFromUser() - throws FaultMessage, ObjectNotFoundException, SchemaException { - // GIVEN - - ObjectDeltaType objectChange = new ObjectDeltaType(); - objectChange.setOid(USER_JACK_OID); - ItemDeltaType modificationDeleteAccountRef = new ItemDeltaType(); - modificationDeleteAccountRef.setModificationType(ModificationTypeType.DELETE); - ObjectReferenceType accountRefToDelete = new ObjectReferenceType(); - accountRefToDelete.setOid(accountShadowOidDerby); - RawType modificationValue = new RawType(prismContext.xnodeSerializer().root(new QName("dummy")).serializeRealValue(accountRefToDelete).getSubnode(), prismContext); - modificationDeleteAccountRef.getValue().add(modificationValue); - modificationDeleteAccountRef.setPath(new ItemPathType(UserType.F_LINK_REF)); - objectChange.getItemDelta().add(modificationDeleteAccountRef); - objectChange.setChangeType(ChangeTypeType.MODIFY); - objectChange.setObjectType(UserType.COMPLEX_TYPE); - displayJaxb("modifyObject input", objectChange, new QName(SchemaConstants.NS_C, "change")); - assertNoRepoThreadLocalCache(); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUser.getLinkRef(); - // only OpenDJ account should be left now - assertEquals(1, accountRefs.size()); - ObjectReferenceType ref = accountRefs.get(0); - assertEquals("Wrong OID in accountRef in " + repoUser, accountShadowOidOpendj, ref.getOid()); - - } - - /** - * Delete the shadow which will cause deletion of associated account. - * The account was unlinked in the previous test, therefore no operation with user is needed. - */ - @Test - public void test041DeleteDerbyAccount() - throws FaultMessage, SchemaException, SQLException { - // GIVEN - - assertNoRepoThreadLocalCache(); - - // WHEN - OperationResultType result = deleteObjectViaModelWS(ObjectTypes.SHADOW.getTypeQName(), accountShadowOidDerby); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("deleteObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("deleteObject has failed", result); - - // Check if shadow was deleted - OperationResult repoResult = new OperationResult("getObject"); - - try { - repositoryService.getObject(ShadowType.class, accountShadowOidDerby, - null, repoResult); - AssertJUnit.fail("Shadow was not deleted"); - } catch (ObjectNotFoundException ex) { - display("Caught expected exception from getObject(shadow): " + ex); - } - - // check if account was deleted in DB Table - - Statement stmt = derbyController.getExecutedStatementWhereLoginName(USER_JACK_DERBY_LOGIN); - ResultSet rs = stmt.getResultSet(); - - System.out.println("RS: " + rs); - - assertFalse("Account was not deleted in database", rs.next()); - - } - - private OperationResultType deleteObjectViaModelWS(QName typeQName, String oid) throws FaultMessage { - ObjectDeltaListType deltaList = new ObjectDeltaListType(); - ObjectDeltaType objectDelta = new ObjectDeltaType(); - objectDelta.setOid(oid); - objectDelta.setObjectType(typeQName); - objectDelta.setChangeType(ChangeTypeType.DELETE); - deltaList.getDelta().add(objectDelta); - ObjectDeltaOperationListType list = modelWeb.executeChanges(deltaList, null); - return getOdoFromDeltaOperationList(list, objectDelta).getExecutionResult(); - } - - @Test - public void test047RenameUser() throws Exception { - // GIVEN - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_NAME_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - display("repository user", repoUser); - - PrismAsserts.assertEqualsPolyString("wrong value for User name", "jsparrow", repoUserType.getName()); - PrismAsserts.assertEqualsPolyString("wrong value for User fullName", "Cpt. Jack Sparrow", repoUserType.getFullName()); - PrismAsserts.assertEqualsPolyString("wrong value for User locality", "somewhere", repoUserType.getLocality()); - assertEquals("wrong value for employeeNumber", "1", repoUserType.getEmployeeNumber()); - - // Check if appropriate accountRef is still there - - List accountRefs = repoUserType.getLinkRef(); - assertEquals(1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.iterator().next(); - assertEquals("Wrong OID in " + accountRef + " in " + repoUserType, - accountShadowOidOpendj, accountRef.getOid()); - - // Check if shadow is still in the repo and that it is untouched - - repoResult = new OperationResult("getObject"); - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - display("repository shadow", repoShadow); - AssertJUnit.assertNotNull(repoShadow); - ShadowType repoShadowType = repoShadow.asObjectable(); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - - String uid = checkRepoShadow(repoShadow); - - // Check if LDAP account was updated - assertOpenDJAccountJack(uid, "jsparrow"); - } - - /** - * We are going to modify the user. As the user has an account, the user - * changes should be also applied to the account (by schemaHandling). - */ - @Test - public void test048ModifyUserRemoveGivenName() throws Exception { - // GIVEN - assertNoRepoThreadLocalCache(); - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_GIVENNAME_FILENAME, ObjectDeltaType.class); - displayJaxb("objectChange:", objectChange, SchemaConstants.T_OBJECT_DELTA); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result:", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - display("repository user", repoUser); - - PrismAsserts.assertEqualsPolyString("wrong value for fullName", "Cpt. Jack Sparrow", repoUserType.getFullName()); - assertNull("Value for givenName still present", repoUserType.getGivenName()); - - // Check if appropriate accountRef is still there - - List accountRefs = repoUserType.getLinkRef(); - assertEquals(1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.iterator().next(); - accountRef.getOid().equals(accountShadowOidOpendj); - - // Check if shadow is still in the repo and that it is untouched - - repoResult = new OperationResult("getObject"); - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject(repo) has failed", repoResult); - display("repository shadow", repoShadow); - AssertJUnit.assertNotNull(repoShadow); - ShadowType repoShadowType = repoShadow.asObjectable(); - AssertJUnit.assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check attributes in the shadow: should be only identifiers (ICF UID) - - String uid = checkRepoShadow(repoShadow); - - // Check if LDAP account was updated - Entry entry = openDJController.searchAndAssertByEntryUuid(uid); - assertOpenDJAccountJack(entry, "jsparrow", null); - } - - /** - * The user should have an account now. Let's try to delete the user. The - * account should be gone as well. - */ - @Test - public void test049DeleteUser() throws SchemaException, FaultMessage, DirectoryException { - // GIVEN - - assertNoRepoThreadLocalCache(); - - // WHEN - OperationResultType result = deleteObjectViaModelWS(ObjectTypes.USER.getTypeQName(), USER_JACK_OID); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("deleteObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("deleteObject has failed", result); - - // User should be gone from the repository - OperationResult repoResult = new OperationResult("getObject"); - try { - repositoryService.getObject(UserType.class, USER_JACK_OID, null, repoResult); - AssertJUnit.fail("User still exists in repo after delete"); - } catch (ObjectNotFoundException e) { - // This is expected - } - - // Account shadow should be gone from the repository - repoResult = new OperationResult("getObject"); - try { - repositoryService.getObject(ShadowType.class, accountShadowOidOpendj, null, repoResult); - AssertJUnit.fail("Shadow still exists in repo after delete"); - } catch (ObjectNotFoundException e) { - // This is expected, but check also the result - AssertJUnit.assertFalse("getObject failed as expected, but the result indicates success", - repoResult.isSuccess()); - } - - // Account should be deleted from LDAP - InternalSearchOperation op = openDJController.getInternalConnection().processSearch( - "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, DereferencePolicy.NEVER_DEREF_ALIASES, 100, - 100, false, "(uid=" + USER_JACK_LDAP_UID + ")", null); - - AssertJUnit.assertEquals(0, op.getEntriesSent()); - - } - - @Test - public void test100AssignRolePirate() throws Exception { - // GIVEN - - // IMPORTANT! Assignment enforcement is FULL now - setAssignmentEnforcement(AssignmentPolicyEnforcementType.FULL); - // This is not redundant. It checks that the previous command set the policy correctly - assertSyncSettingsAssignmentPolicyEnforcement(AssignmentPolicyEnforcementType.FULL); - - PrismObject user = PrismTestUtil.parseObject(USER_GUYBRUSH_FILE); - UserType userType = user.asObjectable(); - - // Encrypt the password - protector.encrypt(userType.getCredentials().getPassword().getValue()); - - OperationResultType result = new OperationResultType(); - Holder resultHolder = new Holder<>(result); - Holder oidHolder = new Holder<>(); - assertNoRepoThreadLocalCache(); - - addObjectViaModelWS(userType, null, oidHolder, resultHolder); - - assertNoRepoThreadLocalCache(); - TestUtil.assertSuccess("addObject has failed", resultHolder.value); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ADD_ROLE_PIRATE_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUser.getLinkRef(); - assertEquals(1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - accountShadowOidGuybrushOpendj = accountRef.getOid(); - assertFalse(accountShadowOidGuybrushOpendj.isEmpty()); - - // Check if shadow was created in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - display("Shadow (repository)", repoShadowType); - assertNotNull(repoShadowType); - assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - accountGuybrushOpendjEntryUuuid = checkRepoShadow(repoShadow); - - // check if account was created in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - OpenDJController.assertAttribute(entry, "title", "Bloody Pirate"); - OpenDJController.assertAttribute(entry, "businessCategory", "loot", "murder"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword was not set on create", guybrushPassword); - - // TODO: Derby - - } - - @Test - public void test101AccountOwnerAfterRole() throws Exception { - // GIVEN - - assertNoRepoThreadLocalCache(); - - Holder resultHolder = new Holder<>(); - Holder userHolder = new Holder<>(); - - // WHEN - - modelWeb.findShadowOwner(accountShadowOidGuybrushOpendj, userHolder, resultHolder); - - // THEN - - TestUtil.assertSuccess("listAccountShadowOwner has failed (result)", resultHolder.value); - UserType user = userHolder.value; - assertNotNull("No owner", user); - assertEquals(USER_GUYBRUSH_OID, user.getOid()); - - System.out.println("Account " + accountShadowOidGuybrushOpendj + " has owner " + ObjectTypeUtil.toShortString(user)); - } - - @Test - public void test102AssignRoleCaptain() throws Exception { - // GIVEN - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ADD_ROLE_CAPTAIN_1_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUser.getLinkRef(); - assertEquals(1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - assertEquals(accountShadowOidGuybrushOpendj, accountRef.getOid()); - - // Check if shadow is still in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject aObject = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - ShadowType repoShadow = aObject.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - display("Shadow (repository)", repoShadow); - assertNotNull(repoShadow); - assertEquals(RESOURCE_OPENDJ_OID, repoShadow.getResourceRef().getOid()); - - // check if account is still in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - OpenDJController.assertAttribute(entry, "title", "Bloody Pirate", "Honorable Captain"); - OpenDJController.assertAttribute(entry, "carLicense", "C4PT41N"); - OpenDJController.assertAttribute(entry, "businessCategory", "loot", "murder", "cruise"); - // Expression in the role taking that from the user - OpenDJController.assertAttribute(entry, "destinationIndicator", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "departmentNumber", "Department of Guybrush"); - // Expression in the role taking that from the assignment - OpenDJController.assertAttribute(entry, "physicalDeliveryOfficeName", "The Sea Monkey"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword disappeared", guybrushPassword); - - // TODO: Derby - - } - - /** - * Assign the same "captain" role again, this time with a slightly different assignment parameters. - */ - @Test - public void test103AssignRoleCaptainAgain() throws Exception { - // GIVEN - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ADD_ROLE_CAPTAIN_2_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUser.getLinkRef(); - assertEquals(1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - assertEquals(accountShadowOidGuybrushOpendj, accountRef.getOid()); - - // Check if shadow is still in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject aObject = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - ShadowType repoShadow = aObject.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - display("Shadow (repository)", repoShadow); - assertNotNull(repoShadow); - assertEquals(RESOURCE_OPENDJ_OID, repoShadow.getResourceRef().getOid()); - - // check if account is still in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - OpenDJController.assertAttribute(entry, "title", "Bloody Pirate", "Honorable Captain"); - OpenDJController.assertAttribute(entry, "carLicense", "C4PT41N"); - OpenDJController.assertAttribute(entry, "businessCategory", "loot", "murder", "cruise"); - // Expression in the role taking that from the user - OpenDJController.assertAttribute(entry, "destinationIndicator", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "departmentNumber", "Department of Guybrush"); - // Expression in the role taking that from the assignments (both of them) - OpenDJController.assertAttribute(entry, "physicalDeliveryOfficeName", "The Sea Monkey", "The Dainty Lady"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword disappeared", guybrushPassword); - - // TODO: Derby - - } - - @Test - public void test105ModifyAccount() throws Exception { - // GIVEN - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_ACCOUNT_MODIFY_ATTRS_FILE, ObjectDeltaType.class); - objectChange.setOid(accountShadowOidGuybrushOpendj); - - // WHEN ObjectTypes.SHADOW.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - Task task = taskManager.createTaskInstance(); - OperationResult parentResult = createOperationResult("get after first modify"); - PrismObject shadow = modelService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, null, task, parentResult); - assertNotNull("shadow must not be null", shadow); - - ShadowType shadowType = shadow.asObjectable(); - QName employeeTypeQName = new QName(resourceTypeOpenDjrepo.getNamespace(), "employeeType"); - ItemPath employeeTypePath = ItemPath.create(ShadowType.F_ATTRIBUTES, employeeTypeQName); - PrismProperty item = shadow.findProperty(employeeTypePath); - - PropertyDelta deleteDelta = prismContext.deltaFactory().property().create(ShadowType.F_ATTRIBUTES, item.getDefinition().getItemName(), item.getDefinition()); -// PropertyDelta deleteDelta = PropertyDelta.createDelta(employeeTypePath, shadow.getDefinition()); -// PrismPropertyValue valToDelte = new PrismPropertyValue("A"); -// valToDelte.setParent(deleteDelta); - Collection> values = item.getValues(); - for (PrismPropertyValue val : values) { - if ("A".equals(val.getValue())) { - deleteDelta.addValueToDelete(val.clone()); - } - } - - ObjectDelta delta = prismContext.deltaFactory().object().create(ShadowType.class, ChangeType.MODIFY); - delta.addModification(deleteDelta); - delta.setOid(accountShadowOidGuybrushOpendj); - Collection> deltas = new ArrayList<>(); - deltas.add(delta); - logger.info("-------->>EXECUTE DELETE MODIFICATION<<------------"); - modelService.executeChanges(deltas, null, task, parentResult); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // check if LDAP account was modified - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - OpenDJController.assertAttribute(entry, "roomNumber", "captain's cabin"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - OpenDJController.assertAttribute(entry, "title", "Bloody Pirate", "Honorable Captain"); - OpenDJController.assertAttribute(entry, "carLicense", "C4PT41N"); - OpenDJController.assertAttribute(entry, "businessCategory", "loot", "murder", "cruise", "fighting", "capsize"); - // Expression in the role taking that from the user - OpenDJController.assertAttribute(entry, "destinationIndicator", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "departmentNumber", "Department of Guybrush"); - // Expression in the role taking that from the assignments (both of them) - OpenDJController.assertAttribute(entry, "physicalDeliveryOfficeName", "The Sea Monkey", "The Dainty Lady"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword disappeared", guybrushPassword); - - } - - /** - * Judge role excludes pirate role. This assignment should fail. - */ - @Test - public void test104AssignRoleJudge() throws Exception { - // GIVEN - OperationResultType result = new OperationResultType(); - Holder resultHolder = new Holder<>(result); - Holder oidHolder = new Holder<>(); - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ADD_ROLE_JUDGE_FILENAME, ObjectDeltaType.class); - try { - - // WHEN ObjectTypes.USER.getTypeQName(), - result = modifyObjectViaModelWS(objectChange); - - // THEN - AssertJUnit.fail("Expected a failure after assigning conflicting roles but nothing happened and life goes on"); - } catch (FaultMessage f) { - // This is expected - // TODO: check if the fault is the right one - } - - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object remain unmodified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUser.getLinkRef(); - assertEquals("Unexpected number or accountRefs", 1, accountRefs.size()); - - } - - @Test - public void test107UnassignRolePirate() throws Exception { - // GIVEN - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_DELETE_ROLE_PIRATE_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUserType.getLinkRef(); - assertEquals(1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - assertEquals(accountShadowOidGuybrushOpendj, accountRef.getOid()); - - // Check if shadow is still in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject aObject = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - ShadowType repoShadow = aObject.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - display("Shadow (repository)", repoShadow); - assertNotNull(repoShadow); - assertEquals(RESOURCE_OPENDJ_OID, repoShadow.getResourceRef().getOid()); - - // check if account is still in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - OpenDJController.assertAttribute(entry, "title", "Honorable Captain"); - OpenDJController.assertAttribute(entry, "carLicense", "C4PT41N"); - OpenDJController.assertAttribute(entry, "businessCategory", "cruise", "fighting", "capsize"); - // Expression in the role taking that from the user - OpenDJController.assertAttribute(entry, "destinationIndicator", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "departmentNumber", "Department of Guybrush"); - // Expression in the role taking that from the assignments (both of them) - OpenDJController.assertAttribute(entry, "physicalDeliveryOfficeName", "The Sea Monkey", "The Dainty Lady"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword disappeared", guybrushPassword); - - // TODO: Derby - - } - - @Test - public void test108UnassignRoleCaptain() throws Exception { - // GIVEN - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_DELETE_ROLE_CAPTAIN_1_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUser); - - List accountRefs = repoUserType.getLinkRef(); - assertEquals(1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - assertEquals(accountShadowOidGuybrushOpendj, accountRef.getOid()); - - // Check if shadow is still in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - display("Shadow (repository)", repoShadow); - assertNotNull(repoShadowType); - assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - // check if account is still in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - OpenDJController.assertAttribute(entry, "title", "Honorable Captain"); - OpenDJController.assertAttribute(entry, "carLicense", "C4PT41N"); - OpenDJController.assertAttribute(entry, "businessCategory", "cruise", "fighting", "capsize"); - // Expression in the role taking that from the user - OpenDJController.assertAttribute(entry, "destinationIndicator", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "departmentNumber", "Department of Guybrush"); - // Expression in the role taking that from the assignments (both of them) - OpenDJController.assertAttribute(entry, "physicalDeliveryOfficeName", "The Dainty Lady"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword disappeared", guybrushPassword); - - // TODO: Derby - - } - - /** - * Captain role was assigned twice. It has to also be unassigned twice. - */ - @Test - public void test109UnassignRoleCaptainAgain() throws Exception { - // GIVEN - - assertNoRepoThreadLocalCache(); - - ObjectDeltaType objectChange = unmarshalValueFromFile( - REQUEST_USER_MODIFY_DELETE_ROLE_CAPTAIN_2_FILENAME, ObjectDeltaType.class); - - // WHEN ObjectTypes.USER.getTypeQName(), - OperationResultType result = modifyObjectViaModelWS(objectChange); - - // THEN - assertNoRepoThreadLocalCache(); - displayJaxb("modifyObject result", result, SchemaConstants.C_RESULT); - - //TODO TODO TODO TODO operation result from repostiory.getObject is unknown...find out why.. -// assertSuccess("modifyObject has failed", result); - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject repoUser = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUserType = repoUser.asObjectable(); - repoResult.computeStatus(); - display("User (repository)", repoUserType); - - List accountRefs = repoUserType.getLinkRef(); - assertEquals(0, accountRefs.size()); - - // Check if shadow was deleted from the repo - - repoResult = new OperationResult("getObject"); - - try { - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - AssertJUnit.fail("Account shadow was not deleted from repo"); - } catch (ObjectNotFoundException ex) { - // This is expected - } - - // check if account was deleted from LDAP - - Entry entry = openDJController.searchByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - assertNull("LDAP account was not deleted", entry); - - // TODO: Derby - - } - - // Synchronization tests - - /** - * Test initialization of synchronization. It will create a cycle task and - * check if the cycle executes No changes are synchronized yet. - */ - @Test - public void test300LiveSyncInit() throws Exception { - // Now it is the right time to add task definition to the repository - // We don't want it there any sooner, as it may interfere with the - // previous tests - - checkAllShadows(); - - // IMPORTANT! Assignment enforcement is POSITIVE now - setAssignmentEnforcement(AssignmentPolicyEnforcementType.POSITIVE); - // This is not redundant. It checks that the previous command set the policy correctly - assertSyncSettingsAssignmentPolicyEnforcement(AssignmentPolicyEnforcementType.POSITIVE); - - final OperationResult result = createOperationResult(); - - repoAddObjectFromFile(TASK_OPENDJ_SYNC_FILENAME, result); - - // We need to wait for a sync interval, so the task scanner has a chance - // to pick up this - // task - - waitFor("Waiting for task manager to pick up the task", new Checker() { - public boolean check() throws ObjectNotFoundException, SchemaException { - Task task = taskManager.getTaskPlain(TASK_OPENDJ_SYNC_OID, result); - display("Task while waiting for task manager to pick up the task", task); - // wait until the task is picked up - return task.getLastRunFinishTimestamp() != null; - } - - @Override - public void timeout() { - // No reaction, the test will fail right after return from this - } - }, 20000); - - // Check task status - - Task task = taskManager.getTaskWithResult(TASK_OPENDJ_SYNC_OID, result); - result.computeStatus(); - display("getTask result", result); - TestUtil.assertSuccess("getTask has failed", result); - AssertJUnit.assertNotNull(task); - display("Task after pickup", task); - - PrismObject o = repositoryService.getObject(TaskType.class, TASK_OPENDJ_SYNC_OID, null, result); - display("Task after pickup in the repository", o.asObjectable()); - - // .. it should be running - AssertJUnit.assertEquals(TaskExecutionStatus.RUNNABLE, task.getExecutionStatus()); - - // .. and claimed -// AssertJUnit.assertEquals(TaskExclusivityStatus.CLAIMED, task.getExclusivityStatus()); - - // .. and last run should not be zero - assertNotNull("No lastRunStartTimestamp", task.getLastRunStartTimestamp()); - assertFalse("Zero lastRunStartTimestamp", task.getLastRunStartTimestamp().longValue() == 0); - assertNotNull("No lastRunFinishedTimestamp", task.getLastRunFinishTimestamp()); - assertFalse("Zero lastRunFinishedTimestamp", task.getLastRunFinishTimestamp().longValue() == 0); - - // Test for extension. This will also roughly test extension processor - // and schema processor - PrismContainer taskExtension = task.getExtensionOrClone(); - AssertJUnit.assertNotNull(taskExtension); - display("Task extension", taskExtension); - PrismProperty shipStateProp = taskExtension.findProperty(MY_SHIP_STATE); - AssertJUnit.assertEquals("Wrong 'shipState' property value", "capsized", shipStateProp.getValue().getValue()); - PrismProperty deadProp = taskExtension.findProperty(MY_DEAD); - PrismPropertyValue deadPVal = deadProp.getValues().iterator().next(); - AssertJUnit.assertEquals("Wrong 'dead' property class", Integer.class, deadPVal.getValue().getClass()); - AssertJUnit.assertEquals("Wrong 'dead' property value", Integer.valueOf(42), deadPVal.getValue()); - - // The progress should be 0, as there were no changes yet - AssertJUnit.assertEquals(0, task.getProgress()); - - // Test for presence of a result. It should be there and it should - // indicate success - OperationResult taskResult = task.getResult(); - AssertJUnit.assertNotNull(taskResult); - - assertTrue("Task result is not a success, it is " + taskResult, taskResult.isSuccess()); - - final Object tokenAfter = findSyncToken(task); - displayValue("Sync token after", tokenAfter.toString()); - lastSyncToken = (Integer) tokenAfter; - - checkAllShadows(); - - // Try without options. The results should NOT be there - // MID-4670 - task = taskManager.getTaskPlain(TASK_OPENDJ_SYNC_OID, null, result); - taskResult = task.getResult(); - AssertJUnit.assertNull("Unexpected task result", taskResult); - - } - - /** - * Create LDAP object. That should be picked up by liveSync and a user - * should be created in repo. - */ - @Test - public void test301LiveSyncCreate() throws Exception { - // Sync task should be running (tested in previous test), so just create - // new LDAP object. - - final OperationResult result = createOperationResult(); - final Task syncCycle = taskManager.getTaskPlain(TASK_OPENDJ_SYNC_OID, result); - AssertJUnit.assertNotNull(syncCycle); - - final Object tokenBefore = findSyncToken(syncCycle); - displayValue("Sync token before", tokenBefore.toString()); - - // WHEN - when(); - - Entry entry = openDJController.addEntryFromLdifFile(LDIF_WILL_FILENAME); - display("Entry from LDIF", entry); - - // Wait a bit to give the sync cycle time to detect the change - basicWaitForSyncChangeDetection(syncCycle, tokenBefore, 2, result); - - // THEN - then(); - // Search for the user that should be created now - UserType user = searchUserByName(WILL_NAME); - - PrismAsserts.assertEqualsPolyString("Wrong name.", WILL_NAME, user.getName()); - assertNotNull(user.getLinkRef()); - assertFalse(user.getLinkRef().isEmpty()); -// AssertJUnit.assertEquals(user.getName(), WILL_NAME); - - // TODO: more checks - - assertAndStoreSyncTokenIncrement(syncCycle, 2); - checkAllShadows(); - } - - @Test - public void test302LiveSyncModify() throws Exception { - final OperationResult result = createOperationResult(); - final Task syncCycle = taskManager.getTaskPlain(TASK_OPENDJ_SYNC_OID, result); - AssertJUnit.assertNotNull(syncCycle); - - int tokenBefore = findSyncToken(syncCycle); - displayValue("Sync token before", tokenBefore); - - // WHEN - display("Modifying LDAP entry"); - ChangeRecordEntry entry = openDJController.executeLdifChange(LDIF_WILL_MODIFY_FILE); - - // THEN - displayValue("Entry from LDIF", entry); - - // Wait a bit to give the sync cycle time to detect the change - basicWaitForSyncChangeDetection(syncCycle, tokenBefore, 1, result); - // Search for the user that should be created now - UserType user = searchUserByName(WILL_NAME); - -// AssertJUnit.assertEquals(WILL_NAME, user.getName()); - PrismAsserts.assertEqualsPolyString("Wrong name.", WILL_NAME, user.getName()); - PrismAsserts.assertEqualsPolyString("wrong givenName", "asdf", user.getGivenName()); - - assertAndStoreSyncTokenIncrement(syncCycle, 1); - checkAllShadows(); - } - - @Test - public void test303LiveSyncLink() throws Exception { - // GIVEN - assertNoRepoThreadLocalCache(); - PrismObject user = PrismTestUtil.parseObject(USER_E_LINK_ACTION_FILE); - UserType userType = user.asObjectable(); - final String userOid = userType.getOid(); - // Encrypt e's password - protector.encrypt(userType.getCredentials().getPassword().getValue()); - // create user in repository - OperationResultType resultType = new OperationResultType(); - Holder resultHolder = new Holder<>(resultType); - Holder oidHolder = new Holder<>(); - display("Adding user object", userType); - addObjectViaModelWS(userType, null, oidHolder, resultHolder); - //check results - assertNoRepoThreadLocalCache(); - displayJaxb("addObject result:", resultHolder.value, SchemaConstants.C_RESULT); - TestUtil.assertSuccess("addObject has failed", resultHolder.value); -// AssertJUnit.assertEquals(userOid, oidHolder.value); - - //WHEN - when(); - - //create account for e which should be correlated - final OperationResult result = createOperationResult(); - final Task syncCycle = taskManager.getTaskPlain(TASK_OPENDJ_SYNC_OID, result); - AssertJUnit.assertNotNull(syncCycle); - - int tokenBefore = findSyncToken(syncCycle); - displayValue("Sync token before", tokenBefore); - - Entry entry = openDJController.addEntryFromLdifFile(LDIF_E_FILENAME_LINK); - display("Entry from LDIF", entry); - - // Wait a bit to give the sync cycle time to detect the change - basicWaitForSyncChangeDetection(syncCycle, tokenBefore, 1, result); - - // THEN - then(); - - //check user and account ref - userType = searchUserByName("e"); - - List accountRefs = userType.getLinkRef(); - assertEquals("Account ref not found, or found too many", 1, accountRefs.size()); - - //check account defined by account ref - String accountOid = accountRefs.get(0).getOid(); - ShadowType account = searchAccountByOid(accountOid); - - assertEqualsPolyString("Name doesn't match", "uid=e,ou=People,dc=example,dc=com", account.getName()); - - assertAndStoreSyncTokenIncrement(syncCycle, 1); - checkAllShadows(); - } - - /** - * Create LDAP object. That should be picked up by liveSync and a user - * should be created in repo. - * Also location (ldap l) should be updated through outbound - */ - @Test - public void test304LiveSyncCreateNoLocation() throws Exception { - // Sync task should be running (tested in previous test), so just create - // new LDAP object. - - final OperationResult result = createOperationResult(); - final Task syncCycle = taskManager.getTaskPlain(TASK_OPENDJ_SYNC_OID, result); - AssertJUnit.assertNotNull(syncCycle); - - int tokenBefore = findSyncToken(syncCycle); - displayValue("Sync token before", tokenBefore); - - // WHEN - Entry entry = openDJController.addEntryFromLdifFile(LDIF_WILL_WITHOUT_LOCATION_FILENAME); - display("Entry from LDIF", entry); - - // THEN - // Wait a bit to give the sync cycle time to detect the change - basicWaitForSyncChangeDetection(syncCycle, tokenBefore, 3, result, 60000); - // Search for the user that should be created now - final String userName = "wturner1"; - UserType user = searchUserByName(userName); - - List accountRefs = user.getLinkRef(); - assertEquals("Account ref not found, or found too many", 1, accountRefs.size()); - - //check account defined by account ref - String accountOid = accountRefs.get(0).getOid(); - ShadowType account = searchAccountByOid(accountOid); - - assertEqualsPolyString("Name doesn't match", "uid=" + userName + ",ou=People,dc=example,dc=com", account.getName()); -// assertEquals("Name doesn't match", "uid=" + userName + ",ou=People,dc=example,dc=com", account.getName()); - Collection localities = getAttributeValues(account, new QName(RESOURCE_OPENDJ_ACCOUNT_OBJECTCLASS.getNamespaceURI(), "l")); - assertNotNull("null value list for attribute 'l'", localities); - assertEquals("unexpected number of values of attribute 'l'", 1, localities.size()); - assertEquals("Locality doesn't match", "middle of nowhere", localities.iterator().next()); - - assertAndStoreSyncTokenIncrement(syncCycle, 3); - checkAllShadows(); - } - - private void assertAndStoreSyncTokenIncrement(Task syncCycle, int increment) { - final Object tokenAfter = findSyncToken(syncCycle); - displayValue("Sync token after", tokenAfter.toString()); - int tokenAfterInt = (Integer) tokenAfter; - int expectedToken = lastSyncToken + increment; - lastSyncToken = tokenAfterInt; - assertEquals("Unexpected sync toke value", expectedToken, tokenAfterInt); - } - - private int findSyncToken(Task syncCycle) { - return (Integer) findSyncTokenObject(syncCycle); - } - - private Object findSyncTokenObject(Task syncCycle) { - Object token = null; - PrismProperty tokenProperty = syncCycle.getExtensionOrClone().findProperty(SchemaConstants.SYNC_TOKEN); - if (tokenProperty != null) { - Collection values = tokenProperty.getRealValues(); - if (values.size() > 1) { - throw new IllegalStateException("Too must values in token " + tokenProperty); - } - token = values.iterator().next(); - } - - return token; - } - - /** - * Not really a test. Just cleans up after live sync. - */ - @Test - public void test399LiveSyncCleanup() throws Exception { - final OperationResult result = createOperationResult(); - - taskManager.deleteTask(TASK_OPENDJ_SYNC_OID, result); - - // TODO: check if the task is really stopped - } - - @Test - public void test400ImportFromResource() throws Exception { - // GIVEN - checkAllShadows(); - assertNoRepoThreadLocalCache(); - - OperationResult result = createOperationResult(); - - // Make sure Mr. Gibbs has "l" attribute set to the same value as an outbound expression is setting - ChangeRecordEntry entry = openDJController.executeLdifChange(LDIF_GIBBS_MODIFY_FILE); - displayValue("Entry from LDIF", entry); - - // Let's add an entry with multiple uids. - Entry addEntry = openDJController.addEntryFromLdifFile(LDIF_HERMAN_FILENAME); - display("Entry from LDIF", addEntry); - - // WHEN - when(); - TaskType taskType = modelWeb.importFromResource(RESOURCE_OPENDJ_OID, RESOURCE_OPENDJ_ACCOUNT_OBJECTCLASS); - - // THEN - then(); - assertNoRepoThreadLocalCache(); - displayJaxb("importFromResource result", taskType.getResult(), SchemaConstants.C_RESULT); - AssertJUnit.assertEquals("importFromResource has failed", OperationResultStatusType.IN_PROGRESS, taskType.getResult().getStatus()); - // Convert the returned TaskType to a more usable Task - Task task = taskManager.createTaskInstance(taskType.asPrismObject(), result); - AssertJUnit.assertNotNull(task); - assertNotNull(task.getOid()); - AssertJUnit.assertTrue(task.isAsynchronous()); - AssertJUnit.assertEquals(TaskExecutionStatus.RUNNABLE, task.getExecutionStatus()); -// AssertJUnit.assertEquals(TaskExclusivityStatus.CLAIMED, task.getExclusivityStatus()); - - display("Import task after launch", task); - - PrismObject tObject = repositoryService.getObject(TaskType.class, task.getOid(), null, result); - TaskType taskAfter = tObject.asObjectable(); - display("Import task in repo after launch", taskAfter); - - result.computeStatus(); - TestUtil.assertSuccess("getObject has failed", result); - - final String taskOid = task.getOid(); - - waitFor("Waiting for import to complete", new Checker() { - @Override - public boolean check() throws CommonException { - Holder resultHolder = new Holder<>(); - Holder objectHolder = new Holder<>(); - OperationResult opResult = new OperationResult("import check"); - assertNoRepoThreadLocalCache(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - try { - modelWeb.getObject(ObjectTypes.TASK.getTypeQName(), taskOid, - options, objectHolder, resultHolder); - } catch (FaultMessage faultMessage) { - throw new SystemException(faultMessage); - } - assertNoRepoThreadLocalCache(); - // display("getObject result (wait loop)",resultHolder.value); - TestUtil.assertSuccess("getObject has failed", resultHolder.value); - Task task = taskManager.createTaskInstance((PrismObject) objectHolder.value.asPrismObject(), opResult); - System.out.println(new Date() + ": Import task status: " + task.getExecutionStatus() + ", progress: " + task.getProgress()); - if (task.getExecutionStatus() == TaskExecutionStatus.CLOSED) { - // Task closed, wait finished - return true; - } - // IntegrationTestTools.display("Task result while waiting: ", task.getResult()); - return false; - } - - @Override - public void timeout() { - // No reaction, the test will fail right after return from this - } - }, 180000); - - // wait a second until the task will be definitely saved - Thread.sleep(1000); - - //### Check task state after the task is finished ### - - Holder objectHolder = new Holder<>(); - Holder resultHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - assertNoRepoThreadLocalCache(); - - modelWeb.getObject(ObjectTypes.TASK.getTypeQName(), task.getOid(), - options, objectHolder, resultHolder); - - assertNoRepoThreadLocalCache(); - TestUtil.assertSuccess("getObject has failed", resultHolder.value); - task = taskManager.createTaskInstance((PrismObject) objectHolder.value.asPrismObject(), result); - - display("Import task after finish (fetched from model)", task); - - AssertJUnit.assertEquals(TaskExecutionStatus.CLOSED, task.getExecutionStatus()); - - assertNotNull("Null lastRunStartTimestamp in " + task, task.getLastRunStartTimestamp()); - assertNotNull("Null lastRunFinishTimestamp in " + task, task.getLastRunFinishTimestamp()); - - long importDuration = task.getLastRunFinishTimestamp() - task.getLastRunStartTimestamp(); - double usersPerSec = (task.getProgress() * 1000) / importDuration; - display("Imported " + task.getProgress() + " users in " + importDuration + " milliseconds (" + usersPerSec + " users/sec)"); - - OperationResultStatusType taskResultStatus = task.getResultStatus(); - AssertJUnit.assertNotNull("Task has no result status", taskResultStatus); - assertEquals("Import task result is not success", OperationResultStatusType.SUCCESS, taskResultStatus); - - AssertJUnit.assertTrue("No progress", task.getProgress() > 0); - - //### Check if the import created users and shadows ### - - // Listing of shadows is not supported by the provisioning. So we need - // to look directly into repository - List> sobjects = repositoryService.searchObjects(ShadowType.class, null, null, result); - result.computeStatus(); - TestUtil.assertSuccess("listObjects has failed", result); - AssertJUnit.assertFalse("No shadows created", sobjects.isEmpty()); - - for (PrismObject aObject : sobjects) { - ShadowType shadow = aObject.asObjectable(); - display("Shadow object after import (repo)", shadow); - assertNotEmpty("No OID in shadow", shadow.getOid()); // This would be really strange ;-) - assertNotEmpty("No name in shadow", shadow.getName()); - AssertJUnit.assertNotNull("No objectclass in shadow", shadow.getObjectClass()); - AssertJUnit.assertNotNull("Null attributes in shadow", shadow.getAttributes()); - String resourceOid = shadow.getResourceRef().getOid(); - if (resourceOid.equals(RESOURCE_OPENDJ_OID)) { - assertAttributeNotNull("No identifier in shadow", shadow, getOpenDjPrimaryIdentifierQName()); - } else { - assertAttributeNotNull("No UID in shadow", shadow, SchemaConstants.ICFS_UID); - } - } - - Holder listHolder = new Holder<>(); - assertNoRepoThreadLocalCache(); - - modelWeb.searchObjects(ObjectTypes.USER.getTypeQName(), null, null, - listHolder, resultHolder); - - assertNoRepoThreadLocalCache(); - ObjectListType uobjects = listHolder.value; - TestUtil.assertSuccess("listObjects has failed", resultHolder.value); - AssertJUnit.assertFalse("No users created", uobjects.getObject().isEmpty()); - - // TODO: use another account, not guybrush - - display("Users after import " + uobjects.getObject().size()); - - for (ObjectType oo : uobjects.getObject()) { - UserType user = (UserType) oo; - if (SystemObjectsType.USER_ADMINISTRATOR.value().equals(user.getOid())) { - //skip administrator check - continue; - } - display("User after import (repo)", user); - assertNotEmpty("No OID in user", user.getOid()); // This would be - // really - // strange ;-) - assertNotEmpty("No name in user", user.getName()); - assertNotNull("No fullName in user", user.getFullName()); - assertNotEmpty("No fullName in user", user.getFullName().getOrig()); - assertNotEmpty("No familyName in user", user.getFamilyName().getOrig()); - // givenName is not mandatory in LDAP, therefore givenName may not - // be present on user - - if (user.getName().getOrig().equals(USER_GUYBRUSH_USERNAME)) { - // skip the rest of checks for guybrush, he does not have LDAP account now - continue; - } - - assertTrue("User " + user.getName() + " is disabled (" + user.getActivation().getAdministrativeStatus() + ")", user.getActivation() == null || - user.getActivation().getAdministrativeStatus() == ActivationStatusType.ENABLED); - - List accountRefs = user.getLinkRef(); - AssertJUnit.assertEquals("Wrong accountRef for user " + user.getName(), 1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - - boolean found = false; - for (PrismObject aObject : sobjects) { - ShadowType acc = aObject.asObjectable(); - if (accountRef.getOid().equals(acc.getOid())) { - found = true; - break; - } - } - if (!found) { - AssertJUnit.fail("accountRef does not point to existing account " + accountRef.getOid()); - } - - PrismObject aObject = modelService.getObject(ShadowType.class, accountRef.getOid(), null, task, result); - ShadowType account = aObject.asObjectable(); - - display("Account after import ", account); - - String attributeValueL = ShadowUtil.getMultiStringAttributeValueAsSingle(account, - new QName(ResourceTypeUtil.getResourceNamespace(resourceTypeOpenDjrepo), "l")); -// assertEquals("Unexcpected value of l", "middle of nowhere", attributeValueL); - assertEquals("Unexcpected value of l", getUserLocality(user), attributeValueL); - } - - // This also includes "idm" user imported from LDAP. Later we need to ignore that one. - assertEquals("Wrong number of users after import", 10, uobjects.getObject().size()); - - checkAllShadows(); - - } - - private String getUserLocality(UserType user) { - return user.getLocality() != null ? user.getLocality().getOrig() : "middle of nowhere"; - } - - @Test - public void test420RecomputeUsers() throws Exception { - // GIVEN - - final OperationResult result = createOperationResult(); - - // Assign role to a user, but we do this using a repository instead of model. - // The role assignment will not be executed and this created an inconsistent state. - ObjectDeltaType changeAddRoleCaptain = unmarshalValueFromFile( - REQUEST_USER_MODIFY_ADD_ROLE_CAPTAIN_1_FILENAME, ObjectDeltaType.class); - Collection modifications = DeltaConvertor.toModifications(changeAddRoleCaptain.getItemDelta(), - getUserDefinition()); - repositoryService.modifyObject(UserType.class, changeAddRoleCaptain.getOid(), modifications, result); - - // TODO: setup more "inconsistent" state - - // Add reconciliation task. This will trigger reconciliation - - importObjectFromFile(TASK_USER_RECOMPUTE_FILENAME, result); - - // We need to wait for a sync interval, so the task scanner has a chance - // to pick up this - // task - - waitFor("Waiting for task to finish", new Checker() { - public boolean check() throws ObjectNotFoundException, SchemaException { - Task task = taskManager.getTaskPlain(TASK_USER_RECOMPUTE_OID, result); - //display("Task while waiting for task manager to pick up the task", task); - // wait until the task is finished - if (TaskExecutionStatus.CLOSED == task.getExecutionStatus()) { - return true; - } - return false; - } - - @Override - public void timeout() { - // No reaction, the test will fail right after return from this - } - }, 40000); - - // wait a second until the task will be definitely saved - Thread.sleep(1000); - - // Check task status - - Task task = taskManager.getTaskWithResult(TASK_USER_RECOMPUTE_OID, result); - result.computeStatus(); - display("getTask result", result); - TestUtil.assertSuccess("getTask has failed", result); - AssertJUnit.assertNotNull(task); - display("Task after finish", task); - AssertJUnit.assertNotNull(task.getTaskIdentifier()); - assertFalse(task.getTaskIdentifier().isEmpty()); - - PrismObject o = repositoryService.getObject(TaskType.class, TASK_USER_RECOMPUTE_OID, null, result); - display("Task after pickup in the repository", o.asObjectable()); - - AssertJUnit.assertEquals(TaskExecutionStatus.CLOSED, task.getExecutionStatus()); - - // .. and last run should not be zero - assertNotNull(task.getLastRunStartTimestamp()); - AssertJUnit.assertFalse(task.getLastRunStartTimestamp().longValue() == 0); - assertNotNull(task.getLastRunFinishTimestamp()); - AssertJUnit.assertFalse(task.getLastRunFinishTimestamp().longValue() == 0); - - AssertJUnit.assertEquals(10, task.getProgress()); - - // Test for presence of a result. It should be there and it should - // indicate success - OperationResult taskResult = task.getResult(); - display("Recompute task result", taskResult); - AssertJUnit.assertNotNull(taskResult); - TestUtil.assertSuccess("Recompute task result", taskResult); - - // STOP the task. We don't need it any more and we don't want to give it a chance to run more than once - taskManager.deleteTask(TASK_USER_RECOMPUTE_OID, result); - - // CHECK RESULT: account created for user guybrush - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject object = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUser = object.asObjectable(); - - repoResult.computeStatus(); - displayJaxb("User (repository)", repoUser, new QName("user")); - - List accountRefs = repoUser.getLinkRef(); - assertEquals("Wrong number of accountRefs after recompute for user " + repoUser.getName(), 1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - accountShadowOidGuybrushOpendj = accountRef.getOid(); - assertFalse(accountShadowOidGuybrushOpendj.isEmpty()); - - // Check if shadow was created in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - displayJaxb("Shadow (repository)", repoShadowType, new QName("shadow")); - assertNotNull(repoShadowType); - assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - accountGuybrushOpendjEntryUuuid = checkRepoShadow(repoShadow); - - // check if account was created in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - OpenDJController.assertAttribute(entry, "title", "Honorable Captain"); - OpenDJController.assertAttribute(entry, "carLicense", "C4PT41N"); - OpenDJController.assertAttribute(entry, "businessCategory", "cruise"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword was not set on create", guybrushPassword); - - checkAllShadows(); - } - - @Test - public void test440ReconcileResourceOpenDj() throws Exception { - // GIVEN - - final OperationResult result = createOperationResult(); - - // Create LDAP account without an owner. The liveSync is off, so it will not be picked up - - Entry ldifEntry = openDJController.addEntryFromLdifFile(LDIF_ELAINE_FILENAME); - display("Entry from LDIF", ldifEntry); - - // Guybrush's attributes were set up by a role in the previous test. Let's mess the up a bit. Recon should sort it out. - - List modifications = new ArrayList<>(); - // Expect that a correct title will be added to this one - RawModification titleMod = RawModification.create(ModificationType.REPLACE, "title", "Scurvy earthworm"); - modifications.add(titleMod); - // Expect that the correct location will replace this one - RawModification lMod = RawModification.create(ModificationType.REPLACE, "l", "Davie Jones' locker"); - modifications.add(lMod); - // Expect that this will be untouched - RawModification poMod = RawModification.create(ModificationType.REPLACE, "postOfficeBox", "X marks the spot"); - modifications.add(poMod); - ModifyOperation modifyOperation = openDJController.getInternalConnection().processModify(USER_GUYBRUSH_LDAP_DN, modifications); - if (ResultCode.SUCCESS != modifyOperation.getResultCode()) { - AssertJUnit.fail("LDAP operation failed: " + modifyOperation.getErrorMessage()); - } - - // TODO: setup more "inconsistent" state - - // Add reconciliation task. This will trigger reconciliation - - repoAddObjectFromFile(TASK_OPENDJ_RECON_FILENAME, result); - - // We need to wait for a sync interval, so the task scanner has a chance - // to pick up this - // task - - waitFor("Waiting for task to finish first run", new Checker() { - public boolean check() throws ObjectNotFoundException, SchemaException { - Task task = taskManager.getTaskPlain(TASK_OPENDJ_RECON_OID, result); - display("Task while waiting for task manager to pick up the task", task); - // wait until the task is finished - return task.getLastRunFinishTimestamp() != null; - } - - @Override - public void timeout() { - // No reaction, the test will fail right after return from this - } - }, 180000); - - // Check task status - - Task task = taskManager.getTaskPlain(TASK_OPENDJ_RECON_OID, result); - result.computeStatus(); - display("getTask result", result); - TestUtil.assertSuccess("getTask has failed", result); - AssertJUnit.assertNotNull(task); - display("Task after pickup", task); - - PrismObject o = repositoryService.getObject(TaskType.class, TASK_OPENDJ_RECON_OID, null, result); - display("Task after pickup in the repository", o.asObjectable()); - - // .. it should be running - AssertJUnit.assertEquals(TaskExecutionStatus.RUNNABLE, task.getExecutionStatus()); - - // .. and claimed -// AssertJUnit.assertEquals(TaskExclusivityStatus.CLAIMED, task.getExclusivityStatus()); - - // .. and last run should not be zero - assertNotNull("Null last run start in recon task", task.getLastRunStartTimestamp()); - AssertJUnit.assertFalse("Zero last run start in recon task", task.getLastRunStartTimestamp().longValue() == 0); - assertNotNull("Null last run finish in recon task", task.getLastRunFinishTimestamp()); - AssertJUnit.assertFalse("Zero last run finish in recon task", task.getLastRunFinishTimestamp().longValue() == 0); - - // The progress should be 0, as there were no changes yet - // [pm] commented out, as progress in recon task is now determined not only using # of changes - //AssertJUnit.assertEquals(0, task.getProgress()); - - // Test for presence of a result. It was not fetched - so it should NOT be there - OperationResult taskResult = task.getResult(); - AssertJUnit.assertNull(taskResult); - - // However, the task should indicate success - AssertJUnit.assertEquals(OperationResultStatusType.SUCCESS, task.getResultStatus()); - - // STOP the task. We don't need it any more and we don't want to give it a chance to run more than once - taskManager.deleteTask(TASK_OPENDJ_RECON_OID, result); - - // CHECK RESULT: account for user guybrush should be still there and unchanged - - // Check if user object was modified in the repo - - OperationResult repoResult = new OperationResult("getObject"); - - PrismObject uObject = repositoryService.getObject(UserType.class, USER_GUYBRUSH_OID, null, repoResult); - UserType repoUser = uObject.asObjectable(); - repoResult.computeStatus(); - displayJaxb("User (repository)", repoUser, new QName("user")); - - List accountRefs = repoUser.getLinkRef(); - assertEquals("Guybrush has wrong number of accounts", 1, accountRefs.size()); - ObjectReferenceType accountRef = accountRefs.get(0); - accountShadowOidGuybrushOpendj = accountRef.getOid(); - assertFalse(accountShadowOidGuybrushOpendj.isEmpty()); - - // Check if shadow was created in the repo - - repoResult = new OperationResult("getObject"); - - PrismObject repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidGuybrushOpendj, - null, repoResult); - ShadowType repoShadowType = repoShadow.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - displayJaxb("Shadow (repository)", repoShadowType, new QName("shadow")); - assertNotNull(repoShadowType); - assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - accountGuybrushOpendjEntryUuuid = checkRepoShadow(repoShadow); - - // check if account was created in LDAP - - Entry entry = openDJController.searchAndAssertByEntryUuid(accountGuybrushOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", "guybrush"); - OpenDJController.assertAttribute(entry, "givenName", "Guybrush"); - OpenDJController.assertAttribute(entry, "sn", "Threepwood"); - OpenDJController.assertAttribute(entry, "cn", "Guybrush Threepwood"); - OpenDJController.assertAttribute(entry, "displayName", "Guybrush Threepwood"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object. It is not tolerant, therefore the other value should be gone now - OpenDJController.assertAttribute(entry, "l", "Deep in the Caribbean"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "sailor"); - - // "title" is tolerant, so it will retain the original value as well as the one provided by the role - OpenDJController.assertAttribute(entry, "title", "Scurvy earthworm", "Honorable Captain"); - - OpenDJController.assertAttribute(entry, "carLicense", "C4PT41N"); - OpenDJController.assertAttribute(entry, "businessCategory", "cruise"); - - // No setting for "postOfficeBox", so the value should be unchanged - OpenDJController.assertAttribute(entry, "postOfficeBox", "X marks the spot"); - - String guybrushPassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Pasword was not set on create", guybrushPassword); - -// QueryType query = QueryUtil.createNameQuery(ELAINE_NAME); -// ObjectQuery query = ObjectQuery.createObjectQuery(EqualsFilter.createEqual(UserType.class, prismContext, UserType.F_NAME, ELAINE_NAME)); - ObjectQuery query = ObjectQueryUtil.createNameQuery(ELAINE_NAME, prismContext); - List> users = repositoryService.searchObjects(UserType.class, query, null, repoResult); - assertEquals("Wrong number of Elaines", 1, users.size()); - repoUser = users.get(0).asObjectable(); - - repoResult.computeStatus(); - displayJaxb("User Elaine (repository)", repoUser, new QName("user")); - - assertNotNull(repoUser.getOid()); - assertEquals(PrismTestUtil.createPolyStringType(ELAINE_NAME), repoUser.getName()); - PrismAsserts.assertEqualsPolyString("wrong repo givenName", "Elaine", repoUser.getGivenName()); - PrismAsserts.assertEqualsPolyString("wrong repo familyName", "Marley", repoUser.getFamilyName()); - PrismAsserts.assertEqualsPolyString("wrong repo fullName", "Elaine Marley", repoUser.getFullName()); - - accountRefs = repoUser.getLinkRef(); - assertEquals("Elaine has wrong number of accounts", 1, accountRefs.size()); - accountRef = accountRefs.get(0); - String accountShadowOidElaineOpendj = accountRef.getOid(); - assertFalse(accountShadowOidElaineOpendj.isEmpty()); - - // Check if shadow was created in the repo - - repoResult = new OperationResult("getObject"); - - repoShadow = repositoryService.getObject(ShadowType.class, accountShadowOidElaineOpendj, - null, repoResult); - repoShadowType = repoShadow.asObjectable(); - repoResult.computeStatus(); - TestUtil.assertSuccess("getObject has failed", repoResult); - displayJaxb("Shadow (repository)", repoShadowType, new QName("shadow")); - assertNotNull(repoShadowType); - assertEquals(RESOURCE_OPENDJ_OID, repoShadowType.getResourceRef().getOid()); - - String accountElainehOpendjEntryUuuid = checkRepoShadow(repoShadow); - - // check if account is still in LDAP - - entry = openDJController.searchAndAssertByEntryUuid(accountElainehOpendjEntryUuuid); - - display("LDAP account", entry); - - OpenDJController.assertAttribute(entry, "uid", ELAINE_NAME); - OpenDJController.assertAttribute(entry, "givenName", "Elaine"); - OpenDJController.assertAttribute(entry, "sn", "Marley"); - OpenDJController.assertAttribute(entry, "cn", "Elaine Marley"); - OpenDJController.assertAttribute(entry, "displayName", "Elaine Marley"); - // The "l" attribute is assigned indirectly through schemaHandling and - // config object - // FIXME - //OpenDJController.assertAttribute(entry, "l", "middle of nowhere"); - - // Set by the role - OpenDJController.assertAttribute(entry, "employeeType", "governor"); - OpenDJController.assertAttribute(entry, "title", "Governor"); - OpenDJController.assertAttribute(entry, "businessCategory", "state"); - - String elainePassword = OpenDJController.getAttributeValue(entry, "userPassword"); - assertNotNull("Password of Elaine has disappeared", elainePassword); - - checkAllShadows(); - } - - @Test - public void test480ListResources() throws Exception { - // GIVEN - OperationResultType result = new OperationResultType(); - Holder resultHolder = new Holder<>(result); - - Holder objectListHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - - // WHEN - modelWeb.searchObjects(ObjectTypes.RESOURCE.getTypeQName(), null, options, objectListHolder, resultHolder); - - // THEN - - displayValue("Resources", objectListHolder.value); - assertEquals("Unexpected number of resources", 4, objectListHolder.value.getObject().size()); - // TODO - - for (ObjectType object : objectListHolder.value.getObject()) { - // Marshalling may fail even though the Java object is OK so test for it - String xml = prismContext.xmlSerializer().serialize(object.asPrismObject()); - } - - } - - @Test - public void test485ListResourcesWithBrokenResource() throws Exception { - // GIVEN - Task task = getTestTask(); - final OperationResult result = getTestOperationResult(); - - // WHEN - List> resources = modelService.searchObjects(ResourceType.class, null, null, task, result); - - // THEN - assertNotNull("listObjects returned null list", resources); - - for (PrismObject object : resources) { - ResourceType resource = object.asObjectable(); - //display("Resource found",resource); - display("Found " + ObjectTypeUtil.toShortString(resource) + ", result " + (resource.getFetchResult() == null ? "null" : resource.getFetchResult().getStatus())); - - assertNotNull(resource.getOid()); - assertNotNull(resource.getName()); - - if (resource.getOid().equals(RESOURCE_BROKEN_OID)) { - assertTrue("No error in fetchResult in " + ObjectTypeUtil.toShortString(resource), - resource.getFetchResult() != null && - (resource.getFetchResult().getStatus() == OperationResultStatusType.PARTIAL_ERROR || - resource.getFetchResult().getStatus() == OperationResultStatusType.FATAL_ERROR)); - } else { - assertTrue("Unexpected error in fetchResult in " + ObjectTypeUtil.toShortString(resource), - resource.getFetchResult() == null || resource.getFetchResult().getStatus() == OperationResultStatusType.SUCCESS); - } - } - } - - @Test - public void test500NotifyChangeCreateAccount() throws Exception { - Entry ldifEntry = openDJController.addEntryFromLdifFile(LDIF_ANGELIKA_FILENAME); - display("Entry from LDIF", ldifEntry); - - List attributes = ldifEntry.getAttributes(); - List attrs = ldifEntry.getAttribute("entryUUID"); - - AttributeValue val = null; - if (attrs == null) { - for (Attribute a : attributes) { - if (a.getName().equals("entryUUID")) { - val = a.iterator().next(); - } - } - } else { - val = attrs.get(0).iterator().next(); - } - - String entryUuid = val.toString(); - - ShadowType anglicaAccount = parseObjectType(new File(ACCOUNT_ANGELIKA_FILENAME), ShadowType.class); - PrismProperty prop = anglicaAccount.asPrismObject().findContainer(ShadowType.F_ATTRIBUTES).getValue().createProperty( - prismContext.definitionFactory().createPropertyDefinition(getOpenDjPrimaryIdentifierQName(), DOMUtil.XSD_STRING)); - prop.setRealValue(entryUuid); - anglicaAccount.setResourceRef(ObjectTypeUtil.createObjectRef(RESOURCE_OPENDJ_OID, ObjectTypes.RESOURCE)); - - displayValue("Angelica shadow: ", anglicaAccount.asPrismObject().debugDump()); - - ResourceObjectShadowChangeDescriptionType changeDescription = new ResourceObjectShadowChangeDescriptionType(); - ObjectDeltaType delta = new ObjectDeltaType(); - delta.setChangeType(ChangeTypeType.ADD); - delta.setObjectToAdd(anglicaAccount); - delta.setObjectType(ShadowType.COMPLEX_TYPE); - changeDescription.setObjectDelta(delta); - changeDescription.setChannel(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - - // WHEN - TaskType task = modelWeb.notifyChange(changeDescription); - - // THEN - OperationResult result = OperationResult.createOperationResult(task.getResult()); - display(result); - assertSuccess(result); - - PrismObject userAngelika = findUserByUsername(ANGELIKA_NAME); - assertNotNull("User with the name angelika must exist.", userAngelika); - - UserType user = userAngelika.asObjectable(); - assertNotNull("User with the name angelika must have one link ref.", user.getLinkRef()); - - assertEquals("Expected one account ref in user", 1, user.getLinkRef().size()); - String oid = user.getLinkRef().get(0).getOid(); - - PrismObject modelShadow = modelService.getObject(ShadowType.class, oid, null, taskManager.createTaskInstance(), result); - - assertAttributeNotNull(modelShadow, getOpenDjPrimaryIdentifierQName()); - assertAttribute(modelShadow, "uid", "angelika"); - assertAttribute(modelShadow, "givenName", "Angelika"); - assertAttribute(modelShadow, "sn", "Marley"); - assertAttribute(modelShadow, "cn", "Angelika Marley"); - - } - - @Test - public void test501NotifyChangeModifyAccount() throws Exception { - PrismObject userAngelika = findUserByUsername(ANGELIKA_NAME); - assertNotNull("User with the name angelika must exist.", userAngelika); - - UserType user = userAngelika.asObjectable(); - assertNotNull("User with the name angelika must have one link ref.", user.getLinkRef()); - - assertEquals("Expected one account ref in user", 1, user.getLinkRef().size()); - String oid = user.getLinkRef().get(0).getOid(); - - ResourceObjectShadowChangeDescriptionType changeDescription = new ResourceObjectShadowChangeDescriptionType(); - ObjectDeltaType delta = new ObjectDeltaType(); - delta.setChangeType(ChangeTypeType.MODIFY); - delta.setObjectType(ShadowType.COMPLEX_TYPE); - - ItemDeltaType mod1 = new ItemDeltaType(); - mod1.setModificationType(ModificationTypeType.REPLACE); - ItemPathType path = new ItemPathType(ItemPath.create(ShadowType.F_ATTRIBUTES, new QName(resourceTypeOpenDjrepo.getNamespace(), "givenName"))); - mod1.setPath(path); - - RawType value = new RawType(prismContext.xnodeFactory().primitive("newAngelika"), prismContext); - mod1.getValue().add(value); - - delta.getItemDelta().add(mod1); - delta.setOid(oid); - - logger.info("item delta: {}", SchemaDebugUtil.prettyPrint(mod1)); - - logger.info("delta: {}", DebugUtil.dump(mod1)); - - changeDescription.setObjectDelta(delta); - - changeDescription.setOldShadowOid(oid); - changeDescription.setChannel(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - - // WHEN - TaskType task = modelWeb.notifyChange(changeDescription); - - // THEN - OperationResult result = OperationResult.createOperationResult(task.getResult()); - display(result); - assertSuccess(result); - - PrismObject userAngelikaAfterSync = findUserByUsername(ANGELIKA_NAME); - assertNotNull("User with the name angelika must exist.", userAngelikaAfterSync); - - UserType userAfterSync = userAngelikaAfterSync.asObjectable(); - - PrismAsserts.assertEqualsPolyString("wrong given name in user angelika", PrismTestUtil.createPolyStringType("newAngelika"), userAfterSync.getGivenName()); - - } - - @Test - public void test502NotifyChangeModifyAccountPassword() throws Exception { - PrismObject userAngelika = findUserByUsername(ANGELIKA_NAME); - assertNotNull("User with the name angelika must exist.", userAngelika); - - UserType user = userAngelika.asObjectable(); - assertNotNull("User with the name angelika must have one link ref.", user.getLinkRef()); - - assertEquals("Expected one account ref in user", 1, user.getLinkRef().size()); - String oid = user.getLinkRef().get(0).getOid(); - - String newPassword = "newPassword"; - - openDJController.modifyReplace("uid=" + ANGELIKA_NAME + "," + openDJController.getSuffixPeople(), "userPassword", newPassword); - - ResourceObjectShadowChangeDescriptionType changeDescription = new ResourceObjectShadowChangeDescriptionType(); - ObjectDeltaType delta = new ObjectDeltaType(); - delta.setChangeType(ChangeTypeType.MODIFY); - delta.setObjectType(ShadowType.COMPLEX_TYPE); - - ItemDeltaType passwordDelta = new ItemDeltaType(); - passwordDelta.setModificationType(ModificationTypeType.REPLACE); - passwordDelta.setPath(ModelClientUtil.createItemPathType("credentials/password/value", prismContext)); - RawType passwordValue = new RawType(prismContext.xnodeSerializer().root(new QName("dummy")).serializeRealValue(ModelClientUtil.createProtectedString(newPassword)).getSubnode(), prismContext); - passwordDelta.getValue().add(passwordValue); - - delta.getItemDelta().add(passwordDelta); - delta.setOid(oid); - - logger.info("item delta: {}", SchemaDebugUtil.prettyPrint(passwordDelta)); - - logger.info("delta: {}", DebugUtil.dump(passwordDelta)); - - changeDescription.setObjectDelta(delta); - - changeDescription.setOldShadowOid(oid); -// changeDescription.setCurrentShadow(angelicaShadowType); - changeDescription.setChannel(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - - // WHEN - TaskType task = modelWeb.notifyChange(changeDescription); - - // THEN - OperationResult result = OperationResult.createOperationResult(task.getResult()); - display(result); - assertSuccess(result); - - PrismObject userAngelikaAfterSync = findUserByUsername(ANGELIKA_NAME); - assertNotNull("User with the name angelika must exist.", userAngelikaAfterSync); - - assertUserLdapPassword(userAngelikaAfterSync, newPassword); - - } - - @Test - public void test503NotifyChangeDeleteAccount() throws Exception { - PrismObject userAngelika = findUserByUsername(ANGELIKA_NAME); - assertNotNull("User with the name angelika must exist.", userAngelika); - - UserType user = userAngelika.asObjectable(); - assertNotNull("User with the name angelika must have one link ref.", user.getLinkRef()); - - assertEquals("Expected one account ref in user", 1, user.getLinkRef().size()); - String oid = user.getLinkRef().get(0).getOid(); - - ResourceObjectShadowChangeDescriptionType changeDescription = new ResourceObjectShadowChangeDescriptionType(); - ObjectDeltaType delta = new ObjectDeltaType(); - delta.setChangeType(ChangeTypeType.DELETE); - delta.setObjectType(ShadowType.COMPLEX_TYPE); - delta.setOid(oid); - changeDescription.setObjectDelta(delta); - changeDescription.setOldShadowOid(oid); - changeDescription.setChannel(SchemaConstants.CHANNEL_WEB_SERVICE_URI); - - // WHEN - TaskType task = modelWeb.notifyChange(changeDescription); - - // THEN - OperationResult result = OperationResult.createOperationResult(task.getResult()); - display(result); - assertTrue(result.isAcceptable()); - - PrismObject userAngelikaAfterSync = findUserByUsername(ANGELIKA_NAME); - display("User after", userAngelikaAfterSync); - assertNotNull("User with the name angelika must exist.", userAngelikaAfterSync); - - UserType userType = userAngelikaAfterSync.asObjectable(); - assertNotNull("User with the name angelika must have one link ref.", userType.getLinkRef()); - - assertEquals("Expected no account ref in user", 0, userType.getLinkRef().size()); - - } - - @Test - public void test999Shutdown() throws Exception { - taskManager.shutdown(); - waitFor("waiting for task manager shutdown", new Checker() { - @Override - public boolean check() { - return taskManager.getLocallyRunningTasks(new OperationResult("dummy")).isEmpty(); - } - - @Override - public void timeout() { - // No reaction, the test will fail right after return from this - } - }, 10000); - AssertJUnit.assertEquals("Some tasks left running after shutdown", new HashSet(), - new HashSet<>(taskManager.getLocallyRunningTasks(new OperationResult("dummy")))); - } - - // TODO: test for missing/corrupt system configuration - // TODO: test for missing sample config (bad reference in expression - // arguments) - - private String checkRepoShadow(PrismObject repoShadow) { - ShadowType repoShadowType = repoShadow.asObjectable(); - String uid = null; - boolean hasOthers = false; - List xmlAttributes = repoShadowType.getAttributes().getAny(); - for (Object element : xmlAttributes) { - if (SchemaConstants.ICFS_UID.equals(JAXBUtil.getElementQName(element)) || getOpenDjPrimaryIdentifierQName().equals(JAXBUtil.getElementQName(element))) { - if (uid != null) { - AssertJUnit.fail("Multiple values for ICF UID in shadow attributes"); - } else { - uid = ((Element) element).getTextContent(); - } - } else if (SchemaConstants.ICFS_NAME.equals(JAXBUtil.getElementQName(element)) || getOpenDjSecondaryIdentifierQName().equals(JAXBUtil.getElementQName(element))) { - // This is OK - } else { - hasOthers = true; - } - } - - assertFalse("Shadow " + repoShadow + " has unexpected elements", hasOthers); - assertNotNull(uid); - - return uid; - } - - private QName getOpenDjPrimaryIdentifierQName() { - return new QName(RESOURCE_OPENDJ_NS, RESOURCE_OPENDJ_PRIMARY_IDENTIFIER_LOCAL_NAME); - } - - private QName getOpenDjSecondaryIdentifierQName() { - return new QName(RESOURCE_OPENDJ_NS, RESOURCE_OPENDJ_SECONDARY_IDENTIFIER_LOCAL_NAME); - } - - private ShadowType searchAccountByOid(final String accountOid) throws Exception { - OperationResultType resultType = new OperationResultType(); - Holder resultHolder = new Holder<>(resultType); - Holder accountHolder = new Holder<>(); - SelectorQualifiedGetOptionsType options = new SelectorQualifiedGetOptionsType(); - modelWeb.getObject(ObjectTypes.SHADOW.getTypeQName(), accountOid, options, accountHolder, resultHolder); - ObjectType object = accountHolder.value; - TestUtil.assertSuccess("searchObjects has failed", resultHolder.value); - assertNotNull("Account is null", object); - - if (!(object instanceof ShadowType)) { - fail("Object is not account."); - } - ShadowType account = (ShadowType) object; - assertEquals(accountOid, account.getOid()); - - return account; - } - - private UserType searchUserByName(String name) throws Exception { -// Document doc = DOMUtil.getDocument(); -// Element nameElement = doc.createElementNS(SchemaConstants.C_NAME.getNamespaceURI(), -// SchemaConstants.C_NAME.getLocalPart()); -// nameElement.setTextContent(name); -// Element filter = QueryUtil.createEqualFilter(doc, null, nameElement); -// -// QueryType query = new QueryType(); -// query.setFilter(filter); - ObjectQuery q = ObjectQueryUtil.createNameQuery(UserType.class, prismContext, name); - QueryType query = prismContext.getQueryConverter().createQueryType(q); - OperationResultType resultType = new OperationResultType(); - Holder resultHolder = new Holder<>(resultType); - Holder listHolder = new Holder<>(); - assertNoRepoThreadLocalCache(); - - modelWeb.searchObjects(ObjectTypes.USER.getTypeQName(), query, null, listHolder, resultHolder); - - assertNoRepoThreadLocalCache(); - ObjectListType objects = listHolder.value; - TestUtil.assertSuccess("searchObjects has failed", resultHolder.value); - AssertJUnit.assertEquals("User not found (or found too many)", 1, objects.getObject().size()); - UserType user = (UserType) objects.getObject().get(0); - - AssertJUnit.assertEquals(user.getName(), PrismTestUtil.createPolyStringType(name)); - - return user; - } - - private void basicWaitForSyncChangeDetection(Task syncCycle, Object tokenBefore, int increment, - final OperationResult result) throws Exception { - basicWaitForSyncChangeDetection(syncCycle, (int) ((Integer) tokenBefore), increment, result); - } - - private void basicWaitForSyncChangeDetection(Task syncCycle, int tokenBefore, int increment, - final OperationResult result) throws Exception { - basicWaitForSyncChangeDetection(syncCycle, tokenBefore, increment, result, 40000); - } - - private void basicWaitForSyncChangeDetection(final Task syncCycle, final int tokenBefore, final int increment, - final OperationResult result, int timeout) throws Exception { - - waitFor("Waiting for sync cycle to detect change", new Checker() { - @Override - public boolean check() throws CommonException { - syncCycle.refresh(result); - display("SyncCycle while waiting for sync cycle to detect change", syncCycle); - if (syncCycle.getExecutionStatus() != TaskExecutionStatus.RUNNABLE) { - throw new IllegalStateException("Task not runnable: " + syncCycle.getExecutionStatus() + "; " + syncCycle); - } - int tokenNow = findSyncToken(syncCycle); - display("tokenNow = " + tokenNow); - - return tokenNow >= tokenBefore + increment; - } - - @Override - public void timeout() { - // No reaction, the test will fail right after return from this - } - }, timeout, WAIT_FOR_LOOP_SLEEP_MILLIS); - } - - private void setAssignmentEnforcement(AssignmentPolicyEnforcementType enforcementType) throws ObjectNotFoundException, SchemaException, ObjectAlreadyExistsException { - assumeAssignmentPolicy(enforcementType); - } - - private void assertSyncSettingsAssignmentPolicyEnforcement(AssignmentPolicyEnforcementType assignmentPolicy) throws - ObjectNotFoundException, SchemaException { - OperationResult result = new OperationResult("Asserting sync settings"); - PrismObject systemConfigurationType = repositoryService.getObject(SystemConfigurationType.class, - SystemObjectsType.SYSTEM_CONFIGURATION.value(), null, result); - result.computeStatus(); - TestUtil.assertSuccess("Asserting sync settings failed (result)", result); - ProjectionPolicyType globalAccountSynchronizationSettings = systemConfigurationType.asObjectable().getGlobalAccountSynchronizationSettings(); - assertNotNull("globalAccountSynchronizationSettings is null", globalAccountSynchronizationSettings); - AssignmentPolicyEnforcementType assignmentPolicyEnforcement = globalAccountSynchronizationSettings.getAssignmentPolicyEnforcement(); - assertNotNull("assignmentPolicyEnforcement is null", assignmentPolicyEnforcement); - assertEquals("Assignment policy mismatch", assignmentPolicy, assignmentPolicyEnforcement); - } - - private void checkAllShadows() throws SchemaException { - logger.trace("Checking all shadows"); - System.out.println("Checking all shadows"); - ObjectChecker checker = null; - IntegrationTestTools.checkAllShadows(resourceTypeOpenDjrepo, repositoryService, checker, prismContext); - } - - public static String getNormalizedAttributeValue(ShadowType repoShadow, RefinedObjectClassDefinition objClassDef, QName name) { - - String value = getAttributeValue(repoShadow, name); - - RefinedAttributeDefinition idDef = objClassDef.getPrimaryIdentifiers().iterator().next(); - if (idDef.getMatchingRuleQName() != null && idDef.getMatchingRuleQName().equals(PrismConstants.STRING_IGNORE_CASE_MATCHING_RULE_NAME)) { - return value.toLowerCase(); - } - - return value; - } - - protected void assertAttribute(ShadowType shadowType, String attrName, T... expectedValues) { - assertAttribute(resourceTypeOpenDjrepo, shadowType, attrName, expectedValues); - } - - protected void assertAttribute(PrismObject shadow, String attrName, T... expectedValues) { - assertAttribute(resourceTypeOpenDjrepo, shadow.asObjectable(), attrName, expectedValues); - } -} diff --git a/testing/sanity/src/test/resources/ctx-sanity-test-main.xml b/testing/sanity/src/test/resources/ctx-sanity-test-main.xml deleted file mode 100644 index 8557e4eee3e..00000000000 --- a/testing/sanity/src/test/resources/ctx-sanity-test-main.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/testing/sanity/src/test/resources/ctx-sanity-test.xml b/testing/sanity/src/test/resources/ctx-sanity-test.xml deleted file mode 100644 index 362551f6d33..00000000000 --- a/testing/sanity/src/test/resources/ctx-sanity-test.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - diff --git a/testing/sanity/src/test/resources/logback-test.xml b/testing/sanity/src/test/resources/logback-test.xml deleted file mode 100644 index 7689b2d6770..00000000000 --- a/testing/sanity/src/test/resources/logback-test.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - ./target/test.log - - - %date [%thread] %-5level [%X{subsystem}]\(%logger{46}\): %message%n - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/testing/sanity/src/test/resources/repo/connector-broken.xml b/testing/sanity/src/test/resources/repo/connector-broken.xml deleted file mode 100644 index 6c04d464565..00000000000 --- a/testing/sanity/src/test/resources/repo/connector-broken.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - ICF Broken Connector - http://midpoint.evolveum.com/xml/ns/public/connector/icf-1 - this.connector.type.does.not.Exist - 1.1.0.0-e1 - non.existent.connector.bundle - http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/bundle/non.existent.connector.bundle - - - - diff --git a/testing/sanity/src/test/resources/repo/resource-broken.xml b/testing/sanity/src/test/resources/repo/resource-broken.xml deleted file mode 100644 index 07d1b18c55f..00000000000 --- a/testing/sanity/src/test/resources/repo/resource-broken.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - Broken Resource - - - - c:connectorType - this.connector.type.does.not.Exist - - - - - - - - - - - http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-ffff-ffffffffffff - diff --git a/testing/sanity/src/test/resources/repo/resource-derby.xml b/testing/sanity/src/test/resources/repo/resource-derby.xml deleted file mode 100644 index 5476a2baf25..00000000000 --- a/testing/sanity/src/test/resources/repo/resource-derby.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Embedded Test Derby - - - - c:connectorType - org.identityconnectors.databasetable.DatabaseTableConnector - - - - - - - org.apache.derby.jdbc.ClientDriver - jdbc:derby://%h:%p/%d - login - password - secret - users - midpoint - target/derbyMidPointTest - localhost - 11527 - - - - http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-999902d3abab - - - - - - - diff --git a/testing/sanity/src/test/resources/repo/resource-dummy.xml b/testing/sanity/src/test/resources/repo/resource-dummy.xml deleted file mode 100644 index e73d84d7974..00000000000 --- a/testing/sanity/src/test/resources/repo/resource-dummy.xml +++ /dev/null @@ -1,322 +0,0 @@ - - - - - - - Dummy Resource - dummy resource - - - - - - connectorType - com.evolveum.icf.dummy.connector.DummyConnector - - - connectorVersion - 2.0 - - - - - - - - - - - - - http://midpoint.evolveum.com/xml/ns/public/resource/instance/10000000-0000-0000-0000-000000000004 - - - - - - - - - - - icfs:uid - icfs:name - icfs:name - icfs:name - __ACCOUNT__ - account - - - - - - - - ICF UID - read - - - - - - - - - - ICF NAME - - - - - - - - - - - - - - - - - - - default - Default Account - true - ri:AccountObjectClass - - icfs:name - Username - - weak - - $user/name - - - - - - - - weak - - $c:user/c:name - - - - - icfs:uid - UID - - - ri:fullname - Full Name - - - $user/fullName - - - - weak - - $user/fullName - - - - - ri:location - Location - - strong - - - $c:user/c:locality - - - - - http://midpoint.evolveum.com/xml/ns/public/provisioning/channels-3#import - - - - - description - - - - - ri:ship - Ship - - - - - - organizationalUnit - - - - - ri:loot - Loot - - http://pirates.net/avast - - 10000 - - - - - ri:weapon - Weapon - - - - $user/extension/piracy:weapon - - - - - - ri:drink - Drink - false - - strong - - rum - - - - - ri:quote - Quote - true - - strong - - Arr! - - - - - - 5 - - - - - - attributes/name - daviejones - - - - - - - attributes/name - calypso - - - - - - - - - - - - - - - - - - - - - - - weak - - - - - - - - - - - - - true - - - - - true - - - c:name - - $account/attributes/icfs:name - - - - - linked - http://midpoint.evolveum.com/xml/ns/public/model/action-3#modifyUser - - - deleted - http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlinkAccount - - - unlinked - http://midpoint.evolveum.com/xml/ns/public/model/action-3#linkAccount - - - unmatched - http://midpoint.evolveum.com/xml/ns/public/provisioning/channels-3#import - http://midpoint.evolveum.com/xml/ns/public/model/action-3#addUser - - - - - diff --git a/testing/sanity/src/test/resources/repo/resource-opendj-legacy.xml b/testing/sanity/src/test/resources/repo/resource-opendj-legacy.xml deleted file mode 100644 index 7b26b380936..00000000000 --- a/testing/sanity/src/test/resources/repo/resource-opendj-legacy.xml +++ /dev/null @@ -1,707 +0,0 @@ - - - - - - - - - Embedded Test OpenDJ - - - - Dummy description, just for the test - - - c:connectorType - org.identityconnectors.ldap.LdapConnector - - - - - - - - - - - 10389 - localhost - dc=example,dc=com - cn=directory manager - - secret - - uid - ds-pwp-account-disabled - - - - - - 120000 - - 1 - 10 - 10 - 150000 - - - 100 - - - -1 - -1 - -1 - -1 - -1 - - -1 - -1 - -1 - -1 - -1 - -1 - - -1 - - - - - - http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff - - - - - - - - account - default - - Default Account - - true - - - ri:AccountObjectClass - - - - icfs:name - - - Distinguished Name - - mr:stringIgnoreCase - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/name - - - - - - - - - - - - - icfs:uid - - - Entry UUID - - - - - - mr:stringIgnoreCase - - - - - - - ri:cn - Common Name - - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - $user/fullName - - - - - - - - - - - - declare namespace i="http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - $i:user/i:fullName - - - - - - - - - ri:sn - - Surname - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$c:user/c:familyName - - - - - - - - - - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";familyName - - - - - - - - ri:givenName - Given Name - - - The mapping is using relative path in default source context (which is $user) - and default expression (which is asIs). - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";givenName - - - - - - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - $user/givenName - - - - - - - - ri:uid - - Login Name - - mr:stringIgnoreCase - - - - It is mapped from (and also to) "name" property of user. It is essentially a login name. - This outbound construction is using a Groovy expression. - - - weak - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/name - - - - - - - - It is mapped to (and also from) "name" property of user. It is essentially a login name - - weak - - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/name - - - - - - - - ri:description - - - - weak - - Created by IDM - - - - - - - declare namespace i="http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - declare namespace my="http://whatever.com/my"; - $i:user/i:extension/my:description - - - - - - - - - ri:carLicense - - - This attibute definition is used to test tolerance of empty values. - - - - - The expression will produce empty value. OpenDJ will die if empty value - is provided for an attribute. midPoint should filter out the empty value - and do not sent it to OpenDJ. - - weak - - - - - - - - - declare namespace i="http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - declare namespace my="http://whatever.com/my"; - $i:user/i:extension/my:description - - - - - - - - ri:employeeType - - - This attibute definition is used to test inboud mapping when deleting the value from attribute. - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/employeeType - - - - - - - ri:displayName - Display Name - - - It is mapped from "fullName" property of user using ECMAScript (JavaScript) expression. - - - $user/fullName - - - - - - - - - ri:postalAddress - Display Name - - - Conditional expression. It will be evaluated only if user's employee number is set. - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$c:user/c:employeeNumber - - - - - - - - - - - - - - ri:l - false - - - - strong - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$c:user/c:locality - - - - - - - - - - - - - - - - - ri:group - LDAP Group Membership - entitlement - ldapGroup - objectToSubject - ri:uniqueMember - icfs:name - - - - - - - - - - - - - - - - - - - http://midpoint.evolveum.com/xml/ns/public/model/channels-3#webService - - - - - - - - weak - http://midpoint.evolveum.com/xml/ns/public/model/channels-3#webService - - - - - - - - - - - - entitlement - ldapGroup - LDAP Group - ri:GroupObjectClass - - - - - - - - - ri:ds-pwp-account-disabled - - true - - - - - - - - - - true - - - - - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";c:name - - - - - - - - - - - - - - - linked - true - - - deleted - true - - http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink - - - - unlinked - true - - http://midpoint.evolveum.com/xml/ns/public/model/action-3#link - - - - unmatched - true - - http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus - - - - - - diff --git a/testing/sanity/src/test/resources/repo/resource-opendj.xml b/testing/sanity/src/test/resources/repo/resource-opendj.xml deleted file mode 100644 index 324e46ee63f..00000000000 --- a/testing/sanity/src/test/resources/repo/resource-opendj.xml +++ /dev/null @@ -1,670 +0,0 @@ - - - - - - - - - Embedded Test OpenDJ - - - - Dummy description, just for the test - - - c:connectorType - com.evolveum.polygon.connector.ldap.LdapConnector - - - - - - - - - 10389 - localhost - dc=example,dc=com - cn=directory manager - secret - auto - entryUUID - ds-pwp-account-disabled - isMemberOf - - - - - - 120000 - - 1 - 10 - 10 - 150000 - - - 100 - - - -1 - -1 - -1 - -1 - -1 - - -1 - -1 - -1 - -1 - -1 - -1 - - -1 - - - - false - false - false - - - - - - http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff - - - - - - - - account - default - - Default Account - - true - - - ri:inetOrgPerson - - - - ri:dn - - - Distinguished Name - - mr:stringIgnoreCase - - - - - - $focus/name - - - - - - - - - - - - - ri:entryUUID - - - Entry UUID - - - - - - mr:stringIgnoreCase - - - - - - - ri:cn - Common Name - - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - $user/fullName - - - - - - - - - - - fullName - - - - - - - - ri:sn - - Surname - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$c:user/c:familyName - - - - - - - - - - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";familyName - - - - - - - - ri:givenName - Given Name - - - The mapping is using relative path in default source context (which is $user) - and default expression (which is asIs). - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";givenName - - - - - - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - $user/givenName - - - - - - - - ri:uid - - Login Name - - mr:stringIgnoreCase - - - - It is mapped from (and also to) "name" property of user. It is essentially a login name. - This outbound construction is using a Groovy expression. - - - weak - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/name - - - - - - - - It is mapped to (and also from) "name" property of user. It is essentially a login name - - weak - - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/name - - - - - - - - ri:description - - - - weak - - Created by IDM - - - - - - - declare namespace i="http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - declare namespace my="http://whatever.com/my"; - $i:user/i:extension/my:description - - - - - - - - - ri:carLicense - - - This attibute definition is used to test tolerance of empty values. - - - - - The expression will produce empty value. OpenDJ will die if empty value - is provided for an attribute. midPoint should filter out the empty value - and do not sent it to OpenDJ. - - weak - - - - - - - - - - - - - - - - - - - ri:employeeType - - - This attibute definition is used to test inboud mapping when deleting the value from attribute. - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/employeeType - - - - - - - ri:displayName - Display Name - - - It is mapped from "fullName" property of user using ECMAScript (JavaScript) expression. - - - $user/fullName - - - - - - - - - ri:postalAddress - Display Name - - - Conditional expression. It will be evaluated only if user's employee number is set. - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$c:user/c:employeeNumber - - - - - - - - - - - - - - ri:l - false - - - - strong - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3";$c:user/c:locality - - - - - - - - - - - - - - - - - ri:group - LDAP Group Membership - entitlement - ldapGroup - objectToSubject - ri:uniqueMember - ri:dn - - - - - - - - - - - - - - - - - - - http://midpoint.evolveum.com/xml/ns/public/model/channels-3#webService - - - - - - - - weak - http://midpoint.evolveum.com/xml/ns/public/model/channels-3#webService - - - - - - - - - - - - entitlement - ldapGroup - LDAP Group - ri:groupOfUniqueNames - - - - - - - - - ri:ds-pwp-account-disabled - - true - - - - - - - - - - true - - - - - - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - c:name - - - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - declare namespace dj="http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff"; - $c:account/c:attributes/dj:uid - - - - - - - - - - - - - - linked - true - - - deleted - true - - http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink - - - - unlinked - true - - http://midpoint.evolveum.com/xml/ns/public/model/action-3#link - - - - unmatched - true - - http://midpoint.evolveum.com/xml/ns/public/model/action-3#addFocus - - - - - - diff --git a/testing/sanity/src/test/resources/repo/role-captain.xml b/testing/sanity/src/test/resources/repo/role-captain.xml deleted file mode 100644 index 4b8315a2df3..00000000000 --- a/testing/sanity/src/test/resources/repo/role-captain.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - Captain Role - - - - - account - - ri:employeeType - - - sailor - - - - - ri:title - - strong - - Honorable Captain - - - - - ri:carLicense - - - C4PT41N - - - - - ri:businessCategory - - - cruise - - - - - ri:departmentNumber - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/givenName - - - - - - - - ri:destinationIndicator - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";fullName - - - - - ri:physicalDeliveryOfficeName - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - declare namespace my="http://whatever.com/my"; - $c:assignment/c:extension/my:shipName - - - - - - diff --git a/testing/sanity/src/test/resources/repo/role-judge.xml b/testing/sanity/src/test/resources/repo/role-judge.xml deleted file mode 100644 index 5e0b88b5a28..00000000000 --- a/testing/sanity/src/test/resources/repo/role-judge.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - Judge - - - - - account - - - - - criminal exclusion: pirate - - - - - - - - - - - diff --git a/testing/sanity/src/test/resources/repo/role-pirate.xml b/testing/sanity/src/test/resources/repo/role-pirate.xml deleted file mode 100644 index 9aab890b426..00000000000 --- a/testing/sanity/src/test/resources/repo/role-pirate.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - Pirate Role - - - - - - - - - account - - ri:title - - strong - - Bloody Pirate - - - - - ri:businessCategory - - - loot - murder - - - - - - diff --git a/testing/sanity/src/test/resources/repo/role-sailor.xml b/testing/sanity/src/test/resources/repo/role-sailor.xml deleted file mode 100644 index e9ffc50ae6d..00000000000 --- a/testing/sanity/src/test/resources/repo/role-sailor.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - Sailor Role - - - - - account - - ri:employeeType - - - sailor - - - - - - diff --git a/testing/sanity/src/test/resources/repo/role-superuser.xml b/testing/sanity/src/test/resources/repo/role-superuser.xml deleted file mode 100644 index bbb992e8733..00000000000 --- a/testing/sanity/src/test/resources/repo/role-superuser.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - Superuser - - http://midpoint.evolveum.com/xml/ns/public/security/authorization-3#all - - diff --git a/testing/sanity/src/test/resources/repo/sample-configuration-object.xml b/testing/sanity/src/test/resources/repo/sample-configuration-object.xml deleted file mode 100644 index 5c0ea8d3217..00000000000 --- a/testing/sanity/src/test/resources/repo/sample-configuration-object.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - My Sample Config Object - - - - - http://myself.me/schemas/objects#SampleConfigType - diff --git a/testing/sanity/src/test/resources/repo/system-configuration.xml b/testing/sanity/src/test/resources/repo/system-configuration.xml deleted file mode 100644 index 2895c993ff5..00000000000 --- a/testing/sanity/src/test/resources/repo/system-configuration.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - SystemConfiguration - - - REPOSITORY - INFO - IDM_LOG - - - com.evolveum.midpoint.common - DEBUG - - - com.evolveum.midpoint.util - DEBUG - - - %date [%thread] %-5level \(%logger{46}\): %message%n - target/test.log - true - - IDM_LOG - INFO - - diff --git a/testing/sanity/src/test/resources/repo/task-derby-reconciliation.xml b/testing/sanity/src/test/resources/repo/task-derby-reconciliation.xml deleted file mode 100644 index 52783e2d61b..00000000000 --- a/testing/sanity/src/test/resources/repo/task-derby-reconciliation.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - Reconciliation: Derby - - - ri:AccountObjectClass - - - 91919191-76e0-59e2-86d6-3d4f02d3dddd - - runnable - - http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/reconciliation/handler-3 - - recurring - tight - - 5 - - - diff --git a/testing/sanity/src/test/resources/repo/task-opendj-reconciliation-legacy.xml b/testing/sanity/src/test/resources/repo/task-opendj-reconciliation-legacy.xml deleted file mode 100644 index d1483141088..00000000000 --- a/testing/sanity/src/test/resources/repo/task-opendj-reconciliation-legacy.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - Reconciliation: OpenDJ - - - ri:AccountObjectClass - - - 91919191-76e0-59e2-86d6-3d4f02d30000 - - runnable - - http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/reconciliation/handler-3 - - recurring - tight - - 5 - - - diff --git a/testing/sanity/src/test/resources/repo/task-opendj-reconciliation.xml b/testing/sanity/src/test/resources/repo/task-opendj-reconciliation.xml deleted file mode 100644 index ccd12281695..00000000000 --- a/testing/sanity/src/test/resources/repo/task-opendj-reconciliation.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - Reconciliation: OpenDJ - - - ri:inetOrgPerson - - - 91919191-76e0-59e2-86d6-3d4f02d30000 - - runnable - - http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/reconciliation/handler-3 - - recurring - tight - - 5 - - - diff --git a/testing/sanity/src/test/resources/repo/task-opendj-sync-legacy.xml b/testing/sanity/src/test/resources/repo/task-opendj-sync-legacy.xml deleted file mode 100644 index 892595d1f45..00000000000 --- a/testing/sanity/src/test/resources/repo/task-opendj-sync-legacy.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - Synchronization: Embedded Test OpenDJ - - - capsized - 42 - ri:AccountObjectClass - - - 91919191-76e0-59e2-86d6-3d4f02d3ffff - - runnable - - http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/live-sync/handler-3 - - recurring - tight - - 3 - - - diff --git a/testing/sanity/src/test/resources/repo/task-opendj-sync.xml b/testing/sanity/src/test/resources/repo/task-opendj-sync.xml deleted file mode 100644 index 6c71c019b1f..00000000000 --- a/testing/sanity/src/test/resources/repo/task-opendj-sync.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - Synchronization: Embedded Test OpenDJ - - - capsized - 42 - - - - 91919191-76e0-59e2-86d6-3d4f02d3ffff - - runnable - - http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/live-sync/handler-3 - - recurring - tight - - 3 - - - diff --git a/testing/sanity/src/test/resources/repo/task-user-recompute.xml b/testing/sanity/src/test/resources/repo/task-user-recompute.xml deleted file mode 100644 index 6a1f736cc23..00000000000 --- a/testing/sanity/src/test/resources/repo/task-user-recompute.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - User Recompute - - - - - - - runnable - - http://midpoint.evolveum.com/xml/ns/public/model/synchronization/task/recompute/handler-3 - single - tight - - diff --git a/testing/sanity/src/test/resources/repo/user-administrator.xml b/testing/sanity/src/test/resources/repo/user-administrator.xml deleted file mode 100644 index 2065d6669df..00000000000 --- a/testing/sanity/src/test/resources/repo/user-administrator.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - administrator - midPoint Administrator - midPoint - Administrator - administrator@evolveum.com - - - - - - http://www.w3.org/2001/04/xmlenc#aes128-cbc - - - 4HXeUejV93Vd3JuIZz7sbs5bVko= - - - Q27VymuHR348Vb9Ln5p06RT667FqZPSijEMxVDWw7D8= - - - - - - - - - - - - - - - - - - - - diff --git a/testing/sanity/src/test/resources/repo/user-e.xml b/testing/sanity/src/test/resources/repo/user-e.xml deleted file mode 100644 index 1648a82ee70..00000000000 --- a/testing/sanity/src/test/resources/repo/user-e.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - e - e - e - e - e - - - - deadmentellnotales - - - - diff --git a/testing/sanity/src/test/resources/repo/user-guybrush.xml b/testing/sanity/src/test/resources/repo/user-guybrush.xml deleted file mode 100644 index e4a39f54720..00000000000 --- a/testing/sanity/src/test/resources/repo/user-guybrush.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - guybrush - Guybrush Threepwood - Guybrush - Threepwood - guybrush@evolveum.com - Loosers - Deep in the Caribbean - A - B - C - - - - - - howMuchWoodWouldWoodchuckChuckIfWoodchuckCouldChuckWood - - - - diff --git a/testing/sanity/src/test/resources/repo/user-jack.xml b/testing/sanity/src/test/resources/repo/user-jack.xml deleted file mode 100644 index aba5e834fbe..00000000000 --- a/testing/sanity/src/test/resources/repo/user-jack.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - jack - - Black Pearl - - Jack Sparrow - Jack - Sparrow - Jackie - Cpt. - - PhD. - jack.sparrow@evolveum.com - 555-1234 - CAPTAIN - Leaders - Black Pearl - - - enabled - - - - - - deadmentellnotales - - - - diff --git a/testing/sanity/src/test/resources/repo/user-template.xml b/testing/sanity/src/test/resources/repo/user-template.xml deleted file mode 100644 index 61a593b8edc..00000000000 --- a/testing/sanity/src/test/resources/repo/user-template.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - Default User Template - - - - weak - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/givenName - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";$user/familyName - - - - - - declare default namespace "http://midpoint.evolveum.com/xml/ns/public/common/common-3";fullName - - - - diff --git a/testing/sanity/src/test/resources/request/account-angelika-legacy.xml b/testing/sanity/src/test/resources/request/account-angelika-legacy.xml deleted file mode 100644 index 2f66a623315..00000000000 --- a/testing/sanity/src/test/resources/request/account-angelika-legacy.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - ri:AccountObjectClass - - uid=angelika,ou=people,dc=example,dc=com - angelika - Angelika Marley - Angelika - Marley - - diff --git a/testing/sanity/src/test/resources/request/account-angelika.xml b/testing/sanity/src/test/resources/request/account-angelika.xml deleted file mode 100644 index 3a3f6ffe5b1..00000000000 --- a/testing/sanity/src/test/resources/request/account-angelika.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - ri:inetOrgPerson - - uid=angelika,ou=people,dc=example,dc=com - angelika - Angelika Marley - Angelika - Marley - - diff --git a/testing/sanity/src/test/resources/request/account-modify-attrs.xml b/testing/sanity/src/test/resources/request/account-modify-attrs.xml deleted file mode 100644 index 789c3c858b7..00000000000 --- a/testing/sanity/src/test/resources/request/account-modify-attrs.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - modify - c:ShadowType - will be replaced by the test code - - replace - - declare namespace c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"; - declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff"; - c:attributes/ri:roomNumber - - captain's cabin - - - add - declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff";c:attributes/ri:businessCategory - capsize - fighting - - - add - declare namespace ri="http://midpoint.evolveum.com/xml/ns/public/resource/instance/ef2bc95b-76e0-59e2-86d6-3d4f02d3ffff";c:attributes/ri:employeeType - A - - diff --git a/testing/sanity/src/test/resources/request/account-modify-bad-path.xml b/testing/sanity/src/test/resources/request/account-modify-bad-path.xml deleted file mode 100644 index 46cfe3ebb3a..00000000000 --- a/testing/sanity/src/test/resources/request/account-modify-bad-path.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - modify - c:ShadowType - will be replaced by the test code - - replace - attributes/thisAttributeDoesNotExist - whatever - - diff --git a/testing/sanity/src/test/resources/request/account-modify-roomnumber-explicit-type.xml b/testing/sanity/src/test/resources/request/account-modify-roomnumber-explicit-type.xml deleted file mode 100644 index ccf6ba8031d..00000000000 --- a/testing/sanity/src/test/resources/request/account-modify-roomnumber-explicit-type.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - modify - c:ShadowType - will be replaced by the test code - - replace - attributes/roomNumber - upperdeck - - diff --git a/testing/sanity/src/test/resources/request/account-modify-roomnumber.xml b/testing/sanity/src/test/resources/request/account-modify-roomnumber.xml deleted file mode 100644 index 3d90e2826de..00000000000 --- a/testing/sanity/src/test/resources/request/account-modify-roomnumber.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - modify - c:ShadowType - will be replaced by the test code - - replace - attributes/roomNumber - quarterdeck - - diff --git a/testing/sanity/src/test/resources/request/angelika.ldif b/testing/sanity/src/test/resources/request/angelika.ldif deleted file mode 100644 index da3a3f2cf80..00000000000 --- a/testing/sanity/src/test/resources/request/angelika.ldif +++ /dev/null @@ -1,16 +0,0 @@ -dn: uid=angelika,ou=People,dc=example,dc=com -uid: angelika -cn: Angelika Marley -sn: Marley -givenname: Angelika -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -l: Caribbean -employeeType: governor -title: Governor -businessCategory: state -mail: governor.marley@deep.in.the.caribbean.com -userpassword: piranhaDogs - diff --git a/testing/sanity/src/test/resources/request/e-create.ldif b/testing/sanity/src/test/resources/request/e-create.ldif deleted file mode 100644 index 8bb545eb729..00000000000 --- a/testing/sanity/src/test/resources/request/e-create.ldif +++ /dev/null @@ -1,15 +0,0 @@ -dn: uid=e,ou=People,dc=example,dc=com -uid: e -cn: e -sn: e -givenname: e -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -l: middle of nowhere -mail: e@example.com -telephonenumber: +1 408 555 1234 -facsimiletelephonenumber: +1 408 555 4321 -userpassword: supersecret - diff --git a/testing/sanity/src/test/resources/request/elaine.ldif b/testing/sanity/src/test/resources/request/elaine.ldif deleted file mode 100644 index 4c1efe5862c..00000000000 --- a/testing/sanity/src/test/resources/request/elaine.ldif +++ /dev/null @@ -1,16 +0,0 @@ -dn: uid=elaine,ou=People,dc=example,dc=com -uid: elaine -cn: Elaine Marley -sn: Marley -givenname: Elaine -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -l: Caribbean -employeeType: governor -title: Governor -businessCategory: state -mail: governor.marley@deep.in.the.caribbean.com -userpassword: piranhaDogs - diff --git a/testing/sanity/src/test/resources/request/gibbs-modify.ldif b/testing/sanity/src/test/resources/request/gibbs-modify.ldif deleted file mode 100644 index 15f2135fb96..00000000000 --- a/testing/sanity/src/test/resources/request/gibbs-modify.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: uid=jgibbs,ou=People,dc=example,dc=com -changetype: modify -replace: l -l: middle of nowhere -- diff --git a/testing/sanity/src/test/resources/request/herman.ldif b/testing/sanity/src/test/resources/request/herman.ldif deleted file mode 100644 index ec84efdf96a..00000000000 --- a/testing/sanity/src/test/resources/request/herman.ldif +++ /dev/null @@ -1,18 +0,0 @@ -dn: uid=herman,ou=People,dc=example,dc=com -uid: herman -cn: Herman Toothrot -cn: Horatio Torquemeda Marley -sn: Marley -sn: Toothrot -givenname: Herman -givenname: Horatio -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -l: Monkey Island -employeeType: governor -employeeType: hermit -mail: ht@monkeyisland.com -userpassword: creepyOldHermit - diff --git a/testing/sanity/src/test/resources/request/user-modify-activation-disable.xml b/testing/sanity/src/test/resources/request/user-modify-activation-disable.xml deleted file mode 100644 index 3a55053ed4a..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-activation-disable.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - - replace - c:activation/c:administrativeStatus - disabled - - diff --git a/testing/sanity/src/test/resources/request/user-modify-activation-enable.xml b/testing/sanity/src/test/resources/request/user-modify-activation-enable.xml deleted file mode 100644 index 897ec92722c..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-activation-enable.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - - replace - c:activation/c:administrativeStatus - enabled - - diff --git a/testing/sanity/src/test/resources/request/user-modify-add-account-derby.xml b/testing/sanity/src/test/resources/request/user-modify-add-account-derby.xml deleted file mode 100644 index 6d416dd542d..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-add-account-derby.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - add - c:linkRef - - - - - - - ri:AccountObjectClass - - jsparrow - Cpt. Jack Sparrow - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-add-account-legacy.xml b/testing/sanity/src/test/resources/request/user-modify-add-account-legacy.xml deleted file mode 100644 index 73ae3559957..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-add-account-legacy.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - add - c:linkRef - - - - - - - ri:AccountObjectClass - - uid=jack,ou=People,dc=example,dc=com - jack - Jack Sparrow - Jack - Sparrow - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-add-account.xml b/testing/sanity/src/test/resources/request/user-modify-add-account.xml deleted file mode 100644 index 34e2d17c195..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-add-account.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - add - c:linkRef - - - - - - - ri:inetOrgPerson - - uid=jack,ou=People,dc=example,dc=com - jack - Jack Sparrow - Jack - Sparrow - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-add-role-captain-1.xml b/testing/sanity/src/test/resources/request/user-modify-add-role-captain-1.xml deleted file mode 100644 index 0726abb7d01..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-add-role-captain-1.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111222 - - add - c:assignment - - - The Sea Monkey - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-add-role-captain-2.xml b/testing/sanity/src/test/resources/request/user-modify-add-role-captain-2.xml deleted file mode 100644 index 3021ebc8473..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-add-role-captain-2.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111222 - - add - c:assignment - - - The Dainty Lady - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-add-role-judge.xml b/testing/sanity/src/test/resources/request/user-modify-add-role-judge.xml deleted file mode 100644 index 635b1c389f9..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-add-role-judge.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111222 - - add - c:assignment - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-add-role-pirate.xml b/testing/sanity/src/test/resources/request/user-modify-add-role-pirate.xml deleted file mode 100644 index a1f8a7965ff..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-add-role-pirate.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111222 - - add - c:assignment - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-delete-role-captain-1.xml b/testing/sanity/src/test/resources/request/user-modify-delete-role-captain-1.xml deleted file mode 100644 index d48ab446a62..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-delete-role-captain-1.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111222 - - delete - c:assignment - - - The Sea Monkey - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-delete-role-captain-2.xml b/testing/sanity/src/test/resources/request/user-modify-delete-role-captain-2.xml deleted file mode 100644 index 939dee6cb66..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-delete-role-captain-2.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111222 - - delete - c:assignment - - - The Dainty Lady - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-delete-role-pirate.xml b/testing/sanity/src/test/resources/request/user-modify-delete-role-pirate.xml deleted file mode 100644 index d2fe6462577..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-delete-role-pirate.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111222 - - delete - c:assignment - - - - - diff --git a/testing/sanity/src/test/resources/request/user-modify-fullname-locality.xml b/testing/sanity/src/test/resources/request/user-modify-fullname-locality.xml deleted file mode 100644 index 216c2864c14..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-fullname-locality.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - - replace - c:fullName - Cpt. Jack Sparrow - - - - replace - c:locality - somewhere - - - - replace - c:employeeNumber - 1 - - diff --git a/testing/sanity/src/test/resources/request/user-modify-givenname.xml b/testing/sanity/src/test/resources/request/user-modify-givenname.xml deleted file mode 100644 index 1ff95edb04f..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-givenname.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - replace - c:givenName - - diff --git a/testing/sanity/src/test/resources/request/user-modify-name.xml b/testing/sanity/src/test/resources/request/user-modify-name.xml deleted file mode 100644 index 3d2d2880c87..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-name.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - replace - c:name - jsparrow - - diff --git a/testing/sanity/src/test/resources/request/user-modify-password.xml b/testing/sanity/src/test/resources/request/user-modify-password.xml deleted file mode 100644 index 90ca9c85495..00000000000 --- a/testing/sanity/src/test/resources/request/user-modify-password.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - modify - c:UserType - c0c010c0-d34d-b33f-f00d-111111111111 - - - replace - c:credentials/c:password/c:value - - butUnd3dM4yT4lkAL0t - - - diff --git a/testing/sanity/src/test/resources/request/will-modify.ldif b/testing/sanity/src/test/resources/request/will-modify.ldif deleted file mode 100644 index c916d6140ef..00000000000 --- a/testing/sanity/src/test/resources/request/will-modify.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: uid=wturner,ou=People,dc=example,dc=com -changetype: modify -replace: givenName -givenName: asdf -- \ No newline at end of file diff --git a/testing/sanity/src/test/resources/request/will-without-location.ldif b/testing/sanity/src/test/resources/request/will-without-location.ldif deleted file mode 100644 index 10d750e265c..00000000000 --- a/testing/sanity/src/test/resources/request/will-without-location.ldif +++ /dev/null @@ -1,14 +0,0 @@ -dn: uid=wturner1,ou=People,dc=example,dc=com -uid: wturner1 -cn: Will Turner -sn: Turner -givenname: Will -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -mail: will.turner@blackpearl.com -telephonenumber: +1 408 555 1234 -facsimiletelephonenumber: +1 408 555 4321 -userpassword: supersecret - diff --git a/testing/sanity/src/test/resources/request/will.ldif b/testing/sanity/src/test/resources/request/will.ldif deleted file mode 100644 index db793aa73f6..00000000000 --- a/testing/sanity/src/test/resources/request/will.ldif +++ /dev/null @@ -1,15 +0,0 @@ -dn: uid=wturner,ou=People,dc=example,dc=com -uid: wturner -cn: Will Turner -sn: Turner -givenname: Will -objectclass: top -objectclass: person -objectclass: organizationalPerson -objectclass: inetOrgPerson -l: Caribbean -mail: will.turner@blackpearl.com -telephonenumber: +1 408 555 1234 -facsimiletelephonenumber: +1 408 555 4321 -userpassword: supersecret - diff --git a/testing/sanity/src/test/resources/schema/extension-piracy.xsd b/testing/sanity/src/test/resources/schema/extension-piracy.xsd deleted file mode 100644 index 51217c95420..00000000000 --- a/testing/sanity/src/test/resources/schema/extension-piracy.xsd +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - 1 - - - - - diff --git a/testing/sanity/src/test/resources/schema/extension-whatever.xsd b/testing/sanity/src/test/resources/schema/extension-whatever.xsd deleted file mode 100644 index 8d0c476e098..00000000000 --- a/testing/sanity/src/test/resources/schema/extension-whatever.xsd +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - - diff --git a/testing/sanity/testng-integration.xml b/testing/sanity/testng-integration.xml deleted file mode 100644 index a0a33f51764..00000000000 --- a/testing/sanity/testng-integration.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - From a53ac9779cdc6fd86c8a6d1b790877c62e4c7d58 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 6 May 2020 09:35:13 +0200 Subject: [PATCH 23/41] TestModelWebServiceNegative: removed, there is no WS to test now --- .../negative/TestModelWebServiceNegative.java | 181 ------------------ .../model-intest/testng-integration-full.xml | 1 - 2 files changed, 182 deletions(-) delete mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/negative/TestModelWebServiceNegative.java diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/negative/TestModelWebServiceNegative.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/negative/TestModelWebServiceNegative.java deleted file mode 100644 index 80d89536b68..00000000000 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/negative/TestModelWebServiceNegative.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2010-2020 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.intest.negative; - -import java.io.File; -import java.util.Map; - -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.prism.marshaller.XNodeProcessorEvaluationMode; -import com.evolveum.midpoint.prism.xnode.PrimitiveXNode; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.annotation.DirtiesContext.ClassMode; -import org.springframework.test.context.ContextConfiguration; -import org.testng.AssertJUnit; -import org.testng.annotations.Test; - -import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest; -import com.evolveum.midpoint.prism.xnode.ValueParser; -import com.evolveum.midpoint.schema.constants.ObjectTypes; -import com.evolveum.midpoint.test.DummyResourceContoller; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectDeltaListType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultMessage; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultType; -import com.evolveum.midpoint.xml.ns._public.common.fault_3.SchemaViolationFaultType; -import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; -import com.evolveum.prism.xml.ns._public.types_3.ItemDeltaType; -import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import com.evolveum.prism.xml.ns._public.types_3.ModificationTypeType; -import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; -import com.evolveum.prism.xml.ns._public.types_3.RawType; - -/** - * @author semancik - * - */ -@ContextConfiguration(locations = {"classpath:ctx-model-intest-test-main.xml"}) -@DirtiesContext(classMode = ClassMode.AFTER_CLASS) -public class TestModelWebServiceNegative extends AbstractInitializedModelIntegrationTest { - - public static final File TEST_DIR = new File("src/test/resources/crud"); - - /** - * First tests are positive, to make sure that this method works. - */ - @Test - public void test100ModifyAccountExplicitType() throws Exception { - // GIVEN - ObjectDeltaType objectChange = createShadowReplaceChange(ACCOUNT_SHADOW_GUYBRUSH_OID, - "attributes/"+DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, - "foo", DOMUtil.XSD_STRING); - ObjectDeltaListType deltaList = new ObjectDeltaListType(); - deltaList.getDelta().add(objectChange); - - // WHEN - modelWeb.executeChanges(deltaList, null); - - // THEN - - // Check account in dummy resource - assertDefaultDummyAccount(ACCOUNT_GUYBRUSH_DUMMY_USERNAME, ACCOUNT_GUYBRUSH_DUMMY_FULLNAME, true); - assertDummyAccountAttribute(null, ACCOUNT_GUYBRUSH_DUMMY_USERNAME, - DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, "foo"); - } - - /** - * First tests are positive, to make sure that this method works. - */ - @Test - public void test110ModifyAccountImplicitType() throws Exception { - // GIVEN - ObjectDeltaType objectChange = createShadowReplaceChange(ACCOUNT_SHADOW_GUYBRUSH_OID, - "attributes/"+DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, - "bar", null); - ObjectDeltaListType deltaList = new ObjectDeltaListType(); - deltaList.getDelta().add(objectChange); - - // WHEN - modelWeb.executeChanges(deltaList, null); - - // THEN - - // Check account in dummy resource - assertDefaultDummyAccount(ACCOUNT_GUYBRUSH_DUMMY_USERNAME, ACCOUNT_GUYBRUSH_DUMMY_FULLNAME, true); - assertDummyAccountAttribute(null, ACCOUNT_GUYBRUSH_DUMMY_USERNAME, - DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, "bar"); - } - - @Test - public void test200ModifyAccountWrongExplicitType() { - given(); - ObjectDeltaType objectChange = createShadowReplaceChange(ACCOUNT_SHADOW_GUYBRUSH_OID, - "attributes/"+DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_GOSSIP_NAME, - "42", DOMUtil.XSD_INT); - ObjectDeltaListType deltaList = new ObjectDeltaListType(); - deltaList.getDelta().add(objectChange); - - expect(); - assertExecuteChangesFailure(deltaList, null, SchemaViolationFaultType.class, "Expected", "but got class"); - } - - - private void assertExecuteChangesFailure( - ObjectDeltaListType deltaList, ModelExecuteOptionsType options, - Class expectedFaultTypeClass, String... messagePatterns) { - - try { - modelWeb.executeChanges(deltaList, options); - - AssertJUnit.fail("Unexpected success"); - } catch (FaultMessage f) { - FaultType faultInfo = f.getFaultInfo(); - if (expectedFaultTypeClass.isAssignableFrom(faultInfo.getClass())) { - // This is expected - String message = f.getMessage(); - for (String pattern: messagePatterns) { - if (!message.contains(pattern)) { - AssertJUnit.fail("Exception message does not contain pattern '"+pattern+"': "+message); - } - } - } else { - AssertJUnit.fail("Expected fault type of "+expectedFaultTypeClass+" but got "+faultInfo.getClass()); - } - } - } - - // TODO: more negative tests - - private ObjectDeltaType createShadowReplaceChange(String oid, String path, final String value, QName type) { - ObjectDeltaType objectChange = new ObjectDeltaType(); - objectChange.setOid(oid); - objectChange.setChangeType(ChangeTypeType.MODIFY); - objectChange.setObjectType(ObjectTypes.SHADOW.getTypeQName()); - ItemDeltaType itemDeltaType = new ItemDeltaType(); - itemDeltaType.setModificationType(ModificationTypeType.REPLACE); - ItemPathType itemPath = prismContext.itemPathParser().asItemPathType(path); - itemDeltaType.setPath(itemPath); - ValueParser valueParser = new ValueParser() { - @Override - public String parse(QName typeName, XNodeProcessorEvaluationMode mode) { - return value; - } - - @Override - public boolean canParseAs(QName typeName) { - return true; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public String getStringValue() { - return value; - } - - @Override - public Map getPotentiallyRelevantNamespaces() { - throw new UnsupportedOperationException(); - } - - @Override - public ValueParser freeze() { - return this; - } - }; - PrimitiveXNode xnode = prismContext.xnodeFactory().primitive(valueParser, type, type != null); - RawType rawValue = new RawType(xnode, prismContext); - itemDeltaType.getValue().add(rawValue); - objectChange.getItemDelta().add(itemDeltaType); - return objectChange; - } -} diff --git a/model/model-intest/testng-integration-full.xml b/model/model-intest/testng-integration-full.xml index 4b53fe97af3..fa9ab6e983c 100644 --- a/model/model-intest/testng-integration-full.xml +++ b/model/model-intest/testng-integration-full.xml @@ -125,7 +125,6 @@ - From 1dcf9fad8485a02c638900e1ca97045fe7b173a2 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 6 May 2020 09:36:39 +0200 Subject: [PATCH 24/41] AbstractModelIntegrationTest: removed @Autowired ModelPortType modelWeb --- .../midpoint/model/test/AbstractModelIntegrationTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index 077c4edaaeb..9834602bf09 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -128,7 +128,6 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.midpoint.xml.ns._public.model.model_3.ModelPortType; import com.evolveum.prism.xml.ns._public.types_3.*; /** @@ -164,7 +163,6 @@ public abstract class AbstractModelIntegrationTest extends AbstractIntegrationTe @Autowired protected ModelDiagnosticService modelDiagnosticService; @Autowired protected DashboardService dashboardService; @Autowired protected ModelAuditService modelAuditService; - @Autowired protected ModelPortType modelWeb; @Autowired @Qualifier("cacheRepositoryService") From 8190d0d902041dd73d3c07d2f80e2ec854e36f33 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 6 May 2020 14:57:53 +0200 Subject: [PATCH 25/41] Introduce typed bulk actions This is to make bulk actions easier to create and understand. This commit also introduces optimized "recompute" action that schedules a recomputation to later time using triggers. This is related to "linked objects". (See MID-6109 and MID-5967.) --- .../midpoint/prism/xml/XmlTypeConverter.java | 11 +- .../xml/ns/public/common/common-core-3.xsd | 52 +- .../ns/public/model/scripting/scripting-3.xsd | 1759 ++++++---- .../caching/AbstractThreadLocalCache.java | 2 +- .../model/api/expr/MidpointFunctions.java | 7 + .../model/api/util/ResourceUtils.java | 17 +- model/model-impl/pom.xml | 4 + .../impl/expr/MidpointFunctionsImpl.java | 14 + .../model/impl/scripting/ActionExecutor.java | 20 +- .../model/impl/scripting/PipelineData.java | 85 +- .../ScriptingActionExecutorRegistry.java | 53 + .../ScriptingExpressionEvaluator.java | 71 +- .../AbstractObjectBasedActionExecutor.java | 91 + .../impl/scripting/actions/AddExecutor.java | 78 +- .../actions/ApplyDefinitionExecutor.java | 69 +- .../scripting/actions/AssignExecutor.java | 195 +- .../actions/AssignmentOperationsExecutor.java | 191 +- .../scripting/actions/BaseActionExecutor.java | 140 +- .../scripting/actions/DeleteExecutor.java | 75 +- .../actions/DiscoverConnectorsExecutor.java | 23 +- .../actions/EnableDisableExecutor.java | 130 +- .../actions/ExecuteScriptExecutor.java | 298 ++ .../actions/GenerateValueExecutor.java | 87 +- .../impl/scripting/actions/LogExecutor.java | 55 +- .../scripting/actions/ModifyExecutor.java | 151 +- .../scripting/actions/NotifyExecutor.java | 93 +- .../actions/PurgeSchemaExecutor.java | 106 +- .../scripting/actions/RecomputeExecutor.java | 117 +- .../scripting/actions/ReencryptExecutor.java | 21 +- .../scripting/actions/ResolveExecutor.java | 112 +- .../scripting/actions/ResumeExecutor.java | 76 - .../scripting/actions/ResumeTaskExecutor.java | 60 + .../scripting/actions/ScriptExecutor.java | 230 -- .../actions/TestResourceExecutor.java | 93 +- .../scripting/actions/UnassignExecutor.java | 150 +- .../scripting/actions/ValidateExecutor.java | 16 +- .../expressions/SearchEvaluator.java | 7 +- .../scripting/helpers/ExpressionHelper.java | 77 +- .../scripting/helpers/OperationsHelper.java | 77 +- .../scripting/helpers/ScriptingBeansUtil.java | 117 + .../scripting/helpers/ScriptingDataUtil.java | 53 + .../scripting/helpers/ScriptingJaxbUtil.java | 57 - ...c.java => AbstractBasicScriptingTest.java} | 1027 +++--- .../scripting/TestScriptingBasicLegacy.java | 19 + .../scripting/TestScriptingBasicNew.java | 175 + ...-captain-and-dummy-red-to-jack-legacy.xml} | 0 .../assign-captain-and-dummy-red-to-jack.xml | 22 + .../assign-captain-by-name-to-jack.xml | 29 + ...gn-nice-pirate-by-name-to-jack-legacy.xml} | 0 .../assign-nice-pirate-by-name-to-jack.xml | 28 + ... assign-pirate-manager-to-will-legacy.xml} | 0 .../assign-pirate-manager-to-will.xml | 21 + ...irate-relation-captain-to-will-legacy.xml} | 0 ...assign-pirate-relation-captain-to-will.xml | 22 + .../assign-to-jack-dry-and-raw-legacy.xml | 38 + .../scripting/assign-to-jack-dry-and-raw.xml | 30 +- .../scripting/delete-and-add-jack-legacy.xml | 27 + .../scripting/delete-and-add-jack.xml | 21 +- .../scripting/disable-jack-legacy.xml | 24 + .../test/resources/scripting/disable-jack.xml | 21 +- .../test/resources/scripting/echo-legacy.xml | 26 + .../scripting/enable-jack-legacy.xml | 21 + .../test/resources/scripting/enable-jack.xml | 4 +- .../scripting/generate-passwords-2-legacy.xml | 33 + .../scripting/generate-passwords-2.xml | 10 +- .../scripting/generate-passwords-3-legacy.xml | 44 + .../scripting/generate-passwords-3.xml | 28 +- .../scripting/generate-passwords-legacy.xml | 21 + .../scripting/generate-passwords.xml | 4 +- .../test/resources/scripting/log-legacy.xml | 18 + .../src/test/resources/scripting/log.xml | 13 +- .../scripting/modify-jack-back-legacy.xml | 36 + .../resources/scripting/modify-jack-back.xml | 26 +- .../scripting/modify-jack-legacy.xml | 35 + .../scripting/modify-jack-password-legacy.xml | 37 + .../modify-jack-password-task-legacy.xml | 55 + .../scripting/modify-jack-password-task.xml | 30 +- .../scripting/modify-jack-password.xml | 26 +- .../test/resources/scripting/modify-jack.xml | 47 +- .../notification-about-jack-legacy.xml | 42 + .../notification-about-jack-type2-legacy.xml | 58 + .../notification-about-jack-type2.xml | 66 +- .../scripting/notification-about-jack.xml | 51 +- .../purge-dummy-black-schema-legacy.xml | 21 + .../scripting/purge-dummy-black-schema.xml | 11 +- .../scripting/recompute-jack-legacy.xml | 21 + .../recompute-jack-new-trigger-direct.xml | 22 + .../recompute-jack-new-trigger-optimized.xml | 24 + .../resources/scripting/recompute-jack.xml | 23 +- .../resume-suspended-tasks-legacy.xml | 25 + .../scripting/resume-suspended-tasks.xml | 24 +- ...ting-users-in-background-assign-legacy.xml | 61 + .../scripting-users-in-background-assign.xml | 62 +- ...rs-in-background-iterative-task-legacy.xml | 44 + ...ing-users-in-background-iterative-task.xml | 35 +- .../scripting-users-in-background-legacy.xml | 61 + .../scripting-users-in-background-task.xml | 32 +- .../scripting-users-in-background.xml | 49 +- .../scripting/scripting-users-legacy.xml | 32 + .../resources/scripting/scripting-users.xml | 36 +- .../search-for-shadows-nofetch-legacy.xml | 32 + .../start-tasks-from-template-legacy.xml | 37 + .../scripting/start-tasks-from-template.xml | 41 +- .../scripting/task-to-keep-suspended.xml | 11 +- .../resources/scripting/task-to-resume.xml | 7 +- .../scripting/test-dummy-resource-legacy.xml | 21 + .../scripting/test-dummy-resource.xml | 4 +- .../scripting/unassign-all-from-jack.xml | 23 + .../scripting/unassign-captain-from-jack.xml | 26 + ...ssign-dummy-resource-from-will-legacy.xml} | 0 .../unassign-dummy-resource-from-will.xml | 26 + ...ssign-pirate-default-from-will-legacy.xml} | 1 + .../unassign-pirate-default-from-will.xml | 25 + ...te-manager-and-owner-from-will-legacy.xml} | 0 ...ign-pirate-manager-and-owner-from-will.xml | 32 + .../scripting/use-variables-legacy.xml | 88 + .../resources/scripting/use-variables.xml | 63 +- .../model-intest/testng-integration-full.xml | 3 +- .../test/AbstractModelIntegrationTest.java | 13 +- .../provisioning/impl/ResourceCache.java | 10 +- .../provisioning/impl/ResourceManager.java | 2935 ++++++++--------- .../sync/ChangeProcessingCoordinator.java | 2 +- .../ConnIdCapabilitiesAndSchemaParser.java | 2 +- .../repo/cache/global/GlobalObjectCache.java | 5 +- .../test/asserter/AssignmentAsserter.java | 11 + .../test/asserter/AssignmentFinder.java | 13 +- .../test/asserter/ResourceAsserter.java | 8 + .../midpoint/schema/xjc/PrefixMapper.java | 2 + 128 files changed, 7035 insertions(+), 4364 deletions(-) create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingActionExecutorRegistry.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AbstractObjectBasedActionExecutor.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ExecuteScriptExecutor.java delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeExecutor.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeTaskExecutor.java delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingBeansUtil.java create mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingDataUtil.java delete mode 100644 model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingJaxbUtil.java rename model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/{TestScriptingBasic.java => AbstractBasicScriptingTest.java} (67%) create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicLegacy.java create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicNew.java rename model/model-intest/src/test/resources/scripting/{assign-to-jack.xml => assign-captain-and-dummy-red-to-jack-legacy.xml} (100%) create mode 100644 model/model-intest/src/test/resources/scripting/assign-captain-and-dummy-red-to-jack.xml create mode 100644 model/model-intest/src/test/resources/scripting/assign-captain-by-name-to-jack.xml rename model/model-intest/src/test/resources/scripting/{assign-to-jack-2.xml => assign-nice-pirate-by-name-to-jack-legacy.xml} (100%) create mode 100644 model/model-intest/src/test/resources/scripting/assign-nice-pirate-by-name-to-jack.xml rename model/model-intest/src/test/resources/scripting/{assign-to-will.xml => assign-pirate-manager-to-will-legacy.xml} (100%) create mode 100644 model/model-intest/src/test/resources/scripting/assign-pirate-manager-to-will.xml rename model/model-intest/src/test/resources/scripting/{assign-to-will-2.xml => assign-pirate-relation-captain-to-will-legacy.xml} (100%) create mode 100644 model/model-intest/src/test/resources/scripting/assign-pirate-relation-captain-to-will.xml create mode 100644 model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/delete-and-add-jack-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/disable-jack-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/echo-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/enable-jack-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/generate-passwords-2-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/generate-passwords-3-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/generate-passwords-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/log-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/modify-jack-back-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/modify-jack-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/modify-jack-password-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/modify-jack-password-task-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/notification-about-jack-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/notification-about-jack-type2-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/purge-dummy-black-schema-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/recompute-jack-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-direct.xml create mode 100644 model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-optimized.xml create mode 100644 model/model-intest/src/test/resources/scripting/resume-suspended-tasks-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/scripting-users-in-background-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/scripting-users-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/search-for-shadows-nofetch-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/start-tasks-from-template-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/test-dummy-resource-legacy.xml create mode 100644 model/model-intest/src/test/resources/scripting/unassign-all-from-jack.xml create mode 100644 model/model-intest/src/test/resources/scripting/unassign-captain-from-jack.xml rename model/model-intest/src/test/resources/scripting/{unassign-from-will-3.xml => unassign-dummy-resource-from-will-legacy.xml} (100%) create mode 100644 model/model-intest/src/test/resources/scripting/unassign-dummy-resource-from-will.xml rename model/model-intest/src/test/resources/scripting/{unassign-from-will.xml => unassign-pirate-default-from-will-legacy.xml} (95%) create mode 100644 model/model-intest/src/test/resources/scripting/unassign-pirate-default-from-will.xml rename model/model-intest/src/test/resources/scripting/{unassign-from-will-2.xml => unassign-pirate-manager-and-owner-from-will-legacy.xml} (100%) create mode 100644 model/model-intest/src/test/resources/scripting/unassign-pirate-manager-and-owner-from-will.xml create mode 100644 model/model-intest/src/test/resources/scripting/use-variables-legacy.xml diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xml/XmlTypeConverter.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xml/XmlTypeConverter.java index 4bcfc6cd21c..a288a974b8b 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xml/XmlTypeConverter.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/xml/XmlTypeConverter.java @@ -190,11 +190,20 @@ public static XMLGregorianCalendar fromNow(String timeSpec) { } public static XMLGregorianCalendar fromNow(Duration duration) { - XMLGregorianCalendar rv = createXMLGregorianCalendar(System.currentTimeMillis()); + return fromNow(System.currentTimeMillis(), duration); + } + + public static XMLGregorianCalendar fromNow(long now, Duration duration) { + XMLGregorianCalendar rv = createXMLGregorianCalendar(now); rv.add(duration); return rv; } + public static long toMillis(Duration duration) { + long now = System.currentTimeMillis(); + return toMillis(fromNow(now, duration)) - now; + } + public static Duration createDuration(long durationInMilliSeconds) { return getDatatypeFactory().newDuration(durationInMilliSeconds); } diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index 32ecd3a5135..676434c18b7 100755 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -20005,7 +20005,6 @@ - @@ -20065,13 +20064,6 @@ - - - - - - - @@ -26556,5 +26548,49 @@ + + + + + Deals with creation of recompute triggers for selected objects. + These triggers can be created in "raw" way, simply by adding respective + TriggerType objects. Or, they can be created in so called optimized way, where + we look if a compatible trigger already exists and skip trigger creation in such + case. + + This structure deals with recompute triggers for now. In might be generalized + in the future. + + + 4.0 + true + true + + + + + + + How long after current time the trigger should be fired. When not specified, + trigger is created unconditionally ("raw" way), with the fire time being + equal to the current time. If specified, the trigger is created with the + fire time in the future; skipping the creation if compatible trigger was + created and still has not been fired. + + + + + + + What is the safety margin, i.e. how many long before the trigger fire time + we need to create a new trigger. This is to avoid (rare but in theory possible) + race conditions where we skip creation of a trigger but it fires in the meanwhile. + + + + + + + diff --git a/infra/schema/src/main/resources/xml/ns/public/model/scripting/scripting-3.xsd b/infra/schema/src/main/resources/xml/ns/public/model/scripting/scripting-3.xsd index e8785af176f..7240ed0f14f 100644 --- a/infra/schema/src/main/resources/xml/ns/public/model/scripting/scripting-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/model/scripting/scripting-3.xsd @@ -1,603 +1,1156 @@ - - - - - - - - - Schema for midPoint scripting language. - - Recommended namespace prefix: s - - - - - - - Common prism annotations used in various XSD schemas. - - - - - - - - Prism data types. - - - - - - - - Prism data types. - - - - - - - - Common midPoint schema. - - - - - - - - Options related to evaluation of scripting expression. - EXPERIMENTAL - In the future, these may become part of any scripting expression, allowing parts of a complex expression - to be evaluated differently from its other parts. - - - - - - - - - Causes evaluation to continue even in the presence of any errors. - TODO make this more elaborate w.r.t. kind of error(s) encountered. - - - - - - - Hides operation results from the output data. - - - - - - - - - - Definition of scripting variables. - - - 3.7 - - - - - - - - - - - - Definition of a scripting variable. - Expression types other than path-based ones are to be considered EXPERIMENTAL. - - - 3.7 - - - - - - - - - Type of the variable. Can be omitted for path expressions. EXPERIMENTAL. - - - - - - - Multiplicity specification. Can be omitted for path expressions. EXPERIMENTAL. - - - - - - - - - - - - - Root of the expression type inheritance hierarchy. - - - - - - - - - - Sequence of command expressions - they are executed one after another, - input sent to the sequence as a whole is then sent individually - to each expression. Output of the last expression is considered to be the output of - the whole sequence. - - - - - - - - - - - - - - - - - - - - - - - Pipeline of expressions - they are executed one after another, - input sent to the pipeline as a whole is sent to the first expression. - Output from the N-th expression is sent as an input to the N+1-th expression. - Output of the last expression is considered to be the output of the whole - pipeline. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Queries the model for objects of a given type, optionally fulfilling given condition. - - - - - - - - - Type whose instances are searched for. - - - - - - - Variable to hold found instances. Not yet supported. - - - - - - - Query to apply when searching for instances. (Alternative to searchFilter. This is tried as the first one.) - - - - - - - Filter to apply when searching for instances. (Alternative to query. This is tried as second one.) - - - - - - - Filter to apply when searching for instances. (Alternative to query. This is tried as second one.) - - - - - - - Action parameters. - - - - - - - Expression to evaluate for each object found. - - - - - - - Whether to aggregate and pass forward the output of expression evaluations that are done - for each object found. (Meaningful only if scriptingExpression is specified.) - Default is true for compatibility reasons. Set to false to optimize memory consumption. - - - 3.7.1 - - - - - - - - - - - - - - - - - - - Filters input on a given condition. - - - - - - - - - Filter to apply to the input stream. - - - - - - - - - - - - - - - - - - - Select given item. - - - - - - - - - Path to the data item that has to be selected. - - - - - - - - - - - - - - - - - - - Keeps/removes selected items from the input value. - - - - - - - - - Path to the data item(s) that should be kept. - - - - - - - Path to the data item(s) that should be removed. - - - - - - - - - - - - - - - - - - - Executes a given command individually for each item arriving at the input. - - - - - - - - - Variable to hold emitted instances. - - - - - - - - - - - - - - - - - - - - Executes a given action (add, modify, delete, enable, disable, assign, ...) - - - - - - - - - Action to execute. - - - - - - - Action parameters. - - - - - - - - - - - - - - - - - - - Value of a parameter for an action. - - - - - - - Parameter name. - - - - - - - - - Parameter (argument) value. - - - - - - - - - - - - - - - - - - 3.7 - - - - - - - - - - - Output of a single script execution. - - - 3.6 - - - - - - - Data output of the execution of the script. - - - - - - - Text ("console") output of the execution of the script. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TODO - - - - - - - - - - TODO - - - - - - - + + + + + + + + + Schema for midPoint scripting language. + + Recommended namespace prefix: s + + + + + + + Common prism annotations used in various XSD schemas. + + + + + + + + Prism data types. + + + + + + + + Prism data types. + + + + + + + + Common midPoint schema. + + + + + + + + + + Options related to evaluation of scripting expression. + EXPERIMENTAL + In the future, these may become part of any scripting expression, allowing parts of a complex expression + to be evaluated differently from its other parts. + + + + + + + + + Causes evaluation to continue even in the presence of any errors. + TODO make this more elaborate w.r.t. kind of error(s) encountered. + + + + + + + Hides operation results from the output data. + + + + + + + + + + Definition of scripting variables. + + + 3.7 + + + + + + + + + + + + Definition of a scripting variable. + Expression types other than path-based ones are to be considered EXPERIMENTAL. + + + 3.7 + + + + + + + + + Type of the variable. Can be omitted for path expressions. EXPERIMENTAL. + + + + + + + Multiplicity specification. Can be omitted for path expressions. EXPERIMENTAL. + + + + + + + + + + + + + Root of the expression type inheritance hierarchy. + + + + + + + + + + Sequence of command expressions - they are executed one after another, + input sent to the sequence as a whole is then sent individually + to each expression. Output of the last expression is considered to be the output of + the whole sequence. + + + + + + + + + + + + + + + + + + + + + + + Pipeline of expressions - they are executed one after another, + input sent to the pipeline as a whole is sent to the first expression. + Output from the N-th expression is sent as an input to the N+1-th expression. + Output of the last expression is considered to be the output of the whole + pipeline. + + + + + + + + + + + + + + + + + + + + + + + Queries the model for objects of a given type, optionally fulfilling given condition. + + + + + + + + + Type whose instances are searched for. + + + + + + + Variable to hold found instances. Not yet supported. + + + + + + + Query to apply when searching for instances. (Alternative to searchFilter. This is tried as the first one.) + + + + + + + Filter to apply when searching for instances. (Alternative to query. This is tried as second one.) + + + + + + + Options to use when executing the search. + + + + + + + Action parameters. + + + + + + + Expression to evaluate for each object found. + + + + + + + Whether to aggregate and pass forward the output of expression evaluations that are done + for each object found. (Meaningful only if scriptingExpression is specified.) + Default is true for compatibility reasons. Set to false to optimize memory consumption. + + + 3.7.1 + + + + + + + + + + + + + + + + + + + Filters input on a given condition. + + + + + + + + + Filter to apply to the input stream. + + + + + + + + + + + + + + + + + + + Select given item. + + + + + + + + + Path to the data item that has to be selected. + + + + + + + + + + + + + + + + + + + Keeps/removes selected items from the input value. + + + + + + + + + Path to the data item(s) that should be kept. + + + + + + + Path to the data item(s) that should be removed. + + + + + + + + + + + + + + + + + + + Executes a given command individually for each item arriving at the input. + + + + + + + + + Variable to hold emitted instances. + + + + + + + + + + + + + + + + + + + + Executes a given action (add, modify, delete, enable, disable, assign, ...) + + + + + + + + + Action to execute. + + + + + + + Action parameters. + + + + + + + + + + + + + + + + + + + Value of a parameter for an action. + + + + + + + Parameter name. + + + + + + + + + Parameter (argument) value. + + + + + + + + + + + Supertype for "execution" actions, carrying some common properties. + + + 4.2 + true + + + + + + + + + Model execute options. + + + + + + + Should the action be only previewed (run as "dry")? + + + + + + + + + + + + Statically-typed "add" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + + Statically-typed "apply-definition" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + Statically-typed "assign" action. Its parameters differ significantly from the + parameters of dynamically-type assign action, because the latter ones were not + designed well. + + + 4.2 + true + + + + + + + + + Target(s) to be assigned. May contain filters. If such filters are present, they + are evaluated at action execution time. (This behavior may change in the future!) + If relations other than default are to be used, they should be provided within the reference(s). + + + + + + + Resource(s) to be assigned. May contain filters. If such filters are present, they + are evaluated at action execution time. (This behavior may change in the future!) + + + + + + + Resource object constructions to be assigned. These are assigned "as is", with no + filter evaluation. (This behavior may change in the future!) + + + + + + + + + + + + + + + + + + + Statically-typed "delete" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + + Statically-typed "disable" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + + Statically-typed "enable" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + + Statically-typed "execute-script" action. + + + 4.2 + true + + + + + + + + + Script to be executed. + + + + + + + If the script produces any output, this is a specification of an item (property, reference, + container, or object) that provides a definition for the output. EXPERIMENTAL. + + + + + + + If the script produces any output, this is a specification of a simple or complex type + that provides a definition for the output. EXPERIMENTAL. + + + + + + + Should we execute the script for the whole input (true) or for each individual input item (false)? + + + + + + + If true, console output is suppressed. + + + + + + + + + + + + + + + + + + + Statically-typed "generate-values" action. + + + 4.2 + true + + + + + + + + + Defined items and rules for generating/validating values. + + + + + + + + + + + + + + + + + + + Statically-typed "log" action. + + + 4.2 + true + + + + + + + + + Message to be logged. + + + + + + + Level on which the message should be logged. + We use xsd:string here just for compatibility with the + dynamic variant. In the future we will switch to c:LoggingLevelType. + + + + + + + + + + + + + + + + + + + Statically-typed "modify" action. + + + 4.2 + true + + + + + + + + + Delta to be applied. + + + + + + + + + + + + + + + + + + + Statically-typed "notify" action. + + + 4.2 + true + + + + + + + + + Subtype of the event created: Free-text information about category (type) + of this custom event. + + + + + + + Operation to be put into event (add, modify, delete). Default is "add". + + + + + + + Status to be put into event (success, failure, inProgress, alsoSuccess, onlyFailure). + Default is "success". + + + + + + + Ad-hoc event handler that should be used to process the event. + If not specified, system configuration is used to determine the handler(s). + + + + + + + Whole input (i.e. all items in the pipeline) should be sent as event object. + The default behavior is to generate one event for each input object. + + + + + + + + + + + + + + + + + + + Statically-typed "purge-schema" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + Statically-typed "recompute" action. + + + 4.2 + true + + + + + + + + + Does not invoke recompute immediately but uses a trigger to do that. + + + + + + + + + + + + + + + + + + + Statically-typed "resolve" action. + + + 4.2 + true + + + + + + + + + Options to use when getting the object(s). + + + + + + + + + + + + + + + + + + + Statically-typed "resume" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + Statically-typed "test-resource" action. + + + 4.2 + true + + + + + + + + + + + + + + + + + + Statically-typed "unassign" action. + + + 4.2 + true + + + + + + + + + Filter matching assignments to be deleted. + + + + + + + + + + + + + + + + + + + + + + + + 3.7 + + + + + + + + + + + Output of a single script execution. + + + 3.6 + + + + + + + Data output of the execution of the script. + + + + + + + Text ("console") output of the execution of the script. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TODO + + + + + + + + + + TODO + + + + + + + diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java b/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java index 995da87f35e..db454a86f4a 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/caching/AbstractThreadLocalCache.java @@ -30,7 +30,7 @@ public static T enter(ConcurrentHashMap ResourceAttributeDefinition getAttributeDefinition(PrismObject ResourceAttributeDefinition getAttributeDefinition(PrismObject resource, String objectClassName, String attributeName) throws SchemaException; + + /** + * Goes directly to repository service. + */ + @Experimental + void createRecomputeTrigger(Class type, String oid) throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException; } diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ResourceUtils.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ResourceUtils.java index 3c2917c7768..0f6ebb0f994 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ResourceUtils.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/util/ResourceUtils.java @@ -16,7 +16,6 @@ import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.XmlSchemaType; -import com.evolveum.prism.xml.ns._public.types_3.SchemaDefinitionType; import static java.util.Collections.singleton; @@ -27,17 +26,13 @@ */ public class ResourceUtils { - private static final ItemPath SCHEMA_PATH = ItemPath.create(ResourceType.F_SCHEMA, XmlSchemaType.F_DEFINITION); - - public static void deleteSchema(PrismObject resource, ModelService modelService, PrismContext prismContext, Task task, OperationResult parentResult) + public static void deleteSchema(PrismObject resource, ModelService modelService, PrismContext prismContext, Task task, OperationResult parentResult) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException { - PrismProperty definition = resource.findProperty(SCHEMA_PATH); - if (definition != null && !definition.isEmpty()) { - ObjectDelta delta = prismContext.deltaFor(ResourceType.class) - .item(SCHEMA_PATH).replace() - .asObjectDelta(resource.getOid()); - modelService.executeChanges(singleton(delta), null, task, parentResult); - } + ObjectDelta delta = prismContext.deltaFor(ResourceType.class) + .item(ItemPath.create(ResourceType.F_SCHEMA, XmlSchemaType.F_DEFINITION)).replace() + .item(ItemPath.create(ResourceType.F_SCHEMA, XmlSchemaType.F_CACHING_METADATA)).replace() + .asObjectDelta(resource.getOid()); + modelService.executeChanges(singleton(delta), null, task, parentResult); } } diff --git a/model/model-impl/pom.xml b/model/model-impl/pom.xml index a9033f1a2de..87532da30f3 100644 --- a/model/model-impl/pom.xml +++ b/model/model-impl/pom.xml @@ -166,6 +166,10 @@ org.apache.commons commons-text + + commons-beanutils + commons-beanutils + org.apache.cxf cxf-core diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java index ef641438565..cbcb601a310 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/expr/MidpointFunctionsImpl.java @@ -2064,4 +2064,18 @@ private T getFocusObjectAny() { } return object.asObjectable(); } + + @Override + public void createRecomputeTrigger(Class type, String oid) throws SchemaException, + ObjectAlreadyExistsException, ObjectNotFoundException { + OperationResult result = getCurrentResult(MidpointFunctions.class.getName() + ".createRecomputeTrigger"); + + TriggerType trigger = new TriggerType(prismContext) + .handlerUri(RecomputeTriggerHandler.HANDLER_URI) + .timestamp(XmlTypeConverter.createXMLGregorianCalendar()); + List> itemDeltas = prismContext.deltaFor(type) + .item(ObjectType.F_TRIGGER).add(trigger) + .asItemDeltas(); + repositoryService.modifyObject(type, oid, itemDeltas, result); + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ActionExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ActionExecutor.java index d48c07bf29e..7685634d2b1 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ActionExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ActionExecutor.java @@ -9,12 +9,11 @@ import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; /** * Executes an action of a given type. Instances of this type must be registered with ScriptingExpressionEvaluator. - * - * @author mederly */ @FunctionalInterface public interface ActionExecutor { @@ -22,9 +21,18 @@ public interface ActionExecutor { /** * Executes given action command. * - * @param command - * @param context - * @param parentResult + * @param command Command to be executed. Its parameters can be defined statically (using "new" specific subclasses + * in the schema) or dynamically (using "old fashioned" dynamic name-value parameters) or in a mixed style, where + * dynamic definitions take precedence. + * + * @param input Input data (pipeline) that the action has to be executed on. + * + * @param context Overall execution context. + * + * @param globalResult Global operation result. This is the parent result that receives subresults related to + * actions executions. (But individual results are stored also into the pipeline, to indicate success/failure of + * individual pipeline items processing.) */ - PipelineData execute(ActionExpressionType command, PipelineData input, ExecutionContext context, OperationResult parentResult) throws ScriptExecutionException; + PipelineData execute(ActionExpressionType command, PipelineData input, ExecutionContext context, OperationResult globalResult) + throws ScriptExecutionException, SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java index bdc692dfb22..330f7d894b4 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/PipelineData.java @@ -9,6 +9,7 @@ import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.helpers.ScriptingDataUtil; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.prism.query.ObjectQuery; import com.evolveum.midpoint.schema.SearchResultList; @@ -32,42 +33,40 @@ /** * Data that are passed between individual scripting actions. - * + *

* The content passed between actions (expressions) is a list of prism values - * (object, container, reference, property). - * - * @author mederly + * (object, container, reference, property) enriched with some additional information, + * see {@link PipelineItem}. */ public class PipelineData implements DebugDumpable { private static final String ITEM_OPERATION_NAME = ScriptingExpressionEvaluator.class.getName() + ".process"; - private final List data = new ArrayList<>(); // all items are not null + /** + * The data in the pipeline. All items are not null. + */ + @NotNull private final List data = new ArrayList<>(); // we want clients to use explicit constructors private PipelineData() { } - public List getData() { + public @NotNull List getData() { return data; } - @Override - public String debugDump(int indent) { - return DebugUtil.debugDump(data, indent); - } - public static PipelineData create(PrismValue value) { - return create(value, VariablesMap.emptyMap()); - } - - public static PipelineData create(PrismValue value, VariablesMap variables) { + public @NotNull static PipelineData create(@NotNull PrismValue value, @NotNull VariablesMap variables) { PipelineData d = createEmpty(); d.add(new PipelineItem(value, newOperationResult(), variables)); return d; } - public static OperationResult newOperationResult() { + public @NotNull static PipelineData createEmpty() { + return new PipelineData(); + } + + public @NotNull static OperationResult newOperationResult() { return new OperationResult(ITEM_OPERATION_NAME); } @@ -75,10 +74,6 @@ public void add(@NotNull PipelineItem pipelineItem) { data.add(pipelineItem); } - public static PipelineData createEmpty() { - return new PipelineData(); - } - public void addAllFrom(PipelineData otherData) { if (otherData != null) { data.addAll(otherData.getData()); @@ -95,39 +90,38 @@ public void addValue(PrismValue value, OperationResult result, VariablesMap vari variables != null ? variables : VariablesMap.emptyMap())); } - public String getDataAsSingleString() throws ScriptExecutionException { - if (!data.isEmpty()) { - if (data.size() == 1) { - return (String) ((PrismPropertyValue) data.get(0).getValue()).getRealValue(); // todo implement some diagnostics when this would fail - } else { - throw new ScriptExecutionException("Multiple values where just one is expected"); - } - } else { + public T getSingleValue(Class clazz) throws SchemaException { + if (data.isEmpty()) { return null; + } else if (data.size() == 1) { + return ScriptingDataUtil.getRealValue(data.get(0).getValue(), clazz); + } else { + throw new SchemaException("Multiple values where just one is expected"); } } - static PipelineData createItem(@NotNull PrismValue value, VariablesMap variables) throws SchemaException { - PipelineData data = createEmpty(); - data.addValue(value, variables); - return data; - } - - public Collection getDataAsReferences(QName defaultTargetType, Class typeForQuery, + /** + * Returns the pipeline content as a list of references. Objects, PRVs, OIDs are converted directly + * to references. Search filters and queries are evaluated first. + * + * This is a legacy method and its use should be avoided. + */ + @NotNull + public List getDataAsReferences(QName defaultTargetType, Class typeForQuery, ExecutionContext context, OperationResult result) throws ScriptExecutionException, CommunicationException, ObjectNotFoundException, SchemaException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException { - Collection retval = new ArrayList<>(data.size()); + List retval = new ArrayList<>(data.size()); for (PipelineItem item : data) { PrismValue value = item.getValue(); if (value instanceof PrismObjectValue) { - PrismObjectValue objectValue = (PrismObjectValue) value; + PrismObjectValue objectValue = (PrismObjectValue) value; ObjectReferenceType ref = new ObjectReferenceType(); - ref.setType(objectValue.asPrismObject().getDefinition().getTypeName()); // todo check the definition is present - ref.setOid(objectValue.getOid()); // todo check if oid is present + ref.setType(objectValue.asPrismObject().getDefinition().getTypeName()); + ref.setOid(objectValue.getOid()); retval.add(ref); } else if (value instanceof PrismPropertyValue) { - Object realValue = ((PrismPropertyValue) value).getRealValue(); + Object realValue = value.getRealValue(); if (realValue instanceof SearchFilterType) { retval.addAll( resolveQuery( @@ -138,7 +132,7 @@ public Collection getDataAsReferences(QName defaultTargetTy } else if (realValue instanceof String) { ObjectReferenceType ref = new ObjectReferenceType(); ref.setType(defaultTargetType); - ref.setOid((String) realValue); // todo implement search by name + ref.setOid((String) realValue); retval.add(ref); } else if (realValue instanceof ObjectReferenceType) { retval.add((ObjectReferenceType) realValue); @@ -165,7 +159,7 @@ private Collection resolveQuery(Class return objects.stream().map(o -> ObjectTypeUtil.createObjectRef(o, context.getPrismContext())).collect(Collectors.toList()); } - static PipelineData parseFrom(ValueListType input, VariablesMap frozenInitialVariables, PrismContext prismContext) { + static @NotNull PipelineData parseFrom(ValueListType input, VariablesMap frozenInitialVariables, PrismContext prismContext) { PipelineData rv = new PipelineData(); if (input != null) { for (Object o : input.getValue()) { @@ -193,9 +187,14 @@ static PipelineData parseFrom(ValueListType input, VariablesMap frozenInitialVar return rv; } - public PipelineData cloneMutableState() { + PipelineData cloneMutableState() { PipelineData rv = new PipelineData(); data.forEach(d -> rv.add(d.cloneMutableState())); return rv; } + + @Override + public String debugDump(int indent) { + return DebugUtil.debugDump(data, indent); + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingActionExecutorRegistry.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingActionExecutorRegistry.java new file mode 100644 index 00000000000..aeaea8b3504 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingActionExecutorRegistry.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.scripting; + +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; + +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + */ +@Component +public class ScriptingActionExecutorRegistry { + + private final Map executorsByTypeName = new ConcurrentHashMap<>(); + private final Map, ActionExecutor> executorsByBeanClass = new ConcurrentHashMap<>(); + + public void register(String name, Class type, ActionExecutor executor) { + executorsByTypeName.put(name, executor); + executorsByBeanClass.put(type, executor); + } + + public void register(String name, ActionExecutor executor) { + executorsByTypeName.put(name, executor); + } + + @NotNull ActionExecutor getExecutor(ActionExpressionType action) { + if (action.getType() != null) { + ActionExecutor executor = executorsByTypeName.get(action.getType()); + if (executor != null) { + return executor; + } else { + throw new IllegalStateException("Unknown action executor for action type '" + action.getType() + "'"); + } + } else { + ActionExecutor executor = executorsByBeanClass.get(action.getClass()); + if (executor != null) { + return executor; + } else { + throw new IllegalStateException("Unknown action executor for bean class '" + action.getClass().getName() + "'"); + } + } + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingExpressionEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingExpressionEvaluator.java index 6c541809139..e38d366093a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingExpressionEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/ScriptingExpressionEvaluator.java @@ -14,9 +14,8 @@ import com.evolveum.midpoint.model.impl.scripting.expressions.FilterContentEvaluator; import com.evolveum.midpoint.model.impl.scripting.expressions.SearchEvaluator; import com.evolveum.midpoint.model.impl.scripting.expressions.SelectEvaluator; -import com.evolveum.midpoint.model.impl.scripting.helpers.ScriptingJaxbUtil; +import com.evolveum.midpoint.model.impl.scripting.helpers.ScriptingBeansUtil; import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.expression.ExpressionProfile; @@ -42,15 +41,12 @@ import org.springframework.stereotype.Component; import javax.xml.bind.JAXBElement; -import javax.xml.namespace.QName; -import java.util.HashMap; import java.util.List; -import java.util.Map; + +import static com.evolveum.midpoint.model.impl.scripting.helpers.ScriptingBeansUtil.getActionType; /** * Main entry point for evaluating scripting expressions. - * - * @author mederly */ @Component public class ScriptingExpressionEvaluator { @@ -66,15 +62,12 @@ public class ScriptingExpressionEvaluator { @Autowired private PrismContext prismContext; @Autowired private ModelObjectResolver modelObjectResolver; @Autowired private ExpressionFactory expressionFactory; - - private ObjectFactory objectFactory = new ObjectFactory(); - - private Map actionExecutors = new HashMap<>(); + @Autowired public ScriptingActionExecutorRegistry actionExecutorRegistry; // TODO implement more nicely public static ExecuteScriptType createExecuteScriptCommand(ScriptingExpressionType expression) { ExecuteScriptType executeScriptCommand = new ExecuteScriptType(); - executeScriptCommand.setScriptingExpression(ScriptingJaxbUtil.toJaxbElement(expression)); + executeScriptCommand.setScriptingExpression(ScriptingBeansUtil.toJaxbElement(expression)); return executeScriptCommand; } @@ -170,12 +163,18 @@ private ExecutionContext evaluateExpression(@NotNull ExecuteScriptType executeSc } // not to be called from outside - public PipelineData evaluateExpression(JAXBElement expression, PipelineData input, ExecutionContext context, OperationResult parentResult) throws ScriptExecutionException { + public PipelineData evaluateExpression(JAXBElement expression, PipelineData input, + ExecutionContext context, OperationResult parentResult) throws ScriptExecutionException, SchemaException, + ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, + ExpressionEvaluationException { return evaluateExpression(expression.getValue(), input, context, parentResult); } // not to be called from outside - public PipelineData evaluateExpression(ScriptingExpressionType value, PipelineData input, ExecutionContext context, OperationResult parentResult) throws ScriptExecutionException { + public PipelineData evaluateExpression(ScriptingExpressionType value, PipelineData input, ExecutionContext context, + OperationResult parentResult) + throws ScriptExecutionException, SchemaException, ConfigurationException, ObjectNotFoundException, + CommunicationException, SecurityViolationException, ExpressionEvaluationException { context.checkTaskStop(); OperationResult globalResult = parentResult.createMinorSubresult(DOT_CLASS + "evaluateExpression"); PipelineData output; @@ -195,37 +194,35 @@ public PipelineData evaluateExpression(ScriptingExpressionType value, PipelineDa throw new IllegalArgumentException("Unsupported expression type: " + (value==null?"(null)":value.getClass())); } globalResult.computeStatusIfUnknown(); + globalResult.setSummarizeSuccesses(true); + globalResult.summarize(); return output; } - private PipelineData executeAction(ActionExpressionType command, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { - Validate.notNull(command, "command"); - Validate.notNull(command.getType(), "command.actionType"); - + private PipelineData executeAction(@NotNull ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Executing action {} on {}", command.getType(), input.debugDump()); + LOGGER.trace("Executing action {} on {}", getActionType(action), input.debugDump()); } else if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Executing action {}", command.getType()); - } - ActionExecutor executor = actionExecutors.get(command.getType()); - if (executor == null) { - throw new IllegalStateException("Unsupported action type: " + command.getType()); - } else { - PipelineData retval = executor.execute(command, input, context, globalResult); - globalResult.setSummarizeSuccesses(true); - globalResult.summarize(); - return retval; + LOGGER.debug("Executing action {}", getActionType(action)); } + return actionExecutorRegistry.getExecutor(action) + .execute(action, input, context, globalResult); } - private PipelineData executePipeline(ExpressionPipelineType pipeline, PipelineData data, ExecutionContext context, OperationResult result) throws ScriptExecutionException { + private PipelineData executePipeline(ExpressionPipelineType pipeline, PipelineData data, ExecutionContext context, + OperationResult result) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { for (JAXBElement expressionType : pipeline.getScriptingExpression()) { data = evaluateExpression(expressionType, data, context, result); } return data; } - private PipelineData executeSequence(ExpressionSequenceType sequence, PipelineData input, ExecutionContext context, OperationResult result) throws ScriptExecutionException { + private PipelineData executeSequence(ExpressionSequenceType sequence, PipelineData input, ExecutionContext context, + OperationResult result) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { PipelineData lastOutput = null; List> scriptingExpression = sequence.getScriptingExpression(); for (int i = 0; i < scriptingExpression.size(); i++) { @@ -236,7 +233,7 @@ private PipelineData executeSequence(ExpressionSequenceType sequence, PipelineDa return lastOutput; } - public PipelineData evaluateConstantExpression(@NotNull RawType constant, @Nullable Class expectedClass, ExecutionContext context, String desc, OperationResult result) throws ScriptExecutionException { + public PipelineData evaluateConstantExpression(@NotNull RawType constant, @Nullable Class expectedClass, ExecutionContext context, String desc) throws ScriptExecutionException { try { // TODO fix this brutal hacking @@ -256,25 +253,21 @@ public PipelineData evaluateConstantExpression(@NotNull RawType constant, @Nulla if (value.isRaw()) { throw new IllegalStateException("Raw value while " + desc + ": " + value + ". Please specify type of the value."); } - return PipelineData.createItem(value, context.getInitialVariables()); + return PipelineData.create(value, context.getInitialVariables()); } catch (SchemaException e) { throw new ScriptExecutionException(e.getMessage(), e); } } - public PipelineData evaluateConstantStringExpression(RawType constant, ExecutionContext context, OperationResult result) throws ScriptExecutionException { + public PipelineData evaluateConstantStringExpression(RawType constant, ExecutionContext context) throws ScriptExecutionException { try { String value = constant.getParsedRealValue(String.class); - return PipelineData.createItem(prismContext.itemFactory().createPropertyValue(value), context.getInitialVariables()); + return PipelineData.create(prismContext.itemFactory().createPropertyValue(value), context.getInitialVariables()); } catch (SchemaException e) { throw new ScriptExecutionException(e.getMessage(), e); } } - public void registerActionExecutor(String actionName, ActionExecutor executor) { - actionExecutors.put(actionName, executor); - } - ModelService getModelService() { return modelService; } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AbstractObjectBasedActionExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AbstractObjectBasedActionExecutor.java new file mode 100644 index 00000000000..a0c21e724a2 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AbstractObjectBasedActionExecutor.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.scripting.actions; + +import com.evolveum.midpoint.model.api.PipelineItem; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; +import com.evolveum.midpoint.prism.Objectable; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectValue; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.CommonException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +import org.jetbrains.annotations.NotNull; + +/** + * Action executor that can iterates over set of PrismObjects of a specified (expected) type. + */ +abstract class AbstractObjectBasedActionExecutor extends BaseActionExecutor { + + @FunctionalInterface + public interface ObjectProcessor { + void process(PrismObject object, PipelineItem item, OperationResult result) throws ScriptExecutionException, CommonException; + } + + @FunctionalInterface + public interface ConsoleFailureMessageWriter { + void write(PrismObject object, @NotNull Throwable exception); + } + + abstract Class getObjectType(); + + void iterateOverObjects(PipelineData input, ExecutionContext context, OperationResult globalResult, + ObjectProcessor consumer, ConsoleFailureMessageWriter writer) + throws ScriptExecutionException { + for (PipelineItem item: input.getData()) { + PrismValue value = item.getValue(); + OperationResult result = operationsHelper.createActionResult(item, this); + try { + context.checkTaskStop(); + PrismObject object = castToObject(value, getObjectType(), context); + if (object != null) { + T objectable = object.asObjectable(); + long started = operationsHelper.recordStart(context, objectable); + try { + consumer.process(object, item, result); + operationsHelper.recordEnd(context, objectable, started, null); + } catch (Throwable e) { + operationsHelper.recordEnd(context, objectable, started, e); + Throwable exception = processActionException(e, getActionName(), value, context); + writer.write(object, exception); + } + } + operationsHelper.trimAndCloneResult(result, globalResult); + } catch (Throwable t) { + result.recordFatalError(t); + throw t; + } finally { + result.computeStatusIfUnknown(); // just in case (should be already computed) + } + } + } + + @SuppressWarnings("ThrowableNotThrown") + private PrismObject castToObject(PrismValue value, Class expectedType, ExecutionContext context) + throws ScriptExecutionException { + if (value instanceof PrismObjectValue) { + PrismObjectValue objectValue = (PrismObjectValue) value; + Class realType = objectValue.asObjectable().getClass(); + if (expectedType.isAssignableFrom(realType)) { + //noinspection unchecked + return objectValue.asPrismObject(); + } else { + processActionException(new ScriptExecutionException("Item is not a PrismObject of " + expectedType.getName() + + "; it is " + realType.getName() + " instead"), getActionName(), value, context); + return null; + } + } else { + processActionException(new ScriptExecutionException("Item is not a PrismObject"), getActionName(), value, context); + return null; + } + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AddExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AddExecutor.java index 53a949cfca0..f686bb67fe1 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AddExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AddExecutor.java @@ -11,18 +11,16 @@ import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.model.impl.scripting.helpers.OperationsHelper; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.DeltaFactory; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.ObjectDeltaOperation; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; -import org.springframework.beans.factory.annotation.Autowired; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.AddActionExpressionType; + import org.springframework.stereotype.Component; import java.util.Collection; @@ -30,56 +28,54 @@ import javax.annotation.PostConstruct; /** - * @author mederly + * */ @Component -public class AddExecutor extends BaseActionExecutor { +public class AddExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "add"; - @Autowired private OperationsHelper operationsHelper; - @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, AddActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - ModelExecuteOptions executionOptions = getOptions(expression, input, context, globalResult); - boolean dryRun = getParamDryRun(expression, input, context, globalResult); + ModelExecuteOptions options = operationsHelper.getOptions(action, input, context, globalResult); + boolean dryRun = operationsHelper.getDryRun(action, input, context, globalResult); + + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + add(object, dryRun, options, context, result), + (object, exception) -> + context.println("Failed to add " + object + drySuffix(dryRun) + exceptionSuffix(exception)) + ); - for (PipelineItem item : input.getData()) { - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - PrismValue value = item.getValue(); - if (value instanceof PrismObjectValue) { - @SuppressWarnings({ "unchecked", "raw" }) - PrismObject prismObject = ((PrismObjectValue) value).asPrismObject(); - ObjectType objectType = prismObject.asObjectable(); - long started = operationsHelper.recordStart(context, objectType); - Throwable exception = null; - try { - Collection> executedDeltas = operationsHelper.applyDelta(createAddDelta(objectType), executionOptions, dryRun, context, result); - String newObjectOid = ObjectDeltaOperation.findAddDeltaOid(executedDeltas, prismObject); - prismObject.setOid(newObjectOid); - operationsHelper.recordEnd(context, objectType, started, null); - } catch (Throwable ex) { - operationsHelper.recordEnd(context, objectType, started, ex); - exception = processActionException(ex, NAME, value, context); - } - context.println((exception != null ? "Attempted to add " : "Added ") + prismObject.toString() + optionsSuffix(executionOptions, dryRun) + exceptionSuffix(exception)); - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } return input; } - private ObjectDelta createAddDelta(ObjectType objectType) { - return DeltaFactory.Object.createAddDelta(objectType.asPrismObject()); + private void add(PrismObject object, boolean dryRun, ModelExecuteOptions options, ExecutionContext context, + OperationResult result) throws ScriptExecutionException { + ObjectDelta addDelta = DeltaFactory.Object.createAddDelta(object); + Collection> executedDeltas = + operationsHelper.applyDelta(addDelta, options, dryRun, context, result); + if (executedDeltas != null) { + String newObjectOid = ObjectDeltaOperation.findAddDeltaOid(executedDeltas, object); + object.setOid(newObjectOid); + } + context.println("Added " + object + drySuffix(dryRun)); + } + + @Override + Class getObjectType() { + return ObjectType.class; + } + + @Override + String getActionName() { + return NAME; } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ApplyDefinitionExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ApplyDefinitionExecutor.java index b83af72f78e..45b5c2a7d2e 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ApplyDefinitionExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ApplyDefinitionExecutor.java @@ -7,64 +7,65 @@ package com.evolveum.midpoint.model.impl.scripting.actions; -import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ApplyDefinitionActionExpressionType; + import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** - * @author mederly + * Applies definitions to relevant objects. Currently supports ShadowType and ResourceType + * that are given definitions by provisioning module. */ @Component -public class ApplyDefinitionExecutor extends BaseActionExecutor { - - //private static final Trace LOGGER = TraceManager.getTrace(ReencryptExecutor.class); +public class ApplyDefinitionExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "apply-definition"; @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, ApplyDefinitionActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType expression, PipelineData input, + ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + applyDefinition(object.asObjectable(), context, result), + (object, exception) -> + context.println("Failed to apply definition to " + object + exceptionSuffix(exception)) + ); - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue) { - @SuppressWarnings({"unchecked", "raw"}) - PrismObject prismObject = ((PrismObjectValue) value).asPrismObject(); - ObjectType objectBean = prismObject.asObjectable(); - if (objectBean instanceof ShadowType || objectBean instanceof ResourceType) { - try { - provisioningService.applyDefinition(prismObject, context.getTask(), result); - result.computeStatus(); - } catch (Throwable ex) { - result.recordFatalError("Couldn't reencrypt object", ex); - Throwable exception = processActionException(ex, NAME, value, context); - context.println("Couldn't apply definition to " + prismObject.toString() + exceptionSuffix(exception)); - } - } - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } return input; } + + private void applyDefinition(ObjectType object, ExecutionContext context, OperationResult result) + throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException, + ExpressionEvaluationException { + if (object instanceof ShadowType || object instanceof ResourceType) { + provisioningService.applyDefinition(object.asPrismObject(), context.getTask(), result); + context.println("Applied definition to " + object); + } + } + + @Override + protected Class getObjectType() { + return ObjectType.class; + } + + @Override + protected String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java index 8a7bd222731..c88d5c1af1f 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2014 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -10,31 +10,70 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; import javax.xml.namespace.QName; -import org.springframework.beans.factory.annotation.Autowired; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; + +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.AssignActionExpressionType; + import org.springframework.stereotype.Component; import com.evolveum.midpoint.prism.PrismConstants; import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.schema.RelationRegistry; import com.evolveum.midpoint.util.MiscUtil; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import static com.evolveum.midpoint.model.impl.scripting.actions.AssignExecutor.*; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + @Component -public class AssignExecutor extends AssignmentOperationsExecutor { +public class AssignExecutor extends AssignmentOperationsExecutor { - @Autowired protected RelationRegistry relationRegistry; + private static final Trace LOGGER = TraceManager.getTrace(AssignExecutor.class); - @Override - protected String getName() { - return AssignmentOperationsExecutor.ASSIGN_NAME; + private static final String NAME = "assign"; + + /** + * These are "purified" parameters: targets and constructions to assign. + * They are created by merging dynamically and statically defined parameters, resolving + * filters in references, and so on. + */ + static class AssignParameters extends Parameters { + private final List targetRefs = new ArrayList<>(); + private final List constructions = new ArrayList<>(); + } + + @PostConstruct + public void init() { + actionExecutorRegistry.register(NAME, AssignActionExpressionType.class, this); } @Override - protected ObjectDelta createDelta(AssignmentHolderType object, Collection resources, - Collection roles, Collection relationSpecifications) throws SchemaException { + AssignParameters parseParameters(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult result) throws SchemaException, ScriptExecutionException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + + AssignParameters parameters = new AssignParameters(); + + // Dynamic parameters + + Collection dynamicRoleRefs = getRolesParameter(action, input, context, result); + Collection dynamicResourceRefs = getResourcesParameter(action, input, context, result); + Collection relationSpecifications = getRelationsParameter(action, input, context, result); QName relationSpecification = MiscUtil.extractSingleton(relationSpecifications, () -> new IllegalArgumentException("Using 'relation' as a multivalued parameter is not allowed")); @@ -43,36 +82,134 @@ protected ObjectDelta createDelta(AssignmentHolderType obj throw new IllegalArgumentException("Using 'q:any' as relation specification is not allowed"); } - List assignmentsToAdd = new ArrayList<>(); - - if (roles != null) { + QName relationOverride; + if (relationSpecification != null) { List relationDefinitions = relationRegistry.getRelationDefinitions(); - QName matchingRelation = relationDefinitions.stream() + relationOverride = relationDefinitions.stream() .filter(definition -> prismContext.relationMatches(relationSpecification, definition.getRef())) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Relation matching '" + relationSpecification + "' not found")) .getRef(); - for (ObjectReferenceType roleRef : roles) { - assignmentsToAdd.add( - new AssignmentType(prismContext) - .targetRef(roleRef.clone() - .relation(matchingRelation))); - } + } else { + relationOverride = null; + } + + // Static parameters + + Collection staticTargetRefs; + Collection staticResourceRefs; + Collection staticConstructions; + if (action instanceof AssignActionExpressionType) { + staticTargetRefs = ((AssignActionExpressionType) action).getTargetRef(); + staticResourceRefs = ((AssignActionExpressionType) action).getResourceRef(); + staticConstructions = ((AssignActionExpressionType) action).getConstruction(); + } else { + staticTargetRefs = emptyList(); + staticResourceRefs = emptyList(); + staticConstructions = emptyList(); } - if (resources != null) { - for (ObjectReferenceType resourceRef : resources) { - assignmentsToAdd.add( - new AssignmentType(prismContext) - .beginConstruction() - .resourceRef(resourceRef) // relation is ignored here - .end()); + // Consolidation + + Task task = context.getTask(); + + parameters.targetRefs.addAll(resolve(staticTargetRefs, relationOverride, task, result)); + parameters.targetRefs.addAll(resolve(dynamicRoleRefs, relationOverride, task, result)); + + QName defaultRelation = relationRegistry.getDefaultRelation(); + parameters.constructions.addAll(staticConstructions); + parameters.constructions.addAll(resourceRefsToConstructions(resolve(staticResourceRefs, defaultRelation, task, result))); + parameters.constructions.addAll(resourceRefsToConstructions(resolve(dynamicResourceRefs, defaultRelation, task, result))); + + return parameters; + } + + @Override + boolean checkParameters(AssignParameters parameters, ExecutionContext context) { + if (parameters.targetRefs.isEmpty() && parameters.constructions.isEmpty()) { + LOGGER.warn("There are no targets nor constructions to assign"); + context.println("Warning: There are no targets nor constructions to assign"); + return false; + } else { + return true; + } + } + + private Collection resolve(Collection targetRefs, QName relationOverride, Task task, + OperationResult result) throws SchemaException, ObjectNotFoundException, SecurityViolationException, + CommunicationException, ConfigurationException, ExpressionEvaluationException { + Collection rv = new ArrayList<>(); + for (ObjectReferenceType ref : targetRefs) { + rv.addAll(resolve(ref, relationOverride, task, result)); + } + return rv; + } + + private Collection resolve(ObjectReferenceType ref, QName relationOverride, Task task, OperationResult result) + throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, + SecurityViolationException, ExpressionEvaluationException { + if (ref.getFilter() != null) { + Class clazz = prismContext.getSchemaRegistry().determineCompileTimeClass(ref.getType()); + if (clazz == null) { + throw new SchemaException("No compile time class for " + ref.getType() + " in " + ref); } + ObjectQuery query = prismContext.getQueryConverter().createObjectQuery(clazz, ref.getFilter()); + QName effectiveRelation = getEffectiveRelation(ref, relationOverride); + return modelService.searchObjects(clazz, query, null, task, result).stream() + .map(object -> ObjectTypeUtil.createObjectRef(object, effectiveRelation)) + .collect(Collectors.toList()); + } else if (relationOverride != null) { + return singletonList(ref.clone().relation(relationOverride)); + } else { + return singletonList(ref); } + } + + private QName getEffectiveRelation(ObjectReferenceType reference, QName relationOverride) { + QName effectiveRelation; + if (relationOverride != null) { + effectiveRelation = relationOverride; + } else if (reference.getRelation() != null) { + effectiveRelation = reference.getRelation(); + } else { + effectiveRelation = relationRegistry.getDefaultRelation(); + } + return effectiveRelation; + } + + private Collection resourceRefsToConstructions(Collection resourceRefs) { + return resourceRefs.stream() + .map(ref -> new ConstructionType(prismContext).resourceRef(ref.clone())) + .collect(Collectors.toList()); + } + + private Collection targetsToAssignments(Collection targetRefs) { + return targetRefs.stream() + .map(ref -> new AssignmentType(prismContext).targetRef(ref.clone())) + .collect(Collectors.toList()); + } + + private Collection constructionsToAssignments(Collection constructions) { + return constructions.stream() + .map(c -> new AssignmentType(prismContext).construction(c.clone())) + .collect(Collectors.toList()); + } + + @Override + protected ObjectDelta createDelta(AssignmentHolderType object, AssignParameters parameters) throws SchemaException { + + List assignmentsToAdd = new ArrayList<>(); + assignmentsToAdd.addAll(targetsToAssignments(parameters.targetRefs)); + assignmentsToAdd.addAll(constructionsToAssignments(parameters.constructions)); return prismContext.deltaFor(object.getClass()) .item(AssignmentHolderType.F_ASSIGNMENT) - .addRealValues(assignmentsToAdd) + .addRealValues(assignmentsToAdd) .asObjectDelta(object.getOid()); } + + @Override + protected String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignmentOperationsExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignmentOperationsExecutor.java index 46870243902..472fe46f004 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignmentOperationsExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignmentOperationsExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -7,146 +7,119 @@ package com.evolveum.midpoint.model.impl.scripting.actions; +import java.util.Collection; +import java.util.Collections; +import java.util.stream.Collectors; +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.util.exception.*; + import com.evolveum.midpoint.model.api.ModelExecuteOptions; -import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ObjectDelta; -import com.evolveum.midpoint.schema.constants.RelationTypes; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.CommonException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionParameterValueType; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.jetbrains.annotations.NotNull; - -import javax.annotation.PostConstruct; -import javax.xml.namespace.QName; -import java.util.Collection; -import java.util.Collections; -import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; /** - * + * Abstract executor for assign and unassign actions. */ -public abstract class AssignmentOperationsExecutor extends BaseActionExecutor { +abstract class AssignmentOperationsExecutor

+ extends AbstractObjectBasedActionExecutor { - private static final Trace LOGGER = TraceManager.getTrace(AssignmentOperationsExecutor.class); - - static final String UNASSIGN_NAME = "unassign"; - static final String ASSIGN_NAME = "assign"; private static final String PARAM_RESOURCE = "resource"; private static final String PARAM_ROLE = "role"; private static final String PARAM_RELATION = "relation"; - @PostConstruct - public void init() { - scriptingExpressionEvaluator.registerActionExecutor(getName(), this); + static class Parameters { + ModelExecuteOptions options; + boolean dryRun; } - protected abstract String getName(); - @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { + + P parameters = parseParameters(action, input, context, globalResult); + parameters.options = operationsHelper.getOptions(action, input, context, globalResult); + parameters.dryRun = operationsHelper.getDryRun(action, input, context, globalResult); + + if (checkParameters(parameters, context)) { + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + apply(object.asObjectable(), parameters, context, result), + (object, exception) -> + context.println("Failed to modify " + object + drySuffix(parameters.dryRun) + exceptionSuffix(exception)) + ); + } - ModelExecuteOptions executionOptions = getOptions(expression, input, context, globalResult); - boolean dryRun = getParamDryRun(expression, input, context, globalResult); + return input; + } - ActionParameterValueType resourceParameterValue = expressionHelper.getArgument(expression.getParameter(), PARAM_RESOURCE, false, false, getName()); - ActionParameterValueType roleParameterValue = expressionHelper.getArgument(expression.getParameter(), PARAM_ROLE, false, false, getName()); - Collection relationSpecificationUris = expressionHelper.getArgumentValues(expression.getParameter(), PARAM_RELATION, false, false, getName(), input, context, String.class, globalResult); + abstract boolean checkParameters(P parameters, ExecutionContext context); - Collection relationSpecifications; - if (relationSpecificationUris.isEmpty()) { - QName defaultRelation = ObjectUtils.defaultIfNull(prismContext.getDefaultRelation(), RelationTypes.MEMBER.getRelation()); - relationSpecifications = Collections.singleton(defaultRelation); - } else { - relationSpecifications = relationSpecificationUris.stream() - .map(uri -> QNameUtil.uriToQName(uri, true)) - .collect(Collectors.toSet()); - } - assert !relationSpecifications.isEmpty(); - - Collection resources; - try { - if (resourceParameterValue != null) { - PipelineData data = expressionHelper - .evaluateParameter(resourceParameterValue, null, input, context, globalResult); - resources = data.getDataAsReferences(ResourceType.COMPLEX_TYPE, ResourceType.class, context, globalResult); - } else { - resources = null; - } - } catch (CommonException e) { - throw new ScriptExecutionException("Couldn't evaluate '" + PARAM_RESOURCE + "' parameter of a scripting expression: " + e.getMessage(), e); - } + abstract P parseParameters(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult result) throws SchemaException, ScriptExecutionException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException; - Collection roles; - try { - if (roleParameterValue != null) { - PipelineData data = expressionHelper.evaluateParameter(roleParameterValue, null, input, context, globalResult); - roles = data.getDataAsReferences(RoleType.COMPLEX_TYPE, AbstractRoleType.class, context, globalResult); // if somebody wants to assign Org, he has to use full reference value (including object type) - } else { - roles = null; - } - } catch (CommonException e) { - throw new ScriptExecutionException("Couldn't evaluate '" + PARAM_ROLE + "' parameter of a scripting expression: " + e.getMessage(), e); - } + private void apply(AssignmentHolderType object, P parameters, ExecutionContext context, OperationResult result) + throws SchemaException, ScriptExecutionException { + ObjectDelta delta = createDelta(object, parameters); + operationsHelper.applyDelta(delta, parameters.options, parameters.dryRun, context, result); + context.println("Modified " + object + optionsSuffix(parameters.options, parameters.dryRun)); + } - if (resources == null && roles == null) { - throw new ScriptExecutionException("Nothing to " + getName() + ": neither resource nor role specified"); - } + abstract ObjectDelta createDelta(AssignmentHolderType object, P parameters) throws SchemaException; - if (CollectionUtils.isEmpty(resources) && CollectionUtils.isEmpty(roles)) { - LOGGER.warn("No resources and no roles to unassign in a scripting expression"); - context.println("Warning: no resources and no roles to unassign"); // TODO some better handling? - return input; - } + @Override + protected Class getObjectType() { + return AssignmentHolderType.class; + } - for (PipelineItem item : input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue && ((PrismObjectValue) value).asObjectable() instanceof AssignmentHolderType) { - @SuppressWarnings({"unchecked", "raw"}) - PrismObject prismObject = ((PrismObjectValue) value).asPrismObject(); - AssignmentHolderType objectType = (AssignmentHolderType) prismObject.asObjectable(); - long started = operationsHelper.recordStart(context, objectType); - Throwable exception = null; - try { - operationsHelper.applyDelta(createDelta(objectType, resources, roles, relationSpecifications), executionOptions, dryRun, context, result); - operationsHelper.recordEnd(context, objectType, started, null); - } catch (Throwable ex) { - operationsHelper.recordEnd(context, objectType, started, ex); - exception = processActionException(ex, getName(), value, context); - } - context.println(createConsoleMessage(prismObject, executionOptions, dryRun, exception)); - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject of AssignmentHolderType"), getName(), value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); + @NotNull Collection getRolesParameter(ActionExpressionType action, PipelineData input, + ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException, SchemaException, + ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, + ExpressionEvaluationException { + ActionParameterValueType roleParameterValue = expressionHelper.getArgument(action.getParameter(), PARAM_ROLE, + false, false, getActionName()); + if (roleParameterValue != null) { + PipelineData data = expressionHelper.evaluateParameter(roleParameterValue, null, input, context, globalResult); + // if somebody wants to assign Org, he has to use full reference value (including object type) + return data.getDataAsReferences(RoleType.COMPLEX_TYPE, AbstractRoleType.class, context, globalResult); + } else { + return Collections.emptyList(); } - return input; // TODO updated objects? } - @NotNull - private String createConsoleMessage(PrismObject object, ModelExecuteOptions executionOptions, - boolean dryRun, Throwable exception) { - return (exception != null ? "Attempted to modify " : "Modified ") + object - + optionsSuffix(executionOptions, dryRun) + exceptionSuffix(exception); + @NotNull Collection getResourcesParameter(ActionExpressionType action, PipelineData input, + ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException, SchemaException, + ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, + ExpressionEvaluationException { + ActionParameterValueType resourceParameterValue = expressionHelper.getArgument(action.getParameter(), PARAM_RESOURCE, + false, false, getActionName()); + if (resourceParameterValue != null) { + PipelineData data = expressionHelper + .evaluateParameter(resourceParameterValue, null, input, context, globalResult); + return data.getDataAsReferences(ResourceType.COMPLEX_TYPE, ResourceType.class, context, globalResult); + } else { + return Collections.emptyList(); + } } - protected abstract ObjectDelta createDelta(AssignmentHolderType object, Collection resources, - Collection roles, Collection relationSpecifications) throws SchemaException; + @NotNull Collection getRelationsParameter(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { + Collection relationSpecificationUris = expressionHelper.getArgumentValues(action.getParameter(), PARAM_RELATION, + false, false, getActionName(), input, context, String.class, globalResult); + return relationSpecificationUris.stream() + .map(uri -> QNameUtil.uriToQName(uri, true)) + .collect(Collectors.toSet()); + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/BaseActionExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/BaseActionExecutor.java index e863a09cf57..6d14aac3b83 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/BaseActionExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/BaseActionExecutor.java @@ -7,55 +7,42 @@ package com.evolveum.midpoint.model.impl.scripting.actions; -import com.evolveum.midpoint.model.api.ModelExecuteOptions; -import com.evolveum.midpoint.model.api.ModelService; -import com.evolveum.midpoint.model.api.TaskService; -import com.evolveum.midpoint.model.impl.scripting.ActionExecutor; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; +import com.evolveum.midpoint.model.api.*; +import com.evolveum.midpoint.model.api.expr.MidpointFunctions; +import com.evolveum.midpoint.model.impl.scripting.*; import com.evolveum.midpoint.model.impl.scripting.helpers.ExpressionHelper; import com.evolveum.midpoint.model.impl.scripting.helpers.OperationsHelper; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.match.MatchingRuleRegistry; import com.evolveum.midpoint.provisioning.api.ProvisioningService; import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.schema.GetOperationOptionsBuilder; +import com.evolveum.midpoint.schema.RelationRegistry; +import com.evolveum.midpoint.schema.SchemaHelper; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.security.api.AuthorizationConstants; import com.evolveum.midpoint.security.api.SecurityContextManager; import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters; import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PartialProcessingOptionsType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import static com.evolveum.midpoint.xml.ns._public.common.common_3.PartialProcessingTypeType.SKIP; - /** - * @author mederly + * Superclass for all action executors. */ public abstract class BaseActionExecutor implements ActionExecutor { private static final Trace LOGGER = TraceManager.getTrace(BaseActionExecutor.class); - private static final String PARAM_RAW = "raw"; - private static final String PARAM_DRY_RUN = "dryRun"; - private static final String PARAM_SKIP_APPROVALS = "skipApprovals"; - private static final String PARAM_OPTIONS = "options"; - @Autowired protected ScriptingExpressionEvaluator scriptingExpressionEvaluator; @Autowired protected PrismContext prismContext; @Autowired protected OperationsHelper operationsHelper; @@ -66,46 +53,21 @@ public abstract class BaseActionExecutor implements ActionExecutor { @Autowired protected SecurityContextManager securityContextManager; @Autowired protected TaskService taskService; @Autowired @Qualifier("cacheRepositoryService") protected RepositoryService cacheRepositoryService; - - // todo move to some helper? - - protected ModelExecuteOptions getOptions(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult result) throws ScriptExecutionException { - boolean raw = expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_RAW, input, context, false, PARAM_RAW, result); - boolean skipApprovals = expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_SKIP_APPROVALS, input, context, false, PARAM_RAW, result); - ModelExecuteOptionsType optionsBean = expressionHelper.getSingleArgumentValue(expression.getParameter(), PARAM_OPTIONS, false, false, "options", input, context, - ModelExecuteOptionsType.class, result); - ModelExecuteOptions options; - if (optionsBean != null) { - options = ModelExecuteOptions.fromModelExecutionOptionsType(optionsBean); - } else { - options = new ModelExecuteOptions(); - } - if (raw) { - options.setRaw(true); - } - if (skipApprovals) { - if (options.getPartialProcessing() != null) { - options.getPartialProcessing().setApprovals(SKIP); - } else { - options.setPartialProcessing(new PartialProcessingOptionsType().approvals(SKIP)); - } - } - return options; - } - - protected boolean getParamDryRun(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult result) throws ScriptExecutionException { - return expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_DRY_RUN, input, context, false, PARAM_DRY_RUN, result); - } + @Autowired protected ScriptingActionExecutorRegistry actionExecutorRegistry; + @Autowired protected MidpointFunctions midpointFunctions; + @Autowired protected RelationRegistry relationRegistry; + @Autowired protected MatchingRuleRegistry matchingRuleRegistry; + @Autowired protected SchemaHelper schemaHelper; private String optionsSuffix(ModelExecuteOptions options) { return options.notEmpty() ? " " + options : ""; } - protected String drySuffix(boolean dry) { + String drySuffix(boolean dry) { return dry ? " (dry run)" : ""; } - protected String optionsSuffix(ModelExecuteOptions options, boolean dry) { + String optionsSuffix(ModelExecuteOptions options, boolean dry) { return optionsSuffix(options) + drySuffix(dry); } @@ -113,7 +75,7 @@ protected String exceptionSuffix(Throwable t) { return t != null ? " (error: " + t.getClass().getSimpleName() + ": " + t.getMessage() + ")" : ""; } - protected Throwable processActionException(Throwable e, String actionName, PrismValue value, ExecutionContext context) throws ScriptExecutionException { + Throwable processActionException(Throwable e, String actionName, PrismValue value, ExecutionContext context) throws ScriptExecutionException { if (context.isContinueOnAnyError()) { LoggingUtils.logUnexpectedException(LOGGER, "Couldn't execute action '{}' on {}: {}", e, actionName, value, e.getMessage()); @@ -123,8 +85,8 @@ protected Throwable processActionException(Throwable e, String actionName, Prism } } - protected void checkRootAuthorization(ExecutionContext context, - OperationResult globalResult, String actionName) throws ScriptExecutionException { + void checkRootAuthorization(ExecutionContext context, OperationResult globalResult, String actionName) + throws ScriptExecutionException { if (context.isPrivileged()) { return; } @@ -135,4 +97,58 @@ protected void checkRootAuthorization(ExecutionContext context, } } + @FunctionalInterface + public interface ItemProcessor { + void process(PrismValue value, PipelineItem item, OperationResult result) throws ScriptExecutionException, CommonException; + } + + @FunctionalInterface + public interface ConsoleFailureMessageWriter { + void write(PrismValue value, @NotNull Throwable exception); + } + + void iterateOverItems(PipelineData input, ExecutionContext context, OperationResult globalResult, + ItemProcessor itemProcessor, ConsoleFailureMessageWriter writer) + throws ScriptExecutionException { + + for (PipelineItem item : input.getData()) { + PrismValue value = item.getValue(); + OperationResult result = operationsHelper.createActionResult(item, this); + + context.checkTaskStop(); + long started; + if (value instanceof PrismObjectValue) { + started = operationsHelper.recordStart(context, asObjectType(value)); + } else { + started = 0; + } + try { + itemProcessor.process(value, item, result); + if (value instanceof PrismObjectValue) { + operationsHelper.recordEnd(context, asObjectType(value), started, null); + } + } catch (Throwable ex) { + if (value instanceof PrismObjectValue) { + operationsHelper.recordEnd(context, asObjectType(value), started, ex); + } + Throwable exception = processActionException(ex, getActionName(), value, context); + writer.write(value, exception); + } + operationsHelper.trimAndCloneResult(result, globalResult); + } + } + + private ObjectType asObjectType(PrismValue value) { + return (ObjectType) ((PrismObjectValue) value).asObjectable(); + } + + String getDescription(PrismValue value) { + if (value instanceof PrismObjectValue) { + return asObjectType(value).asPrismObject().toString(); + } else { + return value.toHumanReadableString(); + } + } + + abstract String getActionName(); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DeleteExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DeleteExecutor.java index 5ca5d3fc03d..063e025e9ac 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DeleteExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DeleteExecutor.java @@ -11,77 +11,68 @@ import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.model.impl.scripting.helpers.OperationsHelper; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.DeleteActionExpressionType; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** - * @author mederly + * Executes the "delete" action. */ @Component -public class DeleteExecutor extends BaseActionExecutor { - - private static final Trace LOGGER = TraceManager.getTrace(DeleteExecutor.class); +public class DeleteExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "delete"; - @Autowired - private OperationsHelper operationsHelper; - @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, DeleteActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - ModelExecuteOptions executionOptions = getOptions(expression, input, context, globalResult); - boolean dryRun = getParamDryRun(expression, input, context, globalResult); + boolean dryRun = operationsHelper.getDryRun(action, input, context, globalResult); + ModelExecuteOptions options = operationsHelper.getOptions(action, input, context, globalResult); + + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + delete(object.asObjectable(), dryRun, options, context, result), + (object, exception) -> + context.println("Failed to delete " + object + drySuffix(dryRun) + exceptionSuffix(exception)) + ); - for (PipelineItem item : input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue) { - @SuppressWarnings({"unchecked", "raw"}) - PrismObject prismObject = ((PrismObjectValue) value).asPrismObject(); - ObjectType objectType = prismObject.asObjectable(); - long started = operationsHelper.recordStart(context, objectType); - Throwable exception = null; - try { - operationsHelper.applyDelta(createDeleteDelta(objectType), executionOptions, dryRun, context, result); - operationsHelper.recordEnd(context, objectType, started, null); - } catch (Throwable ex) { - operationsHelper.recordEnd(context, objectType, started, ex); - exception = processActionException(ex, NAME, value, context); - } - context.println((exception != null ? "Attempted to delete " : "Deleted ") + prismObject.toString() + optionsSuffix(executionOptions, dryRun) + exceptionSuffix(exception)); - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } return input; } - private ObjectDelta createDeleteDelta(ObjectType objectType) { - return prismContext.deltaFactory().object().createDeleteDelta(objectType.getClass(), objectType.getOid() - ); + private void delete(ObjectType object, boolean dryRun, ModelExecuteOptions options, ExecutionContext context, + OperationResult result) throws ScriptExecutionException { + ObjectDelta deleteDelta = prismContext.deltaFactory().object() + .createDeleteDelta(object.getClass(), object.getOid()); + operationsHelper.applyDelta(deleteDelta, options, dryRun, context, result); + context.println("Deleted " + object + optionsSuffix(options, dryRun)); } + @Override + Class getObjectType() { + return ObjectType.class; + } + + @Override + String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DiscoverConnectorsExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DiscoverConnectorsExecutor.java index 3715a81c3a6..63661356999 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DiscoverConnectorsExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/DiscoverConnectorsExecutor.java @@ -15,10 +15,8 @@ import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.delta.ReferenceDelta; import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.exception.CommunicationException; import com.evolveum.midpoint.util.exception.ConfigurationException; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; @@ -29,7 +27,6 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorHostType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; @@ -40,7 +37,10 @@ import java.util.*; /** - * @author mederly + * Executes "discover-connectors" action. + * + * There is no static (typed) definition of this action yet. + * Also, this code is not refactored yet. */ @Component public class DiscoverConnectorsExecutor extends BaseActionExecutor { @@ -52,11 +52,13 @@ public class DiscoverConnectorsExecutor extends BaseActionExecutor { @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { boolean rebind = expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_REBIND_RESOURCES, input, context, false, PARAM_REBIND_RESOURCES, globalResult); @@ -64,7 +66,7 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, for (PipelineItem item: input.getData()) { PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); + OperationResult result = operationsHelper.createActionResult(item, this); context.checkTaskStop(); if (value instanceof PrismObjectValue && ((PrismObjectValue) value).asObjectable() instanceof ConnectorHostType) { PrismObject connectorHostTypePrismObject = ((PrismObjectValue) value).asPrismObject(); @@ -96,7 +98,7 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, //noinspection ThrowableNotThrown processActionException(new ScriptExecutionException("Input item is not a PrismObject"), NAME, value, context); } - operationsHelper.trimAndCloneResult(result, globalResult, context); + operationsHelper.trimAndCloneResult(result, globalResult); } return output; // TODO configurable output (either connector hosts or discovered connectors) } @@ -171,4 +173,9 @@ private void determineConnectorMappings(Map rebindMap, ConnectorT } } } + + @Override + String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/EnableDisableExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/EnableDisableExecutor.java index 91f60b00ff3..d2b9e6e7eb2 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/EnableDisableExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/EnableDisableExecutor.java @@ -7,104 +7,92 @@ package com.evolveum.midpoint.model.impl.scripting.actions; +import static com.evolveum.midpoint.model.impl.scripting.helpers.ScriptingBeansUtil.getActionType; +import static com.evolveum.midpoint.schema.constants.SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS; + +import javax.annotation.PostConstruct; + +import com.evolveum.midpoint.model.impl.scripting.helpers.ScriptingBeansUtil; +import com.evolveum.midpoint.model.impl.scripting.helpers.ScriptingDataUtil; + +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.ModelExecuteOptions; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.model.impl.scripting.helpers.OperationsHelper; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationStatusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -import static com.evolveum.midpoint.schema.constants.SchemaConstants.PATH_ACTIVATION_ADMINISTRATIVE_STATUS; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.DisableActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.EnableActionExpressionType; /** - * @author mederly + * Implements "enable" and "disable" actions. + * It is ObjectType-typed just because it handles both FocusType and ShadowType objects. */ @Component -public class EnableDisableExecutor extends BaseActionExecutor { - - private static final Trace LOGGER = TraceManager.getTrace(EnableDisableExecutor.class); +public class EnableDisableExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME_ENABLE = "enable"; private static final String NAME_DISABLE = "disable"; - @Autowired - private OperationsHelper operationsHelper; - @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME_ENABLE, this); - scriptingExpressionEvaluator.registerActionExecutor(NAME_DISABLE, this); + actionExecutorRegistry.register(NAME_ENABLE, EnableActionExpressionType.class, this); + actionExecutorRegistry.register(NAME_DISABLE, DisableActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { - - boolean isEnable = NAME_ENABLE.equals(expression.getType()); - ModelExecuteOptions executionOptions = getOptions(expression, input, context, globalResult); - boolean dryRun = getParamDryRun(expression, input, context, globalResult); - - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - result.addParam("operation", expression.getType()); - context.checkTaskStop(); - if (value instanceof PrismObjectValue) { - @SuppressWarnings({"unchecked", "raw"}) - PrismObject prismObject = ((PrismObjectValue) value).asPrismObject(); - ObjectType objectType = prismObject.asObjectable(); - long started = operationsHelper.recordStart(context, objectType); - Throwable exception = null; - try { - if (objectType instanceof FocusType) { - operationsHelper.applyDelta(createEnableDisableDelta((FocusType) objectType, isEnable), executionOptions, dryRun, context, result); - } else if (objectType instanceof ShadowType) { - operationsHelper.applyDelta(createEnableDisableDelta((ShadowType) objectType, isEnable), executionOptions, dryRun, context, result); - } else { - throw new ScriptExecutionException("Item is not a FocusType nor ShadowType: " + value.toString()); - } - operationsHelper.recordEnd(context, objectType, started, null); - } catch (Throwable ex) { - operationsHelper.recordEnd(context, objectType, started, ex); - exception = processActionException(ex, expression.getType(), value, context); - } - context.println((exception != null ? "Attempted to " + expression.getType() : (isEnable ? "Enabled " : "Disabled ")) - + prismObject.toString() + optionsSuffix(executionOptions, dryRun) + exceptionSuffix(exception)); - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), expression.getType(), value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + + ModelExecuteOptions options = operationsHelper.getOptions(action, input, context, globalResult); + boolean dryRun = operationsHelper.getDryRun(action, input, context, globalResult); + boolean isEnable = NAME_ENABLE.equals(getActionType(action)); + + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + enableOrDisable(object.asObjectable(), dryRun, options, isEnable, context, result), + (object, exception) -> + context.println("Failed to " + (isEnable?"enable":"disable") + object + drySuffix(dryRun) + exceptionSuffix(exception)) + ); + return input; } - private ObjectDelta createEnableDisableDelta(FocusType focus, boolean isEnable) { - return prismContext.deltaFactory().object().createModificationReplaceProperty(focus.getClass(), - focus.getOid(), PATH_ACTIVATION_ADMINISTRATIVE_STATUS, - isEnable ? ActivationStatusType.ENABLED : ActivationStatusType.DISABLED); + private void enableOrDisable(ObjectType object, boolean dryRun, ModelExecuteOptions options, boolean isEnable, + ExecutionContext context, OperationResult result) throws ScriptExecutionException, SchemaException { + if (object instanceof FocusType || object instanceof ShadowType) { + ObjectDelta delta = createEnableDisableDelta(object, isEnable); + operationsHelper.applyDelta(delta, options, dryRun, context, result); + context.println((isEnable ? "Enabled " : "Disabled ") + object + optionsSuffix(options, dryRun)); + } else { + throw new ScriptExecutionException("Object is not a FocusType nor ShadowType: " + object); + } } - private ObjectDelta createEnableDisableDelta(ShadowType shadow, boolean isEnable) { - return prismContext.deltaFactory().object().createModificationReplaceProperty(shadow.getClass(), - shadow.getOid(), PATH_ACTIVATION_ADMINISTRATIVE_STATUS, - isEnable ? ActivationStatusType.ENABLED : ActivationStatusType.DISABLED); + private ObjectDelta createEnableDisableDelta(ObjectType object, boolean isEnable) + throws SchemaException { + return prismContext.deltaFor(object.getClass()) + .item(PATH_ACTIVATION_ADMINISTRATIVE_STATUS) + .replace(isEnable ? ActivationStatusType.ENABLED : ActivationStatusType.DISABLED) + .asObjectDelta(object.getOid()); } + @Override + Class getObjectType() { + return ObjectType.class; + } + + @Override + String getActionName() { + return "enable-disable"; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ExecuteScriptExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ExecuteScriptExecutor.java new file mode 100644 index 00000000000..138e7ad9885 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ExecuteScriptExecutor.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2010-2019 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.scripting.actions; + +import com.evolveum.midpoint.model.api.PipelineItem; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpression; +import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; +import com.evolveum.midpoint.model.impl.util.ModelImplUtils; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; +import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; +import com.evolveum.midpoint.repo.common.expression.ExpressionSyntaxException; +import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; +import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; +import com.evolveum.midpoint.schema.SchemaConstantsGenerated; +import com.evolveum.midpoint.schema.constants.ExpressionConstants; +import com.evolveum.midpoint.schema.expression.ExpressionProfile; +import com.evolveum.midpoint.schema.expression.TypedValue; +import com.evolveum.midpoint.schema.expression.VariablesMap; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.QNameUtil; +import com.evolveum.midpoint.util.exception.CommunicationException; +import com.evolveum.midpoint.util.exception.ConfigurationException; +import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExpressionEvaluatorType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ExecuteScriptActionExpressionType; + +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.xml.namespace.QName; +import java.util.Collection; +import java.util.List; + +import static com.evolveum.midpoint.model.impl.scripting.VariablesUtil.cloneIfNecessary; + +/** + * Executes "execute-script" (s:execute) actions. + */ +@Component +public class ExecuteScriptExecutor extends BaseActionExecutor { + + @Autowired private ScriptExpressionFactory scriptExpressionFactory; + @Autowired private ExpressionFactory expressionFactory; + + private static final String NAME = "execute-script"; + private static final String PARAM_SCRIPT = "script"; + private static final String PARAM_QUIET = "quiet"; // could be useful for other actions as well + private static final String PARAM_OUTPUT_ITEM = "outputItem"; // item name or type (as URI!) -- EXPERIMENTAL + private static final String PARAM_FOR_WHOLE_INPUT = "forWholeInput"; + + @PostConstruct + public void init() { + actionExecutorRegistry.register(NAME, ExecuteScriptActionExpressionType.class, this); + } + + private static class Parameters { + @NotNull private final ScriptExpression scriptExpression; + private final ItemDefinition outputDefinition; + private final boolean forWholeInput; + private final boolean quiet; + + private Parameters(@NotNull ScriptExpression scriptExpression, + ItemDefinition outputDefinition, boolean forWholeInput, boolean quiet) { + this.scriptExpression = scriptExpression; + this.outputDefinition = outputDefinition; + this.forWholeInput = forWholeInput; + this.quiet = quiet; + } + } + + @Override + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { + + checkRootAuthorization(context, globalResult, NAME); + + Parameters parameters = getParameters(action, input, context, globalResult); + PipelineData output = PipelineData.createEmpty(); + + if (parameters.forWholeInput) { + executeForWholeInput(input, output, parameters, context, globalResult); + } else { + iterateOverItems(input, context, globalResult, + (value, item, result) -> + processItem(context, parameters, output, item, value, result), + (value, exception) -> + context.println("Failed to execute script on " + getDescription(value) + exceptionSuffix(exception))); + } + return output; + } + + private void executeForWholeInput(PipelineData input, PipelineData output, Parameters parameters, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException { + OperationResult result = operationsHelper.createActionResult(null, this); + context.checkTaskStop(); + try { + TypedValue inputTypedValue = new TypedValue<>(input, PipelineData.class); + Object outObject = executeScript(parameters.scriptExpression, inputTypedValue, context.getInitialVariables(), context, result); + if (outObject != null) { + addToData(outObject, PipelineData.newOperationResult(), output); + } else { + // no definition means we don't plan to provide any output - so let's just copy the input item to the output + // (actually, null definition with non-null outObject should not occur) + if (parameters.outputDefinition == null) { + output.addAllFrom(input); + } + } + if (!parameters.quiet) { + context.println("Executed script on the pipeline"); + } + + } catch (Throwable ex) { + Throwable exception = processActionException(ex, NAME, null, context); // TODO value for error reporting (3rd parameter) + context.println("Failed to execute script on the pipeline" + exceptionSuffix(exception)); + } + operationsHelper.trimAndCloneResult(result, globalResult); + } + + private void processItem(ExecutionContext context, Parameters parameters, + PipelineData output, PipelineItem item, PrismValue value, OperationResult result) + throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, + ConfigurationException, SecurityViolationException { + // Hack. TODO: we need to add definitions to Pipeline items. + //noinspection unchecked + TypedValue typedValue = new TypedValue(value, value == null ? Object.class : value.getClass()); + Object outObject = executeScript(parameters.scriptExpression, typedValue, item.getVariables(), context, result); + if (outObject != null) { + addToData(outObject, item.getResult(), output); + } else { + // no definition means we don't plan to provide any output - so let's just copy the input item to the output + // (actually, null definition with non-null outObject should not occur) + if (parameters.outputDefinition == null) { + output.add(item); + } + } + if (!parameters.quiet) { + context.println("Executed script on " + getDescription(value)); + } + } + + private Parameters getParameters(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws CommunicationException, ObjectNotFoundException, SchemaException, + ScriptExecutionException, SecurityViolationException, ConfigurationException, ExpressionEvaluationException { + ScriptExpressionEvaluatorType script = expressionHelper.getActionArgument(ScriptExpressionEvaluatorType.class, action, + ExecuteScriptActionExpressionType.F_SCRIPT, PARAM_SCRIPT, input, context, null, PARAM_SCRIPT, globalResult); + ItemDefinition outputDefinition; + String outputItemUri = expressionHelper.getSingleArgumentValue(action.getParameter(), PARAM_OUTPUT_ITEM, false, false, + NAME, input, context, String.class, globalResult); + if (StringUtils.isNotBlank(outputItemUri)) { + outputDefinition = getItemDefinition(outputItemUri); + } else if (action instanceof ExecuteScriptActionExpressionType) { + ExecuteScriptActionExpressionType execute = (ExecuteScriptActionExpressionType) action; + if (execute.getOutputItemName() != null) { + outputDefinition = getItemDefinitionFromItemName(execute.getOutputItemName()); + } else if (execute.getOutputTypeName() != null) { + outputDefinition = getItemDefinitionFromTypeName(execute.getOutputTypeName()); + } else { + outputDefinition = null; + } + } else { + outputDefinition = null; + } + boolean forWholeInput = expressionHelper.getActionArgument(Boolean.class, action, + ExecuteScriptActionExpressionType.F_FOR_WHOLE_INPUT, PARAM_FOR_WHOLE_INPUT, input, context, false, PARAM_FOR_WHOLE_INPUT, globalResult); + boolean quiet = expressionHelper.getActionArgument(Boolean.class, action, + ExecuteScriptActionExpressionType.F_QUIET, PARAM_QUIET, input, context, false, PARAM_QUIET, globalResult); + + if (script == null) { + throw new IllegalArgumentException("No script provided"); + } + + ExpressionProfile expressionProfile = null; // TODO + + ScriptExpression scriptExpression; + try { + scriptExpression = scriptExpressionFactory.createScriptExpression(script, outputDefinition, + expressionProfile, expressionFactory, "script", globalResult); + } catch (ExpressionSyntaxException | SecurityViolationException e) { + throw new ScriptExecutionException("Couldn't parse script expression: " + e.getMessage(), e); + } + + return new Parameters(scriptExpression, outputDefinition, forWholeInput, quiet); + } + + private void addToData(@NotNull Object outObject, @NotNull OperationResult result, PipelineData output) { + if (outObject instanceof Collection) { + for (Object o : (Collection) outObject) { + addToData(o, result, output); + } + } else { + PrismValue value; + if (outObject instanceof PrismValue) { + value = (PrismValue) outObject; + } else if (outObject instanceof Objectable) { + value = prismContext.itemFactory().createObjectValue((Objectable) outObject); + } else if (outObject instanceof Containerable) { + value = prismContext.itemFactory().createContainerValue((Containerable) outObject); + } else { + value = prismContext.itemFactory().createPropertyValue(outObject); + } + output.add(new PipelineItem(value, result)); + } + } + + private ItemDefinition getItemDefinition(String uri) throws ScriptExecutionException { + QName name = QNameUtil.uriToQName(uri, true); + ItemDefinition byName = prismContext.getSchemaRegistry().findItemDefinitionByElementName(name); + if (byName != null) { + return byName; + } + + ItemDefinition byType = prismContext.getSchemaRegistry().findItemDefinitionByType(name); + if (byType != null) { + return byType; + } + + throw new ScriptExecutionException("Supplied item identification '" + uri + "' corresponds neither to item name nor type name"); + } + + private ItemDefinition getItemDefinitionFromItemName(QName itemName) throws ScriptExecutionException { + ItemDefinition def = prismContext.getSchemaRegistry().findItemDefinitionByElementName(itemName); + if (def != null) { + return def; + } + throw new ScriptExecutionException("Item with name '" + itemName + "' couldn't be found."); + } + + private ItemDefinition getItemDefinitionFromTypeName(QName typeName) throws ScriptExecutionException { + ItemDefinition byType = prismContext.getSchemaRegistry().findItemDefinitionByType(typeName); + if (byType != null) { + return byType; + } + + if (XmlTypeConverter.canConvert(typeName)) { + return prismContext.definitionFactory().createPropertyDefinition(SchemaConstantsGenerated.C_VALUE, typeName); + } + + TypeDefinition typeDef = prismContext.getSchemaRegistry().findTypeDefinitionByType(typeName); + if (typeDef instanceof SimpleTypeDefinition) { + return prismContext.definitionFactory().createPropertyDefinition(SchemaConstantsGenerated.C_VALUE, typeName); + } else if (typeDef instanceof ComplexTypeDefinition) { + ComplexTypeDefinition ctd = (ComplexTypeDefinition) typeDef; + if (ctd.isContainerMarker() || ctd.isObjectMarker()) { + return prismContext.definitionFactory().createContainerDefinition(SchemaConstantsGenerated.C_VALUE, ctd); + } else { + return prismContext.definitionFactory().createPropertyDefinition(SchemaConstantsGenerated.C_VALUE, typeName); + } + } else if (typeDef != null) { + throw new ScriptExecutionException("Type with name '" + typeName + "' couldn't be used as output type: " + typeDef); + } else { + throw new ScriptExecutionException("Type with name '" + typeName + "' couldn't be found."); + } + } + + private Object executeScript(ScriptExpression scriptExpression, TypedValue inputTypedValue, + VariablesMap externalVariables, ExecutionContext context, OperationResult result) + throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { + ExpressionVariables variables = new ExpressionVariables(); + variables.put(ExpressionConstants.VAR_INPUT, inputTypedValue); + variables.put(ExpressionConstants.VAR_PRISM_CONTEXT, prismContext, PrismContext.class); + ExpressionUtil.addActorVariable(variables, securityContextManager, prismContext); + //noinspection unchecked + externalVariables.forEach((k, v) -> variables.put(k, cloneIfNecessary(k, v))); + variables.registerAliasesFrom(externalVariables); + + List rv = ModelImplUtils.evaluateScript(scriptExpression, null, variables, true, "in '"+NAME+"' action", context.getTask(), result); + + if (rv == null || rv.size() == 0) { + return null; + } else if (rv.size() == 1) { + return rv.get(0); + } else { + return rv; // shouldn't occur; would cause problems + } + } + + @Override + String getActionName() { + return NAME; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/GenerateValueExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/GenerateValueExecutor.java index bda68090a8b..a1b8e5c8b48 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/GenerateValueExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/GenerateValueExecutor.java @@ -7,88 +7,81 @@ package com.evolveum.midpoint.model.impl.scripting.actions; +import static com.evolveum.midpoint.schema.constants.SchemaConstants.PATH_CREDENTIALS_PASSWORD_VALUE; + +import javax.annotation.PostConstruct; + +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.GenerateValueActionExpressionType; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.ModelInteractionService; -import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.PolicyItemDefinitionType; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.PolicyItemTargetType; import com.evolveum.midpoint.xml.ns._public.common.api_types_3.PolicyItemsDefinitionType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.PasswordType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; import com.evolveum.prism.xml.ns._public.types_3.ItemPathType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -import static com.evolveum.midpoint.schema.constants.SchemaConstants.PATH_CREDENTIALS_PASSWORD_VALUE; /** - * @author mederly + * Executes "generate-value" action. */ @Component -public class GenerateValueExecutor extends BaseActionExecutor { - - private static final Trace LOGGER = TraceManager.getTrace(GenerateValueExecutor.class); +public class GenerateValueExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "generate-value"; - public static final String PARAMETER_ITEMS = "items"; + private static final String PARAMETER_ITEMS = "items"; @Autowired private ModelInteractionService modelInteraction; @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, GenerateValueActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { - PolicyItemsDefinitionType itemsDefinition = expressionHelper.getSingleArgumentValue(expression.getParameter(), - PARAMETER_ITEMS, false, false, PARAMETER_ITEMS, input, context, - PolicyItemsDefinitionType.class, globalResult); - if (itemsDefinition == null) { + PolicyItemsDefinitionType itemsDefinition; + PolicyItemsDefinitionType configured = expressionHelper.getActionArgument(PolicyItemsDefinitionType.class, action, + GenerateValueActionExpressionType.F_ITEMS, PARAMETER_ITEMS, input, context, null, PARAMETER_ITEMS, globalResult); + if (configured != null) { + itemsDefinition = configured; + } else { itemsDefinition = new PolicyItemsDefinitionType().policyItemDefinition( new PolicyItemDefinitionType() .target(new PolicyItemTargetType().path(new ItemPathType(PATH_CREDENTIALS_PASSWORD_VALUE))) .execute(false)); } - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue) { - PrismObject object = ((PrismObjectValue) value).asPrismObject(); - ObjectType objectBean = object.asObjectable(); - long started = operationsHelper.recordStart(context, objectBean); - Throwable exception = null; - try { - LOGGER.trace("Generating value(s) for {}", objectBean); + iterateOverObjects(input, context, globalResult, + (object, item, result) -> { modelInteraction.generateValue(object, itemsDefinition, context.getTask(), result); - operationsHelper.recordEnd(context, objectBean, started, null); - } catch (Throwable e) { - operationsHelper.recordEnd(context, objectBean, started, e); - exception = processActionException(e, NAME, value, context); - } - context.println((exception != null ? "Attempted to generate value(s) for " : "Generated value(s) for ") + objectBean.toString() + exceptionSuffix(exception)); - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } + context.println("Generated value(s) for " + object); + }, + (object, exception) -> { + context.println("Failed to generate value(s) for " + object + exceptionSuffix(exception)); + }); return input; } + + @Override + String getActionName() { + return NAME; + } + + @Override + Class getObjectType() { + return ObjectType.class; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/LogExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/LogExecutor.java index 5594c59bcc5..848d36d8365 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/LogExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/LogExecutor.java @@ -12,16 +12,19 @@ import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.LogActionExpressionType; + import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** - * @author mederly + * Executes "log" scripting action. */ @Component public class LogExecutor extends BaseActionExecutor { @@ -29,39 +32,43 @@ public class LogExecutor extends BaseActionExecutor { private static final Trace LOGGER = TraceManager.getTrace(LogExecutor.class); public static final String NAME = "log"; - public static final String PARAM_LEVEL = "level"; - public static final String PARAM_MESSAGE = "message"; - public static final String LEVEL_INFO = "info"; - public static final String LEVEL_DEBUG = "debug"; - public static final String LEVEL_TRACE = "trace"; + private static final String PARAM_LEVEL = "level"; + private static final String PARAM_MESSAGE = "message"; + private static final String LEVEL_INFO = "info"; + private static final String LEVEL_DEBUG = "debug"; + private static final String LEVEL_TRACE = "trace"; + + private static final String DEFAULT_MESSAGE = "Current data: "; + private static final String DEFAULT_LEVEL = LEVEL_INFO; @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, LogActionExpressionType.class,this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult parentResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - String levelAsString = expressionHelper.getArgumentAsString(expression.getParameter(), PARAM_LEVEL, input, context, LEVEL_INFO, NAME, parentResult); - String message = expressionHelper.getArgumentAsString(expression.getParameter(), PARAM_MESSAGE, input, context, "Current data: ", NAME, parentResult); - message += "{}"; + String message = expressionHelper.getActionArgument(String.class, expression, + LogActionExpressionType.F_MESSAGE, PARAM_MESSAGE, input, context, DEFAULT_MESSAGE, NAME, globalResult) + "{}"; + String level = expressionHelper.getActionArgument(String.class, expression, + LogActionExpressionType.F_LEVEL, PARAM_LEVEL, input, context, DEFAULT_LEVEL, NAME, globalResult); - if (LEVEL_INFO.equals(levelAsString)) { - if (LOGGER.isInfoEnabled()) { - LOGGER.info(message, DebugUtil.debugDump(input)); - } - } else if (LEVEL_DEBUG.equals(levelAsString)) { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(message, DebugUtil.debugDump(input)); - } - } else if (LEVEL_TRACE.equals(levelAsString)) { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace(message, DebugUtil.debugDump(input)); - } + if (LEVEL_INFO.equalsIgnoreCase(level)) { + LOGGER.info(message, DebugUtil.debugDumpLazily(input)); + } else if (LEVEL_DEBUG.equalsIgnoreCase(level)) { + LOGGER.debug(message, DebugUtil.debugDumpLazily(input)); + } else if (LEVEL_TRACE.equalsIgnoreCase(level)) { + LOGGER.trace(message, DebugUtil.debugDumpLazily(input)); } else { - LOGGER.warn("Invalid logging level specified for 'log' scripting action: " + levelAsString); + LOGGER.warn("Invalid logging level specified for 'log' scripting action: " + level); } return input; } + + @Override + String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ModifyExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ModifyExecutor.java index 7122aa7b076..2f21b0104d2 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ModifyExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ModifyExecutor.java @@ -7,115 +7,110 @@ package com.evolveum.midpoint.model.impl.scripting.actions; +import javax.annotation.PostConstruct; + +import com.evolveum.midpoint.util.exception.*; + +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.ModelExecuteOptions; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.util.ModelImplUtils; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.schema.DeltaConvertor; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionParameterValueType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ModifyActionExpressionType; import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; import com.evolveum.prism.xml.ns._public.types_3.EvaluationTimeType; import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - /** - * @author mederly + * Executor for 'modify' actions. */ @Component -public class ModifyExecutor extends BaseActionExecutor { - - private static final Trace LOGGER = TraceManager.getTrace(ModifyExecutor.class); +public class ModifyExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "modify"; private static final String PARAM_DELTA = "delta"; @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, ModifyActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { - - ModelExecuteOptions executionOptions = getOptions(expression, input, context, globalResult); - boolean dryRun = getParamDryRun(expression, input, context, globalResult); - - ActionParameterValueType deltaParameterValue = expressionHelper.getArgument(expression.getParameter(), PARAM_DELTA, true, true, NAME); - PipelineData deltaData = expressionHelper.evaluateParameter(deltaParameterValue, ObjectDeltaType.class, input, context, globalResult); - - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue) { - @SuppressWarnings({"unchecked", "raw"}) - PrismObject prismObject = ((PrismObjectValue) value).asPrismObject(); - ObjectType objectType = prismObject.asObjectable(); - long started = operationsHelper.recordStart(context, objectType); - Throwable exception = null; - try { - ObjectDelta delta = createDelta(objectType, deltaData); - result.addParam("delta", delta); - // This is only a preliminary solution for MID-4138. There are few things to improve: - // 1. References could be resolved earlier (before the main cycle); however it would require much more - // coding, as we have only skeleton of ObjectDeltaType there - we don't know the specific object type - // the delta will be applied to. It is not a big problem, but still a bit of work. - // 2. If the evaluation time is IMPORT, and the bulk action is part of a task that is being imported into - // repository, it should be perhaps resolved at that time. But again, it is a lot of work and it does - // not cover bulk actions which are not part of a task. - // We consider this solution to be adequate for now. - ModelImplUtils.resolveReferences(delta, cacheRepositoryService, false, false, EvaluationTimeType.IMPORT, true, prismContext, result); - operationsHelper.applyDelta(delta, executionOptions, dryRun, context, result); - operationsHelper.recordEnd(context, objectType, started, null); - } catch (Throwable ex) { - operationsHelper.recordEnd(context, objectType, started, ex); - exception = processActionException(ex, NAME, value, context); - } - context.println((exception != null ? "Attempted to modify " : "Modified ") + prismObject.toString() + optionsSuffix(executionOptions, dryRun) + exceptionSuffix(exception)); - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + + ModelExecuteOptions options = operationsHelper.getOptions(action, input, context, globalResult); + boolean dryRun = operationsHelper.getDryRun(action, input, context, globalResult); + ObjectDeltaType deltaBean = expressionHelper.getActionArgument(ObjectDeltaType.class, action, + ModifyActionExpressionType.F_DELTA, PARAM_DELTA, input, context, null, + PARAM_DELTA, globalResult); + if (deltaBean == null) { + throw new SchemaException("Found no delta to be applied"); } + + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + modify(object, dryRun, options, deltaBean, context, result), + (object, exception) -> + context.println("Failed to recompute " + object + drySuffix(dryRun) + exceptionSuffix(exception)) + ); + return input; } - private ObjectDelta createDelta(ObjectType objectType, PipelineData deltaData) throws ScriptExecutionException { - if (deltaData.getData().size() != 1) { - throw new ScriptExecutionException("Expected exactly one delta to apply, found " + deltaData.getData().size() + " instead."); - } - @SuppressWarnings({"unchecked", "raw"}) - ObjectDeltaType deltaType = ((PrismPropertyValue) deltaData.getData().get(0).getValue()).clone().getRealValue(); - if (deltaType.getChangeType() == null) { - deltaType.setChangeType(ChangeTypeType.MODIFY); + private void modify(PrismObject object, boolean dryRun, ModelExecuteOptions options, + ObjectDeltaType deltaBean, ExecutionContext context, OperationResult result) + throws ScriptExecutionException, SchemaException { + ObjectDelta delta = createDelta(object.asObjectable(), deltaBean); + result.addParam("delta", delta); + + // This is only a preliminary solution for MID-4138. There are few things to improve: + // 1. References could be resolved earlier (before the main cycle); however it would require much more + // coding, as we have only skeleton of ObjectDeltaType there - we don't know the specific object type + // the delta will be applied to. It is not a big problem, but still a bit of work. + // 2. If the evaluation time is IMPORT, and the bulk action is part of a task that is being imported into + // repository, it should be perhaps resolved at that time. But again, it is a lot of work and it does + // not cover bulk actions which are not part of a task. + // We consider this solution to be adequate for now. + ModelImplUtils.resolveReferences(delta, cacheRepositoryService, false, false, + EvaluationTimeType.IMPORT, true, prismContext, result); + + operationsHelper.applyDelta(delta, options, dryRun, context, result); + context.println("Modified " + object + optionsSuffix(options, dryRun)); + } + + private ObjectDelta createDelta(ObjectType object, ObjectDeltaType deltaBean) + throws ScriptExecutionException, SchemaException { + if (deltaBean.getChangeType() == null) { + deltaBean.setChangeType(ChangeTypeType.MODIFY); } - if (deltaType.getOid() == null && deltaType.getChangeType() != ChangeTypeType.ADD) { - deltaType.setOid(objectType.getOid()); + if (deltaBean.getOid() == null && deltaBean.getChangeType() != ChangeTypeType.ADD) { + deltaBean.setOid(object.getOid()); } - if (deltaType.getObjectType() == null) { - if (objectType.asPrismObject().getDefinition() == null) { - throw new ScriptExecutionException("No definition for prism object " + objectType); + if (deltaBean.getObjectType() == null) { + if (object.asPrismObject().getDefinition() == null) { + throw new ScriptExecutionException("No definition for prism object " + object); } - deltaType.setObjectType(objectType.asPrismObject().getDefinition().getTypeName()); - } - try { - return DeltaConvertor.createObjectDelta(deltaType, prismContext); - } catch (SchemaException e) { - throw new ScriptExecutionException("Couldn't process delta due to schema exception", e); + deltaBean.setObjectType(object.asPrismObject().getDefinition().getTypeName()); } + return DeltaConvertor.createObjectDelta(deltaBean, prismContext); + } + + @Override + Class getObjectType() { + return ObjectType.class; + } + + @Override + String getActionName() { + return NAME; } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java index 56973eb8fe0..67a5768c6b9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/NotifyExecutor.java @@ -7,32 +7,35 @@ package com.evolveum.midpoint.model.impl.scripting.actions; +import static java.util.Objects.requireNonNull; + +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.api.PipelineItem; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.notifications.api.NotificationManager; import com.evolveum.midpoint.notifications.api.events.CustomEvent; import com.evolveum.midpoint.notifications.api.events.factory.CustomEventFactory; -import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventHandlerType; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventOperationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.EventStatusType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.NotifyActionExpressionType; /** - * @author mederly + * Executes "notify" actions. */ @Component public class NotifyExecutor extends BaseActionExecutor { - @Autowired(required = false) // During some tests this might be unavailable + @Autowired(required = false) // During some tests this might be unavailable private NotificationManager notificationManager; @Autowired(required = false) @@ -47,56 +50,54 @@ public class NotifyExecutor extends BaseActionExecutor { @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, NotifyActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - String subtype = expressionHelper.getArgumentAsString(expression.getParameter(), PARAM_SUBTYPE, input, context, null, PARAM_SUBTYPE, globalResult); - EventHandlerType handler = expressionHelper.getSingleArgumentValue(expression.getParameter(), PARAM_HANDLER, false, false, - PARAM_HANDLER, input, context, EventHandlerType.class, globalResult); - EventStatusType status = expressionHelper.getSingleArgumentValue(expression.getParameter(), PARAM_STATUS, false, false, - PARAM_STATUS, input, context, EventStatusType.class, globalResult); - EventOperationType operation = expressionHelper.getSingleArgumentValue(expression.getParameter(), PARAM_OPERATION, false, false, - PARAM_OPERATION, input, context, EventOperationType.class, globalResult); - boolean forWholeInput = expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_FOR_WHOLE_INPUT, input, context, false, PARAM_FOR_WHOLE_INPUT, globalResult); + String subtype = expressionHelper.getActionArgument(String.class, action, + NotifyActionExpressionType.F_SUBTYPE, PARAM_SUBTYPE, input, context, null, PARAM_SUBTYPE, globalResult); + EventHandlerType handler = expressionHelper.getActionArgument(EventHandlerType.class, action, + NotifyActionExpressionType.F_HANDLER, PARAM_HANDLER, input, context, null, PARAM_HANDLER, globalResult); + EventStatusType status = expressionHelper.getActionArgument(EventStatusType.class, action, + NotifyActionExpressionType.F_STATUS, PARAM_STATUS, input, context, EventStatusType.SUCCESS, PARAM_STATUS, globalResult); + EventOperationType operation = expressionHelper.getActionArgument(EventOperationType.class, action, + NotifyActionExpressionType.F_OPERATION, PARAM_OPERATION, input, context, EventOperationType.ADD, PARAM_OPERATION, globalResult); + boolean forWholeInput = expressionHelper.getActionArgument(Boolean.class, action, + NotifyActionExpressionType.F_FOR_WHOLE_INPUT, PARAM_FOR_WHOLE_INPUT, input, context, false, PARAM_FOR_WHOLE_INPUT, globalResult); if (handler != null) { - checkRootAuthorization(context, globalResult, NAME); // TODO explain that the reason is that handler is not null - } - - if (status == null) { - status = EventStatusType.SUCCESS; - } - if (operation == null) { - operation = EventOperationType.ADD; + checkRootAuthorization(context, globalResult, NAME); // TODO explain that the reason is that handler is not null } - if (notificationManager == null) { - throw new IllegalStateException("Notification manager is unavailable"); - } - if (customEventFactory == null) { - throw new IllegalStateException("Custom event factory is unavailable"); - } + requireNonNull(notificationManager, "Notification manager is unavailable"); + requireNonNull(customEventFactory, "Custom event factory is unavailable"); - int eventCount = 0; + AtomicInteger eventCount = new AtomicInteger(); if (forWholeInput) { CustomEvent event = customEventFactory.createEvent(subtype, handler, input.getData(), operation, status, context.getChannel()); notificationManager.processEvent(event, context.getTask(), globalResult); - eventCount++; + eventCount.incrementAndGet(); } else { - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - CustomEvent event = customEventFactory.createEvent(subtype, handler, value, operation, status, context.getChannel()); - notificationManager.processEvent(event, context.getTask(), result); - eventCount++; - operationsHelper.trimAndCloneResult(result, globalResult, context); - } + iterateOverItems(input, context, globalResult, + (value, item, result) -> { + CustomEvent event = customEventFactory.createEvent(subtype, handler, value, operation, status, context.getChannel()); + notificationManager.processEvent(event, context.getTask(), result); + eventCount.incrementAndGet(); + }, + (value, exception) -> { + context.println("Failed to notify on " + getDescription(value) + exceptionSuffix(exception)); + }); } - context.println("Produced " + eventCount + " event(s)"); + context.println("Produced " + eventCount.get() + " event(s)"); return input; } + + @Override + String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/PurgeSchemaExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/PurgeSchemaExecutor.java index 986bfba45de..ed042b0f2d3 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/PurgeSchemaExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/PurgeSchemaExecutor.java @@ -7,86 +7,74 @@ package com.evolveum.midpoint.model.impl.scripting.actions; -import com.evolveum.midpoint.model.api.ModelExecuteOptions; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.api.ScriptExecutionException; +import javax.annotation.PostConstruct; + +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.api.util.ResourceUtils; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; +import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.XmlSchemaType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; - -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.PurgeSchemaActionExpressionType; /** - * @author mederly + * Executes "purge-schema" action. */ @Component -public class PurgeSchemaExecutor extends BaseActionExecutor { - - private static final Trace LOGGER = TraceManager.getTrace(PurgeSchemaExecutor.class); +public class PurgeSchemaExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "purge-schema"; @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, PurgeSchemaActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException { PipelineData output = PipelineData.createEmpty(); - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue && ((PrismObjectValue) value).asObjectable() instanceof ResourceType) { - PrismObject resourceTypePrismObject = ((PrismObjectValue) value).asPrismObject(); - ResourceType resourceType = resourceTypePrismObject.asObjectable(); - long started = operationsHelper.recordStart(context, resourceType); - ObjectDelta delta = createDelta(resourceTypePrismObject.asObjectable()); - try { - if (delta != null) { - operationsHelper.applyDelta(delta, ModelExecuteOptions.createRaw(), context, result); - context.println("Purged schema information from " + resourceTypePrismObject); - output.addValue(operationsHelper.getObject(ResourceType.class, resourceTypePrismObject.getOid(), true, context, result).getValue(), item.getResult(), item.getVariables()); - } else { - context.println("There's no schema information to be purged in " + value); - output.addValue(resourceTypePrismObject.getValue(), item.getResult(), item.getVariables()); - } - operationsHelper.recordEnd(context, resourceType, started, null); - } catch (Throwable ex) { - operationsHelper.recordEnd(context, resourceType, started, ex); - Throwable exception = processActionException(ex, NAME, value, context); - context.println("Couldn't purge schema information from " + resourceTypePrismObject + exceptionSuffix(exception)); - } - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + purge(object, item, output, context, result), + (object, exception) -> + context.println("Failed to purge schema in " + object + exceptionSuffix(exception)) + ); + return output; } - private ObjectDelta createDelta(ResourceType resourceType) throws ScriptExecutionException { - PrismContainer schemaContainer = resourceType.asPrismObject().findContainer(ResourceType.F_SCHEMA); - if (schemaContainer == null || schemaContainer.isEmpty()) { - return null; - } - return prismContext.deltaFactory().object().createModificationDeleteContainer( - ResourceType.class, - resourceType.getOid(), - ResourceType.F_SCHEMA, - schemaContainer.getValue().clone()); + private void purge(PrismObject resource, PipelineItem item, PipelineData output, + ExecutionContext context, OperationResult result) throws ScriptExecutionException, + ExpressionEvaluationException, SchemaException, CommunicationException, ObjectAlreadyExistsException, + PolicyViolationException, SecurityViolationException, ConfigurationException, ObjectNotFoundException { + + ResourceUtils.deleteSchema(resource, modelService, prismContext, context.getTask(), result); + context.println("Purged schema information from " + resource); + + // It is questionable if noFetch should be used here. But it was so for a number of years. + // (Actually, the resource was fetched because of model operation used to implement + // deleteSchema method. So there is a complete version in the repository anyway.) + PrismObject resourceAfter = operationsHelper.getObject(ResourceType.class, + resource.getOid(), true, context, result); + output.addValue(resourceAfter.getValue(), item.getResult(), item.getVariables()); + } + + @Override + Class getObjectType() { + return ResourceType.class; + } + + @Override + String getActionName() { + return NAME; } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/RecomputeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/RecomputeExecutor.java index fb74e93981b..9cc1018dd64 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/RecomputeExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/RecomputeExecutor.java @@ -8,84 +8,95 @@ package com.evolveum.midpoint.model.impl.scripting.actions; import com.evolveum.midpoint.model.api.ModelExecuteOptions; -import com.evolveum.midpoint.model.impl.lens.Clockwork; -import com.evolveum.midpoint.model.impl.lens.ContextFactory; import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; -import com.evolveum.midpoint.prism.delta.ChangeType; import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerCreationType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; -import org.springframework.beans.factory.annotation.Autowired; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.RecomputeActionExpressionType; + import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** - * @author mederly + * Executes "recompute" action. */ @Component -public class RecomputeExecutor extends BaseActionExecutor { - - private static final Trace LOGGER = TraceManager.getTrace(RecomputeExecutor.class); +public class RecomputeExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "recompute"; - @Autowired - private ContextFactory contextFactory; - - @Autowired - private Clockwork clockwork; - @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, RecomputeActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { - - boolean dryRun = getParamDryRun(expression, input, context, globalResult); - - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue && FocusType.class.isAssignableFrom(((PrismObjectValue) value).asPrismObject().getCompileTimeClass())) { - PrismObject focalPrismObject = ((PrismObjectValue) value).asPrismObject(); - FocusType focusType = focalPrismObject.asObjectable(); - long started = operationsHelper.recordStart(context, focusType); - Throwable exception = null; - try { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Recomputing object {} with dryRun={}", focalPrismObject, dryRun); - } - ObjectDelta emptyDelta = prismContext.deltaFactory().object() - .createEmptyDelta(focusType.getClass(), focusType.getOid(), ChangeType.MODIFY - ); - operationsHelper.applyDelta(emptyDelta, ModelExecuteOptions.createReconcile(), dryRun, context, result); - LOGGER.trace("Recomputing of object {}: {}", focalPrismObject, result.getStatus()); - operationsHelper.recordEnd(context, focusType, started, null); - } catch (Throwable e) { - operationsHelper.recordEnd(context, focusType, started, e); - exception = processActionException(e, NAME, value, context); - } - context.println((exception != null ? "Attempted to recompute " : "Recomputed ") + focalPrismObject.toString() + drySuffix(dryRun) + exceptionSuffix(exception)); + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + + boolean dryRun = operationsHelper.getDryRun(action, input, context, globalResult); + ModelExecuteOptions options = operationsHelper.getOptions(action, input, context, globalResult); + options.setReconcile(true); + TriggerCreationType triggerCreation = action instanceof RecomputeActionExpressionType ? + ((RecomputeActionExpressionType) action).getTriggered() : null; + + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + recompute(object, dryRun, options, triggerCreation, context, result), + (object, exception) -> + context.println("Failed to recompute " + object + drySuffix(dryRun) + exceptionSuffix(exception)) + ); + + return input; + } + + private void recompute(PrismObject object, boolean dryRun, ModelExecuteOptions options, + TriggerCreationType triggerCreation, ExecutionContext context, OperationResult result) + throws ScriptExecutionException, SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException { + + AssignmentHolderType objectable = object.asObjectable(); + if (triggerCreation == null) { + ObjectDelta emptyDelta = prismContext.deltaFactory().object() + .createEmptyModifyDelta(objectable.getClass(), objectable.getOid()); + operationsHelper.applyDelta(emptyDelta, options, dryRun, context, result); + context.println("Recomputed " + object.toString() + drySuffix(dryRun)); + } else if (dryRun) { + context.println("Skipping dry run of triggered-recompute of " + object.toString()); + } else if (triggerCreation.getFireAfter() == null) { + // direct trigger creation + midpointFunctions.createRecomputeTrigger(objectable.getClass(), objectable.getOid()); + context.println("Triggered recompute of " + object.toString()); + } else { + // optimized trigger creation + long fireAfter = XmlTypeConverter.toMillis(triggerCreation.getFireAfter()); + long safetyMargin = triggerCreation.getSafetyMargin() != null ? + XmlTypeConverter.toMillis(triggerCreation.getSafetyMargin()) : 0; + boolean triggerCreated = midpointFunctions.getOptimizingTriggerCreator(fireAfter, safetyMargin) + .createForObject(objectable.getClass(), objectable.getOid()); + if (triggerCreated) { + context.println("Triggered recompute of " + object.toString()); } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); + context.println("Skipped triggering recompute of " + object.toString() + " because a trigger was already present"); } - operationsHelper.trimAndCloneResult(result, globalResult, context); } - return input; + } + + @Override + Class getObjectType() { + return AssignmentHolderType.class; + } + + @Override + protected String getActionName() { + return NAME; } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ReencryptExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ReencryptExecutor.java index a2e346c7c16..3b645c7ecd4 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ReencryptExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ReencryptExecutor.java @@ -16,6 +16,7 @@ import com.evolveum.midpoint.prism.crypto.Protector; import com.evolveum.midpoint.prism.delta.ItemDelta; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; import org.springframework.stereotype.Component; @@ -24,7 +25,10 @@ import java.util.Collection; /** - * @author mederly + * Executes "reencrypt" action. + * + * There is no static (typed) definition of this action yet. + * Also, this code is not refactored yet. */ @Component public class ReencryptExecutor extends BaseActionExecutor { @@ -35,21 +39,21 @@ public class ReencryptExecutor extends BaseActionExecutor { @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException, SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { Protector protector = prismContext.getDefaultProtector(); - boolean dryRun = getParamDryRun(expression, input, context, globalResult); + boolean dryRun = operationsHelper.getDryRun(expression, input, context, globalResult); PipelineData output = PipelineData.createEmpty(); for (PipelineItem item: input.getData()) { PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); + OperationResult result = operationsHelper.createActionResult(item, this); context.checkTaskStop(); if (value instanceof PrismObjectValue) { @SuppressWarnings({"unchecked", "raw"}) @@ -81,8 +85,13 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, //noinspection ThrowableNotThrown processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); } - operationsHelper.trimAndCloneResult(result, globalResult, context); + operationsHelper.trimAndCloneResult(result, globalResult); } return output; } + + @Override + String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResolveExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResolveExecutor.java index 682df7c0e3d..ca69b92e911 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResolveExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResolveExecutor.java @@ -7,76 +7,98 @@ package com.evolveum.midpoint.model.impl.scripting.actions; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.api.ScriptExecutionException; +import java.util.Collection; +import javax.annotation.PostConstruct; +import javax.xml.namespace.QName; + +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; +import com.evolveum.midpoint.prism.PrismObjectValue; +import com.evolveum.midpoint.prism.PrismReferenceValue; +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SelectorQualifiedGetOptionsType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; - -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.xml.namespace.QName; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ResolveReferenceActionExpressionType; /** * Resolves a reference, e.g. a linkRef into a set of accounts. - * - * @author mederly */ @Component public class ResolveExecutor extends BaseActionExecutor { - private static final Trace LOGGER = TraceManager.getTrace(ResolveExecutor.class); - private static final String NAME = "resolve"; private static final String PARAM_NO_FETCH = "noFetch"; @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, ResolveReferenceActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { - boolean noFetch = expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_NO_FETCH, input, context, false, NAME, globalResult); + Collection> options; + if (action instanceof ResolveReferenceActionExpressionType) { + SelectorQualifiedGetOptionsType optionsBean = ((ResolveReferenceActionExpressionType) action).getOptions(); + options = MiscSchemaUtil.optionsTypeToOptions(optionsBean, prismContext); + } else { + boolean noFetch = expressionHelper.getArgumentAsBoolean(action.getParameter(), PARAM_NO_FETCH, input, context, false, NAME, globalResult); + options = schemaHelper.getOperationOptionsBuilder().noFetch(noFetch).build(); + } PipelineData output = PipelineData.createEmpty(); - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismReferenceValue) { - PrismReferenceValue prismReferenceValue = (PrismReferenceValue) value; - String oid = prismReferenceValue.getOid(); - QName targetTypeQName = prismReferenceValue.getTargetType(); - if (targetTypeQName == null) { - throw new ScriptExecutionException("Couldn't resolve reference, because target type is unknown: " + prismReferenceValue); - } - Class typeClass = prismContext.getSchemaRegistry().determineCompileTimeClass(targetTypeQName); - if (typeClass == null) { - throw new ScriptExecutionException("Couldn't resolve reference, because target type class is unknown for target type " + targetTypeQName); - } - try { - PrismObjectValue resolved = operationsHelper.getObject(typeClass, oid, noFetch, context, result).getValue(); - output.add(new PipelineItem(resolved, item.getResult())); - } catch (Throwable e) { - //noinspection ThrowableNotThrown - processActionException(e, NAME, value, context); - output.add(item); // to keep track of failed item (may trigger exceptions downstream) - } - } else { + iterateOverItems(input, context, globalResult, + (value, item, result) -> + resolveReference(context, options, output, item, value, result), + (value, exception) -> + context.println("Couldn't resolve reference: " + value + exceptionSuffix(exception))); + + return output; + } + + private void resolveReference(ExecutionContext context, Collection> options, + PipelineData output, PipelineItem item, PrismValue value, OperationResult result) throws ScriptExecutionException { + if (value instanceof PrismReferenceValue) { + PrismReferenceValue prismReferenceValue = (PrismReferenceValue) value; + String oid = prismReferenceValue.getOid(); + QName targetTypeQName = prismReferenceValue.getTargetType(); + if (targetTypeQName == null) { + throw new ScriptExecutionException("Couldn't resolve reference, because target type is unknown: " + prismReferenceValue); + } + Class type = prismContext.getSchemaRegistry().determineCompileTimeClass(targetTypeQName); + if (type == null) { + throw new ScriptExecutionException("Couldn't resolve reference, because target type class is unknown for target type " + targetTypeQName); + } + try { + PrismObjectValue resolved = modelService.getObject(type, oid, options, context.getTask(), result).getValue(); + output.add(new PipelineItem(resolved, item.getResult())); + } catch (Throwable e) { //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismReference"), NAME, value, context); + processActionException(e, NAME, value, context); + output.add(item); // to keep track of failed item (may trigger exceptions downstream) } - operationsHelper.trimAndCloneResult(result, globalResult, context); + } else { + //noinspection ThrowableNotThrown + processActionException(new ScriptExecutionException("Value is not a reference"), NAME, value, context); + output.add(item); // to keep track of failed item (may trigger exceptions downstream) } - return output; + } + + @Override + String getActionName() { + return NAME; } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeExecutor.java deleted file mode 100644 index 77c7b477456..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeExecutor.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2010-2018 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ - -package com.evolveum.midpoint.model.impl.scripting.actions; - -import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectValue; -import com.evolveum.midpoint.prism.PrismValue; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; - -import static java.util.Collections.singleton; - -/** - * @author mederly - */ -@Component -public class ResumeExecutor extends BaseActionExecutor { - - //private static final Trace LOGGER = TraceManager.getTrace(ResumeExecutor.class); - - private static final String NAME = "resume"; - - @PostConstruct - public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); - } - - @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { - - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue) { - @SuppressWarnings({"unchecked", "raw"}) - PrismObject prismObject = ((PrismObjectValue) value).asPrismObject(); - ObjectType object = prismObject.asObjectable(); - if (object instanceof TaskType) { - long started = operationsHelper.recordStart(context, object); - Throwable exception = null; - try { - taskService.resumeTasks(singleton(object.getOid()), context.getTask(), result); - operationsHelper.recordEnd(context, object, started, null); - } catch (Throwable ex) { - operationsHelper.recordEnd(context, object, started, ex); - exception = processActionException(ex, NAME, value, context); - } - context.println((exception != null ? "Attempted to resume " : "Resumed ") + prismObject.toString() + exceptionSuffix(exception)); - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a task"), NAME, value, context); - } - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } - return input; - } -} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeTaskExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeTaskExecutor.java new file mode 100644 index 00000000000..f9e35cc9bfc --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ResumeTaskExecutor.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2010-2018 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.scripting.actions; + +import static java.util.Collections.singleton; + +import javax.annotation.PostConstruct; + +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ResumeTaskActionExpressionType; + +/** + * Executes "resume" action. + */ +@Component +public class ResumeTaskExecutor extends AbstractObjectBasedActionExecutor { + + private static final String NAME = "resume"; + + @PostConstruct + public void init() { + actionExecutorRegistry.register(NAME, ResumeTaskActionExpressionType.class, this); + } + + @Override + public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException { + + iterateOverObjects(input, context, globalResult, + (object, item, result) -> { + taskService.resumeTasks(singleton(object.getOid()), context.getTask(), result); + context.println("Resumed " + object); + }, (object, exception) -> + context.println("Failed to resume " + object + exceptionSuffix(exception)) + ); + return input; + } + + @Override + String getActionName() { + return NAME; + } + + @Override + Class getObjectType() { + return TaskType.class; + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java deleted file mode 100644 index f24b1fc49a4..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ScriptExecutor.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ - -package com.evolveum.midpoint.model.impl.scripting.actions; - -import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.model.api.ScriptExecutionException; -import com.evolveum.midpoint.model.common.expression.script.ScriptExpression; -import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionFactory; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.model.impl.util.ModelImplUtils; -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; -import com.evolveum.midpoint.repo.common.expression.ExpressionSyntaxException; -import com.evolveum.midpoint.repo.common.expression.ExpressionUtil; -import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; -import com.evolveum.midpoint.schema.constants.ExpressionConstants; -import com.evolveum.midpoint.schema.expression.ExpressionProfile; -import com.evolveum.midpoint.schema.expression.TypedValue; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ScriptExpressionEvaluatorType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; -import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.xml.namespace.QName; -import java.util.Collection; -import java.util.List; - -import static com.evolveum.midpoint.model.impl.scripting.VariablesUtil.cloneIfNecessary; - -/** - * @author mederly - */ -@Component -public class ScriptExecutor extends BaseActionExecutor { - - //private static final Trace LOGGER = TraceManager.getTrace(ScriptExecutor.class); - - @Autowired private ScriptExpressionFactory scriptExpressionFactory; - @Autowired private ExpressionFactory expressionFactory; - - private static final String NAME = "execute-script"; - private static final String PARAM_SCRIPT = "script"; - private static final String PARAM_QUIET = "quiet"; // todo implement for other actions as well - private static final String PARAM_OUTPUT_ITEM = "outputItem"; // item name or type (as URI!) -- EXPERIMENTAL - private static final String PARAM_FOR_WHOLE_INPUT = "forWholeInput"; - - @PostConstruct - public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); - } - - @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { - - checkRootAuthorization(context, globalResult, NAME); - - ScriptExpressionEvaluatorType script = expressionHelper.getSingleArgumentValue(expression.getParameter(), PARAM_SCRIPT, true, true, - NAME, input, context, ScriptExpressionEvaluatorType.class, globalResult); - String outputItem = expressionHelper.getSingleArgumentValue(expression.getParameter(), PARAM_OUTPUT_ITEM, false, false, - NAME, input, context, String.class, globalResult); - boolean forWholeInput = expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_FOR_WHOLE_INPUT, input, context, false, PARAM_FOR_WHOLE_INPUT, globalResult); - boolean quiet = expressionHelper.getArgumentAsBoolean(expression.getParameter(), PARAM_QUIET, input, context, false, PARAM_QUIET, globalResult); - - ItemDefinition outputDefinition = getItemDefinition(outputItem); - ExpressionProfile expressionProfile = null; // TODO - - ScriptExpression scriptExpression; - try { - scriptExpression = scriptExpressionFactory.createScriptExpression(script, outputDefinition, expressionProfile, expressionFactory, "script", - globalResult); - } catch (ExpressionSyntaxException | SecurityViolationException e) { - throw new ScriptExecutionException("Couldn't parse script expression: " + e.getMessage(), e); - } - - PipelineData output = PipelineData.createEmpty(); - - if (forWholeInput) { - OperationResult result = operationsHelper.createActionResult(null, this, context, globalResult); - context.checkTaskStop(); - Throwable exception = null; - try { - TypedValue inputTypedValue = new TypedValue<>(input, PipelineData.class); - Object outObject = executeScript(scriptExpression, inputTypedValue, context.getInitialVariables(), context, result); - if (outObject != null) { - addToData(outObject, PipelineData.newOperationResult(), output); - } else { - // no definition means we don't plan to provide any output - so let's just copy the input item to the output - // (actually, null definition with non-null outObject should not occur) - if (outputDefinition == null) { - output.addAllFrom(input); - } - } - } catch (Throwable ex) { - exception = processActionException(ex, NAME, null, context); // TODO value for error reporting (3rd parameter) - } - if (!quiet) { - context.println((exception != null ? "Attempted to execute " : "Executed ") - + "script on the pipeline" + exceptionSuffix(exception)); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } else { - for (PipelineItem item : input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - - context.checkTaskStop(); - String valueDescription; - long started; - if (value instanceof PrismObjectValue) { - started = operationsHelper.recordStart(context, asObjectType(value)); - valueDescription = asObjectType(value).asPrismObject().toString(); - } else { - started = 0; - valueDescription = value.toHumanReadableString(); - } - Throwable exception = null; - try { - // Hack. TODO: we need to add definitions to Pipeline items. - TypedValue typedValue = new TypedValue(value, value == null ? Object.class : value.getClass()); - Object outObject = executeScript(scriptExpression, typedValue, item.getVariables(), context, result); - if (outObject != null) { - addToData(outObject, item.getResult(), output); - } else { - // no definition means we don't plan to provide any output - so let's just copy the input item to the output - // (actually, null definition with non-null outObject should not occur) - if (outputDefinition == null) { - output.add(item); - } - } - if (value instanceof PrismObjectValue) { - operationsHelper.recordEnd(context, asObjectType(value), started, null); - } - } catch (Throwable ex) { - if (value instanceof PrismObjectValue) { - operationsHelper.recordEnd(context, asObjectType(value), started, ex); - } - exception = processActionException(ex, NAME, value, context); - } - if (!quiet) { - context.println((exception != null ? "Attempted to execute " : "Executed ") - + "script on " + valueDescription + exceptionSuffix(exception)); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } - } - return output; - } - - private void addToData(@NotNull Object outObject, @NotNull OperationResult result, PipelineData output) throws SchemaException { - if (outObject instanceof Collection) { - for (Object o : (Collection) outObject) { - addToData(o, result, output); - } - } else { - PrismValue value; - if (outObject instanceof PrismValue) { - value = (PrismValue) outObject; - } else if (outObject instanceof Objectable) { - value = prismContext.itemFactory().createObjectValue((Objectable) outObject); - } else if (outObject instanceof Containerable) { - value = prismContext.itemFactory().createContainerValue((Containerable) outObject); - } else { - value = prismContext.itemFactory().createPropertyValue(outObject); - } - output.add(new PipelineItem(value, result)); - } - } - - private ItemDefinition getItemDefinition(String itemUri) throws ScriptExecutionException { - if (StringUtils.isBlank(itemUri)) { - return null; - } - - QName itemName = QNameUtil.uriToQName(itemUri, true); - ItemDefinition def = prismContext.getSchemaRegistry().findItemDefinitionByElementName(itemName); - if (def != null) { - return def; - } - def = prismContext.getSchemaRegistry().findItemDefinitionByType(itemName); - if (def != null) { - return def; - } - throw new ScriptExecutionException("Supplied item identification " + itemUri + " corresponds neither to item name nor type name"); - } - - private Object executeScript(ScriptExpression scriptExpression, TypedValue inputTypedValue, - VariablesMap externalVariables, ExecutionContext context, OperationResult result) - throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, SecurityViolationException { - ExpressionVariables variables = new ExpressionVariables(); - variables.put(ExpressionConstants.VAR_INPUT, inputTypedValue); - variables.put(ExpressionConstants.VAR_PRISM_CONTEXT, prismContext, PrismContext.class); - ExpressionUtil.addActorVariable(variables, securityContextManager, prismContext); - externalVariables.forEach((k, v) -> variables.put(k, cloneIfNecessary(k, v))); - variables.registerAliasesFrom(externalVariables); - - List rv = ModelImplUtils.evaluateScript(scriptExpression, null, variables, true, "in '"+NAME+"' action", context.getTask(), result); - - if (rv == null || rv.size() == 0) { - return null; - } else if (rv.size() == 1) { - return rv.get(0); - } else { - return rv; // shouldn't occur; would cause problems - } - } - - private ObjectType asObjectType(PrismValue value) { - return (ObjectType) ((PrismObjectValue) value).asObjectable(); - } - -} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/TestResourceExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/TestResourceExecutor.java index 96559920918..02ac3da5e3a 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/TestResourceExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/TestResourceExecutor.java @@ -7,76 +7,71 @@ package com.evolveum.midpoint.model.impl.scripting.actions; -import com.evolveum.midpoint.model.impl.scripting.PipelineData; -import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; -import com.evolveum.midpoint.model.api.ScriptExecutionException; +import javax.annotation.PostConstruct; + +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectValue; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; - -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.TestResourceActionExpressionType; /** - * @author mederly + * Executes "test-resource" action. */ @Component -public class TestResourceExecutor extends BaseActionExecutor { - - private static final Trace LOGGER = TraceManager.getTrace(TestResourceExecutor.class); +public class TestResourceExecutor extends AbstractObjectBasedActionExecutor { private static final String NAME = "test-resource"; @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, TestResourceActionExpressionType.class, this); } @Override - public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, OperationResult globalResult) throws ScriptExecutionException { + public PipelineData execute(ActionExpressionType expression, PipelineData input, ExecutionContext context, + OperationResult globalResult) throws ScriptExecutionException { PipelineData output = PipelineData.createEmpty(); - for (PipelineItem item: input.getData()) { - PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); - context.checkTaskStop(); - if (value instanceof PrismObjectValue && ((PrismObjectValue) value).asObjectable() instanceof ResourceType) { - PrismObject resourceTypePrismObject = ((PrismObjectValue) value).asPrismObject(); - ResourceType resourceType = resourceTypePrismObject.asObjectable(); - long started = operationsHelper.recordStart(context, resourceType); - Throwable exception = null; - OperationResult testResult; - try { - testResult = modelService.testResource(resourceTypePrismObject.getOid(), context.getTask()); - operationsHelper.recordEnd(context, resourceType, started, null); - } catch (ObjectNotFoundException|RuntimeException e) { - operationsHelper.recordEnd(context, resourceType, started, e); - exception = processActionException(e, NAME, value, context); - testResult = new OperationResult(TestResourceExecutor.class.getName() + ".testResource"); - testResult.recordFatalError(e); - } - result.addSubresult(testResult); - context.println("Tested " + resourceTypePrismObject + ": " + testResult.getStatus() + exceptionSuffix(exception)); - try { - PrismObjectValue resourceValue = operationsHelper.getObject(ResourceType.class, resourceTypePrismObject.getOid(), false, context, result).getValue(); - output.add(new PipelineItem(resourceValue, item.getResult())); - } catch (ExpressionEvaluationException e) { - throw new ScriptExecutionException("Error getting resource "+resourceTypePrismObject.getOid()+": "+e.getMessage(), e); - } - } else { - //noinspection ThrowableNotThrown - processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); - } - operationsHelper.trimAndCloneResult(result, globalResult, context); - } + iterateOverObjects(input, context, globalResult, + (object, item, result) -> + test(object, output, item, context, result), + (object, exception) -> + context.println("Failed to test " + object + exceptionSuffix(exception)) + ); + return output; } + + private void test(PrismObject object, PipelineData output, PipelineItem item, ExecutionContext context, + OperationResult result) throws ObjectNotFoundException, ExpressionEvaluationException, ScriptExecutionException { + String oid = object.getOid(); + OperationResult testResult = modelService.testResource(oid, context.getTask()); + context.println("Tested " + object + ": " + testResult.getStatus()); + result.addSubresult(testResult); + + PrismObjectValue resourceAfter = operationsHelper.getObject(ResourceType.class, oid, + false, context, result).getValue(); + output.add(new PipelineItem(resourceAfter, item.getResult())); + } + + @Override + Class getObjectType() { + return ResourceType.class; + } + + @Override + String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java index ad5c4cb42d3..f2baba330a5 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. @@ -10,68 +10,136 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import javax.annotation.PostConstruct; import javax.xml.namespace.QName; +import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; + +import com.evolveum.midpoint.prism.query.ObjectFilter; +import com.evolveum.midpoint.prism.util.CloneUtil; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; + +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.UnassignActionExpressionType; +import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType; + import org.springframework.stereotype.Component; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +import static com.evolveum.midpoint.model.impl.scripting.actions.UnassignExecutor.*; /** - * + * Executor for "unassign" actions. */ @Component -public class UnassignExecutor extends AssignmentOperationsExecutor { +public class UnassignExecutor extends AssignmentOperationsExecutor { + + private static final Trace LOGGER = TraceManager.getTrace(UnassignExecutor.class); + + private static final String NAME = "unassign"; + + static class UnassignParameters extends Parameters { + // These come from dynamic parameters (~ legacy way) + private final Collection dynamicRoleRefs = new ArrayList<>(); + private final Collection dynamicResourceRefs = new ArrayList<>(); + private final Collection dynamicRelations = new ArrayList<>(); // used only with dynamicRoleRefs + // This one is defined statically (~ modern way) + private ObjectFilter staticFilter; + } + + @PostConstruct + public void init() { + actionExecutorRegistry.register(NAME, UnassignActionExpressionType.class, this); + } @Override - protected String getName() { - return AssignmentOperationsExecutor.UNASSIGN_NAME; + UnassignParameters parseParameters(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult result) throws SchemaException, ScriptExecutionException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + UnassignParameters parameters = new UnassignParameters(); + parameters.dynamicResourceRefs.addAll(getResourcesParameter(action, input, context, result)); + parameters.dynamicRoleRefs.addAll(getRolesParameter(action, input, context, result)); + parameters.dynamicRelations.addAll(getRelationsParameter(action, input, context, result)); + // This was the original behavior: if not specified, we look for org:default references. + if (parameters.dynamicRelations.isEmpty()) { + parameters.dynamicRelations.add(relationRegistry.getDefaultRelation()); + } + + if (action instanceof UnassignActionExpressionType) { + SearchFilterType filterBean = ((UnassignActionExpressionType) action).getFilter(); + if (filterBean != null) { + parameters.staticFilter = prismContext.getQueryConverter().parseFilter(filterBean, AssignmentType.class); + } + } + return parameters; } @Override - protected ObjectDelta createDelta(AssignmentHolderType object, Collection resources, - Collection roles, Collection relationSpecifications) throws SchemaException { + boolean checkParameters(UnassignParameters parameters, ExecutionContext context) { + if (parameters.dynamicRoleRefs.isEmpty() && parameters.dynamicResourceRefs.isEmpty() + && parameters.staticFilter == null) { + LOGGER.warn("There are no roles nor resources to unassign and no filter is specified"); + context.println("Warning: There are no roles nor resources to unassign and no filter is specified"); + return false; + } else { + return true; + } + } - List assignmentsToDelete = new ArrayList<>(); + @Override + protected ObjectDelta createDelta(AssignmentHolderType object, UnassignParameters parameters) + throws SchemaException { - for (AssignmentType oldAssignment : object.getAssignment()) { - ObjectReferenceType targetRef = oldAssignment.getTargetRef(); - if (targetRef != null) { - if (roles != null) { - outerloop: - for (ObjectReferenceType roleRef : roles) { - if (targetRef.getOid() != null && targetRef.getOid().equals(roleRef.getOid())) { - for (QName relationSpecification : relationSpecifications) { - if (prismContext.relationMatches(relationSpecification, targetRef.getRelation())) { - assignmentsToDelete.add(oldAssignment.clone()); - break outerloop; - } - } - } - } - } - } else if (oldAssignment.getConstruction() != null) { - if (resources != null) { - for (ObjectReferenceType resourceRefToUnassign : resources) { - ObjectReferenceType oldResourceRef = oldAssignment.getConstruction().getResourceRef(); - if (oldResourceRef != null && oldResourceRef.getOid() != null && - oldResourceRef.getOid().equals(resourceRefToUnassign.getOid())) { - assignmentsToDelete.add(oldAssignment.clone()); - break; - } - } - } + List assignmentsToDelete = new ArrayList<>(); + for (AssignmentType existingAssignment : object.getAssignment()) { + if (matches(existingAssignment, parameters)) { + assignmentsToDelete.add(existingAssignment); } } return prismContext.deltaFor(object.getClass()) .item(ItemPath.create(AssignmentHolderType.F_ASSIGNMENT)) - .deleteRealValues(assignmentsToDelete) + .deleteRealValues(CloneUtil.cloneCollectionMembers(assignmentsToDelete)) .asObjectDelta(object.getOid()); } + + @SuppressWarnings("SimplifiableIfStatement") + private boolean matches(AssignmentType existingAssignment, UnassignParameters parameters) throws SchemaException { + ObjectReferenceType targetRef = existingAssignment.getTargetRef(); + if (targetRef != null && matchesOid(targetRef.getOid(), parameters.dynamicRoleRefs) + && matchesRelation(targetRef.getRelation(), parameters.dynamicRelations)) { + return true; + } + ConstructionType construction = existingAssignment.getConstruction(); + if (construction != null && construction.getResourceRef() != null + && matchesOid(construction.getResourceRef().getOid(), parameters.dynamicResourceRefs)) { + return true; + } + return parameters.staticFilter != null + && parameters.staticFilter.match(existingAssignment.asPrismContainerValue(), matchingRuleRegistry); + } + + private boolean matchesOid(String existingOid, Collection refsToUnassign) { + return existingOid != null && + refsToUnassign.stream() + .anyMatch(ref -> existingOid.equals(ref.getOid())); + } + + private boolean matchesRelation(QName existingRelation, Collection relationsToUnassign) { + return relationsToUnassign.stream() + .anyMatch(relationToUnassign -> prismContext.relationMatches(relationToUnassign, existingRelation)); + } + + @Override + protected String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ValidateExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ValidateExecutor.java index 0cb2cdefb8b..105b93ae023 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ValidateExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/ValidateExecutor.java @@ -28,7 +28,10 @@ import javax.annotation.PostConstruct; /** - * @author mederly + * Executes "validate" action. + * + * There is no static (typed) definition of this action yet. + * Also, this code is not refactored yet. */ @Component public class ValidateExecutor extends BaseActionExecutor { @@ -42,7 +45,7 @@ public class ValidateExecutor extends BaseActionExecutor { @PostConstruct public void init() { - scriptingExpressionEvaluator.registerActionExecutor(NAME, this); + actionExecutorRegistry.register(NAME, this); } @Override @@ -52,7 +55,7 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, for (PipelineItem item: input.getData()) { PrismValue value = item.getValue(); - OperationResult result = operationsHelper.createActionResult(item, this, context, globalResult); + OperationResult result = operationsHelper.createActionResult(item, this); context.checkTaskStop(); if (value instanceof PrismObjectValue && ((PrismObjectValue) value).asObjectable() instanceof ResourceType) { PrismObject resourceTypePrismObject = ((PrismObjectValue) value).asPrismObject(); @@ -78,8 +81,13 @@ public PipelineData execute(ActionExpressionType expression, PipelineData input, //noinspection ThrowableNotThrown processActionException(new ScriptExecutionException("Item is not a PrismObject"), NAME, value, context); } - operationsHelper.trimAndCloneResult(result, globalResult, context); + operationsHelper.trimAndCloneResult(result, globalResult); } return output; } + + @Override + String getActionName() { + return NAME; + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/expressions/SearchEvaluator.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/expressions/SearchEvaluator.java index 5fe7b516c96..55146d1a654 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/expressions/SearchEvaluator.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/expressions/SearchEvaluator.java @@ -40,7 +40,6 @@ import org.springframework.stereotype.Component; import javax.xml.bind.JAXBElement; -import javax.xml.namespace.QName; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -48,7 +47,7 @@ import static com.evolveum.midpoint.model.impl.scripting.VariablesUtil.cloneIfNecessary; /** - * @author mederly + * Evaluates "search" scripting expression. */ @Component public class SearchEvaluator extends BaseExpressionEvaluator { @@ -63,7 +62,7 @@ public class SearchEvaluator extends BaseExpressionEvaluator { public PipelineData evaluate(SearchExpressionType searchExpression, PipelineData input, ExecutionContext context, OperationResult globalResult) - throws ScriptExecutionException { + throws ScriptExecutionException, SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { Validate.notNull(searchExpression.getType()); ExpressionProfile expressionProfile = MiscSchemaUtil.getExpressionProfile(); @@ -139,7 +138,7 @@ public PipelineData evaluate(SearchExpressionType searchE } globalResult.setSummarizeSuccesses(true); globalResult.summarize(); - } catch (ScriptExecutionException e) { + } catch (ScriptExecutionException | SchemaException | ConfigurationException | ObjectNotFoundException | CommunicationException | SecurityViolationException | ExpressionEvaluationException e) { // todo think about this if (context.isContinueOnAnyError()) { LoggingUtils.logUnexpectedException(LOGGER, "Exception when evaluating item from search result list.", e); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ExpressionHelper.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ExpressionHelper.java index 2ee087a9ed5..65456995718 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ExpressionHelper.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ExpressionHelper.java @@ -14,8 +14,11 @@ import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; import com.evolveum.midpoint.prism.PrismPropertyValue; import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.prism.util.JavaTypeConverter; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionParameterValueType; import com.evolveum.prism.xml.ns._public.types_3.RawType; import org.apache.commons.lang.Validate; @@ -29,17 +32,12 @@ import java.util.List; /** - * @author mederly + * */ @Component public class ExpressionHelper { - @Autowired - private ScriptingExpressionEvaluator scriptingExpressionEvaluator; - - // public JAXBElement getArgument(ActionExpressionType actionExpression, String parameterName) throws ScriptExecutionException { - // return getArgument(actionExpression.getParameter(), parameterName, false, false, actionExpression.getType()); - // } + @Autowired private ScriptingExpressionEvaluator scriptingExpressionEvaluator; public ActionParameterValueType getArgument(List arguments, String parameterName, boolean required, boolean requiredNonNull, String context) throws ScriptExecutionException { @@ -64,18 +62,18 @@ public ActionParameterValueType getArgument(List argum } public String getArgumentAsString(List arguments, String argumentName, PipelineData input, ExecutionContext context, - String defaultValue, String contextName, OperationResult parentResult) throws ScriptExecutionException { + String defaultValue, String contextName, OperationResult parentResult) throws ScriptExecutionException, SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { ActionParameterValueType parameterValue = getArgument(arguments, argumentName, false, false, contextName); if (parameterValue != null) { if (parameterValue.getScriptingExpression() != null) { PipelineData data = scriptingExpressionEvaluator.evaluateExpression(parameterValue.getScriptingExpression(), input, context, parentResult); if (data != null) { - return data.getDataAsSingleString(); + return data.getSingleValue(String.class); } } else if (parameterValue.getValue() != null) { - PipelineData data = scriptingExpressionEvaluator.evaluateConstantStringExpression((RawType) parameterValue.getValue(), context, parentResult); + PipelineData data = scriptingExpressionEvaluator.evaluateConstantStringExpression((RawType) parameterValue.getValue(), context); if (data != null) { - return data.getDataAsSingleString(); + return data.getSingleValue(String.class); } } else { throw new IllegalStateException("No expression nor value specified"); @@ -84,8 +82,31 @@ public String getArgumentAsString(List arguments, Stri return defaultValue; } + public T getActionArgument(Class clazz, ActionExpressionType action, ItemName staticName, String dynamicName, PipelineData input, ExecutionContext context, + T defaultValue, String contextName, OperationResult parentResult) throws ScriptExecutionException, SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { + List arguments = action.getParameter(); + ActionParameterValueType dynamicValue = getArgument(arguments, dynamicName, false, false, contextName); + if (dynamicValue != null) { + if (dynamicValue.getScriptingExpression() != null) { + PipelineData data = scriptingExpressionEvaluator.evaluateExpression(dynamicValue.getScriptingExpression(), input, context, parentResult); + if (data != null) { + return data.getSingleValue(clazz); + } + } else if (dynamicValue.getValue() != null) { + return ScriptingDataUtil.getRealValue(dynamicValue.getValue(), clazz); + } else { + throw new IllegalStateException("No expression nor value specified in parameter '" + dynamicName + "'"); + } + } + T staticValue = ScriptingBeansUtil.getBeanPropertyValue(action, staticName.getLocalPart(), clazz); + if (staticValue != null) { + return staticValue; + } + return defaultValue; + } + public Boolean getArgumentAsBoolean(List arguments, String argumentName, PipelineData input, ExecutionContext context, - Boolean defaultValue, String contextName, OperationResult parentResult) throws ScriptExecutionException { + Boolean defaultValue, String contextName, OperationResult parentResult) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { String stringValue = getArgumentAsString(arguments, argumentName, input, context, null, contextName, parentResult); if (stringValue == null) { return defaultValue; @@ -99,19 +120,19 @@ public Boolean getArgumentAsBoolean(List arguments, St } public PipelineData evaluateParameter(ActionParameterValueType parameter, @Nullable Class expectedClass, PipelineData input, ExecutionContext context, OperationResult result) - throws ScriptExecutionException { + throws ScriptExecutionException, SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { Validate.notNull(parameter, "parameter"); if (parameter.getScriptingExpression() != null) { return scriptingExpressionEvaluator.evaluateExpression(parameter.getScriptingExpression(), input, context, result); } else if (parameter.getValue() != null) { - return scriptingExpressionEvaluator.evaluateConstantExpression((RawType) parameter.getValue(), expectedClass, context, "evaluating parameter " + parameter.getName(), result); + return scriptingExpressionEvaluator.evaluateConstantExpression((RawType) parameter.getValue(), expectedClass, context, "evaluating parameter " + parameter.getName()); } else { throw new IllegalStateException("No expression nor value specified"); } } public T getSingleArgumentValue(List arguments, String parameterName, boolean required, - boolean requiredNonNull, String context, PipelineData input, ExecutionContext executionContext, Class clazz, OperationResult result) throws ScriptExecutionException { + boolean requiredNonNull, String context, PipelineData input, ExecutionContext executionContext, Class clazz, OperationResult result) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { ActionParameterValueType paramValue = getArgument(arguments, parameterName, required, requiredNonNull, context); if (paramValue == null) { return null; @@ -134,22 +155,22 @@ public T getSingleArgumentValue(List arguments, St @NotNull public Collection getArgumentValues(List arguments, String parameterName, boolean required, - boolean requiredNonNull, String context, PipelineData input, ExecutionContext executionContext, Class clazz, OperationResult result) throws ScriptExecutionException { + boolean requiredNonNull, String context, PipelineData input, ExecutionContext executionContext, Class clazz, + OperationResult result) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { List rv = new ArrayList<>(); for (ActionParameterValueType paramValue : arguments) { if (parameterName.equals(paramValue.getName())) { if (paramValue.getScriptingExpression() != null || paramValue.getValue() != null) { - if (paramValue != null) { - PipelineData paramData = evaluateParameter(paramValue, clazz, input, executionContext, result); - for (PipelineItem item : paramData.getData()) { - PrismValue prismValue = item.getValue(); - if (!(prismValue instanceof PrismPropertyValue)) { - throw new ScriptExecutionException( - "A prism property value was expected in '" + parameterName + "' parameter. Got " + prismValue - .getClass().getName() + " instead."); - } else { - rv.add(JavaTypeConverter.convert(clazz, prismValue.getRealValue())); - } + PipelineData paramData = evaluateParameter(paramValue, clazz, input, executionContext, result); + for (PipelineItem item : paramData.getData()) { + PrismValue prismValue = item.getValue(); + if (!(prismValue instanceof PrismPropertyValue)) { + throw new ScriptExecutionException( + "A prism property value was expected in '" + parameterName + "' parameter. Got " + prismValue + .getClass().getName() + " instead."); + } else { + rv.add(JavaTypeConverter.convert(clazz, prismValue.getRealValue())); } } } else { @@ -161,7 +182,7 @@ public Collection getArgumentValues(List argume } } } - if (required) { + if (required && rv.isEmpty()) { throw new ScriptExecutionException("Required parameter " + parameterName + " not present in invocation of \"" + context + "\""); } else { return rv; diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/OperationsHelper.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/OperationsHelper.java index 8510f27ea5b..fa3bccea930 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/OperationsHelper.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/OperationsHelper.java @@ -10,6 +10,7 @@ import com.evolveum.midpoint.model.api.*; import com.evolveum.midpoint.model.impl.scripting.ActionExecutor; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.delta.ObjectDelta; @@ -32,15 +33,23 @@ import com.evolveum.midpoint.util.exception.SecurityViolationException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ModelExecuteOptionsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.PartialProcessingOptionsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.SelectorQualifiedGetOptionsType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.AbstractExecutionActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; + +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Collections; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.PartialProcessingTypeType.SKIP; + /** * @author mederly */ @@ -49,9 +58,61 @@ public class OperationsHelper { private static final Trace LOGGER = TraceManager.getTrace(OperationsHelper.class); + private static final String PARAM_RAW = "raw"; + private static final String PARAM_DRY_RUN = "dryRun"; + private static final String PARAM_SKIP_APPROVALS = "skipApprovals"; + private static final String PARAM_OPTIONS = "options"; + @Autowired private ModelService modelService; @Autowired private ModelInteractionService modelInteractionService; @Autowired private PrismContext prismContext; + @Autowired private ExpressionHelper expressionHelper; + + public boolean getDryRun(ActionExpressionType action, PipelineData input, ExecutionContext context, OperationResult result) + throws ScriptExecutionException, SchemaException, ObjectNotFoundException, SecurityViolationException, + CommunicationException, ConfigurationException, ExpressionEvaluationException { + return expressionHelper.getActionArgument(Boolean.class, action, + AbstractExecutionActionExpressionType.F_DRY_RUN, PARAM_DRY_RUN, + input, context, false, PARAM_DRY_RUN, result); + } + + @NotNull + public ModelExecuteOptions getOptions(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult result) throws ScriptExecutionException, SchemaException, ConfigurationException, + ObjectNotFoundException, CommunicationException, SecurityViolationException, ExpressionEvaluationException { + ModelExecuteOptions options = getRawOptions(action, input, context, result); + + // raw and skipApprovals are not part of static schema + Boolean raw = expressionHelper.getArgumentAsBoolean(action.getParameter(), PARAM_RAW, input, context, null, PARAM_RAW, result); + Boolean skipApprovals = expressionHelper.getArgumentAsBoolean(action.getParameter(), PARAM_SKIP_APPROVALS, input, context, null, PARAM_SKIP_APPROVALS, result); + + if (Boolean.TRUE.equals(raw)) { + options.setRaw(true); + } + if (Boolean.TRUE.equals(skipApprovals)) { + if (options.getPartialProcessing() != null) { + options.getPartialProcessing().setApprovals(SKIP); + } else { + options.setPartialProcessing(new PartialProcessingOptionsType().approvals(SKIP)); + } + } + return options; + } + + @NotNull + private ModelExecuteOptions getRawOptions(ActionExpressionType action, PipelineData input, ExecutionContext context, + OperationResult result) throws ScriptExecutionException, SchemaException, ObjectNotFoundException, + SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + ModelExecuteOptionsType optionsBean = expressionHelper.getActionArgument(ModelExecuteOptionsType.class, action, + AbstractExecutionActionExpressionType.F_EXECUTE_OPTIONS, PARAM_OPTIONS, input, context, null, + "executeOptions", result); + if (optionsBean != null) { + return ModelExecuteOptions.fromModelExecutionOptionsType(optionsBean); + } else { + return new ModelExecuteOptions(); + } + } + public Collection> applyDelta(ObjectDelta delta, ExecutionContext context, OperationResult result) throws ScriptExecutionException { return applyDelta(delta, null, context, result); @@ -59,6 +120,7 @@ public Collection> applyDelta(ObjectD public Collection> applyDelta(ObjectDelta delta, ModelExecuteOptions options, ExecutionContext context, OperationResult result) throws ScriptExecutionException { try { + //noinspection unchecked return modelService.executeChanges(Collections.singleton(delta), options, context.getTask(), result); } catch (ObjectAlreadyExistsException|ObjectNotFoundException|SchemaException|ExpressionEvaluationException|CommunicationException|ConfigurationException|PolicyViolationException|SecurityViolationException e) { throw new ScriptExecutionException("Couldn't modify object: " + e.getMessage(), e); @@ -67,6 +129,7 @@ public Collection> applyDelta(ObjectD public Collection> applyDelta(ObjectDelta delta, ModelExecuteOptions options, boolean dryRun, ExecutionContext context, OperationResult result) throws ScriptExecutionException { try { + LOGGER.debug("Going to execute delta (raw={}):\n{}", dryRun, delta.debugDumpLazily()); if (dryRun) { modelInteractionService.previewChanges(Collections.singleton(delta), options, context.getTask(), result); return null; @@ -103,12 +166,6 @@ public PrismObject getObject(Class type, String oid } } - public ModelExecuteOptions createExecutionOptions(boolean raw) { - ModelExecuteOptions options = new ModelExecuteOptions(); - options.setRaw(raw); - return options; - } - public long recordStart(ExecutionContext context, ObjectType objectType) { long started = System.currentTimeMillis(); if (context.isRecordProgressAndIterationStatistics()) { @@ -149,8 +206,7 @@ public void recordEnd(ExecutionContext context, ObjectType objectType, long star } } - public OperationResult createActionResult(PipelineItem item, ActionExecutor executor, ExecutionContext context, - OperationResult globalResult) { + public OperationResult createActionResult(PipelineItem item, ActionExecutor executor) { OperationResult result = new OperationResult(executor.getClass().getName() + "." + "execute"); if (item != null) { result.addParam("value", String.valueOf(item.getValue())); @@ -159,11 +215,10 @@ public OperationResult createActionResult(PipelineItem item, ActionExecutor exec return result; } - public void trimAndCloneResult(OperationResult result, OperationResult globalResult, - ExecutionContext context) { + public void trimAndCloneResult(OperationResult result, OperationResult globalResult) { result.computeStatusIfUnknown(); // TODO make this configurable - result.getSubresults().forEach(s -> s.setMinor()); + result.getSubresults().forEach(OperationResult::setMinor); result.cleanupResult(); globalResult.addSubresult(result.clone()); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingBeansUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingBeansUtil.java new file mode 100644 index 00000000000..19af01dae0e --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingBeansUtil.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.scripting.helpers; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; + +import org.apache.commons.beanutils.MethodUtils; +import org.apache.commons.beanutils.PropertyUtils; + +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ObjectFactory; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionType; + +import org.apache.commons.lang3.StringUtils; + +/** + * Utility methods related to ScriptingExpressionType beans. + */ +public class ScriptingBeansUtil { + + private static final Trace LOGGER = TraceManager.getTrace(ScriptingBeansUtil.class); + + private static final Map, QName> ELEMENTS = new HashMap<>(); + + static { + ObjectFactory objectFactory = new ObjectFactory(); + + for (Method method : objectFactory.getClass().getDeclaredMethods()) { + if (method.getReturnType() == JAXBElement.class) { + JAXBElement jaxbElement; + try { + //noinspection unchecked + jaxbElement = (JAXBElement) method.invoke(objectFactory, new Object[1]); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException("Couldn't execute " + method); + } + + QName elementName = jaxbElement.getName(); + Class elementType = jaxbElement.getDeclaredType(); + if (ELEMENTS.containsKey(elementType)) { + throw new IllegalStateException("More than one JAXBElement for " + elementType + ": " + + elementName + ", " + ELEMENTS.get(elementType)); + } else { + ELEMENTS.put(elementType, elementName); + } + } + } + LOGGER.trace("Map: {}", ELEMENTS); + } + + /** + * Sometimes we have to convert "bare" ScriptingExpressionType instance to the JAXBElement version, + * with the correct element name. + */ + public static JAXBElement toJaxbElement(T expression) { + QName qname = ELEMENTS.get(expression.getClass()); + if (qname != null) { + //noinspection unchecked + return new JAXBElement<>(qname, (Class) expression.getClass(), expression); + } else { + throw new IllegalArgumentException("Unsupported expression type: " + expression.getClass()); + } + } + + public static String getActionType(ActionExpressionType action) { + if (action.getType() != null) { + return action.getType(); + } else { + return toJaxbElement(action).getName().getLocalPart(); + } + } + + static T getBeanPropertyValue(ActionExpressionType action, String propertyName, Class clazz) throws SchemaException { + try { + try { + Object rawValue = PropertyUtils.getSimpleProperty(action, propertyName); + return ScriptingDataUtil.cast(rawValue, clazz); + } catch (NoSuchMethodException e) { + if (Boolean.class.equals(clazz)) { + // Note that getSimpleProperty looks for "getX" instead of our "isX" getter for Boolean (not boolean) props. + //noinspection unchecked + return (T) getBeanBooleanPropertyValue(action, propertyName); + } else { + // This can occur when dynamic parameters are used: the action is of generic type, not the specific one. + return null; + } + } + } catch (IllegalAccessException | InvocationTargetException e) { + throw new SchemaException("Couldn't access '" + propertyName + "' in '" + action + "'", e); + } + } + + private static Boolean getBeanBooleanPropertyValue(ActionExpressionType action, String propertyName) + throws IllegalAccessException, InvocationTargetException, SchemaException { + try { + String methodName = "is" + StringUtils.capitalize(propertyName); + Object rawValue = MethodUtils.invokeExactMethod(action, methodName, new Object[0]); + return ScriptingDataUtil.cast(rawValue, Boolean.class); + } catch (NoSuchMethodException e) { + // This can occur when dynamic parameters are used: the action is of generic type, not the specific one. + return null; + } + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingDataUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingDataUtil.java new file mode 100644 index 00000000000..092719e4a20 --- /dev/null +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingDataUtil.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.impl.scripting.helpers; + +import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.prism.xml.ns._public.types_3.RawType; + +/** + * Utility methods related to processing data objects. + */ +public class ScriptingDataUtil { + + public static T getRealValue(Object value, Class clazz) throws SchemaException { + if (value == null) { + return null; + } else if (value instanceof RawType) { + return ((RawType) value).getParsedRealValue(clazz); + } else if (value instanceof PrismValue) { + return getRealValue((PrismValue) value, clazz); + } else + return cast(value, clazz); + } + + public static T getRealValue(PrismValue prismValue, Class clazz) throws SchemaException { + if (prismValue == null) { + return null; + } else { + Object realValue = prismValue.getRealValue(); + if (realValue == null) { + throw new SchemaException("Real value of 'null' embedded in " + prismValue); + } else { + return cast(realValue, clazz); + } + } + } + + static T cast(Object value, Class expectedClass) throws SchemaException { + if (value == null) { + return null; + } else if (!expectedClass.isAssignableFrom(value.getClass())) { + throw new SchemaException("Expected '" + expectedClass.getName() + "' but got '" + value.getClass().getName() + "'"); + } else { + //noinspection unchecked + return (T) value; + } + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingJaxbUtil.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingJaxbUtil.java deleted file mode 100644 index 80ed118b20a..00000000000 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/helpers/ScriptingJaxbUtil.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2010-2014 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ - -package com.evolveum.midpoint.model.impl.scripting.helpers; - -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ActionExpressionType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ExpressionPipelineType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ExpressionSequenceType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.FilterExpressionType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ForeachExpressionType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ObjectFactory; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.SearchExpressionType; -import com.evolveum.midpoint.xml.ns._public.model.scripting_3.SelectExpressionType; - -import javax.xml.bind.JAXBElement; -import javax.xml.namespace.QName; -import java.util.HashMap; -import java.util.Map; - -/** - * @author mederly - */ -public class ScriptingJaxbUtil { - - private static Map, QName> elements = new HashMap<>(); - - private static ObjectFactory objectFactory = new ObjectFactory(); - - static { - elements.put(ExpressionPipelineType.class, objectFactory.createPipeline(null).getName()); - elements.put(ExpressionSequenceType.class, objectFactory.createSequence(null).getName()); - elements.put(ForeachExpressionType.class, objectFactory.createForeach(null).getName()); - elements.put(SelectExpressionType.class, objectFactory.createSelect(null).getName()); - elements.put(FilterExpressionType.class, objectFactory.createFilter(null).getName()); - elements.put(SearchExpressionType.class, objectFactory.createSearch(null).getName()); - elements.put(ActionExpressionType.class, objectFactory.createAction(null).getName()); - } - - /** - * Ugly hack ... sometimes we have to convert "bare" ScriptingExpressionType instance to the JAXBElement version, - * with the correct element name. - */ - @SuppressWarnings({"raw", "unchecked"}) - public static JAXBElement toJaxbElement(ScriptingExpressionType expressionType) { - QName qname = elements.get(expressionType.getClass()); - if (qname == null) { - throw new IllegalArgumentException("Unsupported expression type: " + expressionType.getClass()); - } - return new JAXBElement(qname, expressionType.getClass(), expressionType); - } - -} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/AbstractBasicScriptingTest.java similarity index 67% rename from model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java rename to model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/AbstractBasicScriptingTest.java index 58e56d53e8f..707872b0239 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasic.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/AbstractBasicScriptingTest.java @@ -1,130 +1,155 @@ /* - * Copyright (c) 2010-2019 Evolveum and contributors + * Copyright (c) 2020 Evolveum and contributors * * This work is dual-licensed under the Apache License 2.0 * and European Union Public License. See LICENSE file for details. */ package com.evolveum.midpoint.model.intest.scripting; +import static com.evolveum.midpoint.util.MiscUtil.emptyIfNull; + import static java.util.Collections.singleton; -import static org.apache.commons.collections4.CollectionUtils.emptyIfNull; import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.*; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; +import com.evolveum.midpoint.model.api.ModelPublicConstants; import com.evolveum.midpoint.model.api.ScriptExecutionException; +import com.evolveum.midpoint.notifications.api.transports.Message; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.crypto.EncryptionException; +import com.evolveum.midpoint.prism.delta.ItemDelta; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.prism.util.PrismAsserts; +import com.evolveum.midpoint.schema.constants.SchemaConstants; + +import com.evolveum.midpoint.test.util.TestUtil; +import com.evolveum.midpoint.util.exception.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; +import org.testng.SkipException; import org.testng.annotations.Test; -import org.testng.collections.Sets; import com.evolveum.midpoint.common.LoggingConfigurationManager; -import com.evolveum.midpoint.model.api.ModelPublicConstants; import com.evolveum.midpoint.model.api.PipelineItem; import com.evolveum.midpoint.model.impl.ModelWebService; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; import com.evolveum.midpoint.model.intest.AbstractInitializedModelIntegrationTest; -import com.evolveum.midpoint.notifications.api.transports.Message; -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.crypto.EncryptionException; -import com.evolveum.midpoint.prism.delta.ItemDelta; -import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemName; -import com.evolveum.midpoint.prism.util.PrismAsserts; -import com.evolveum.midpoint.schema.constants.RelationTypes; -import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.expression.VariablesMap; import com.evolveum.midpoint.schema.internals.InternalMonitor; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.result.OperationResultStatus; -import com.evolveum.midpoint.schema.util.ObjectQueryUtil; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.util.LogfileTestTailer; -import com.evolveum.midpoint.test.util.MidPointAsserts; -import com.evolveum.midpoint.test.util.TestUtil; import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.midpoint.xml.ns._public.model.scripting_3.*; -import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType; + +import org.testng.collections.Sets; @ContextConfiguration(locations = { "classpath:ctx-model-intest-test-main.xml" }) @DirtiesContext(classMode = ClassMode.AFTER_CLASS) -public class TestScriptingBasic extends AbstractInitializedModelIntegrationTest { +public abstract class AbstractBasicScriptingTest extends AbstractInitializedModelIntegrationTest { public static final File TEST_DIR = new File("src/test/resources/scripting"); private static final File SYSTEM_CONFIGURATION_FILE = new File(TEST_DIR, "system-configuration.xml"); - private static final File LOG_FILE = new File(TEST_DIR, "log.xml"); + private static final ItemName USER_NAME_TASK_EXTENSION_PROPERTY = new ItemName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "userName"); + private static final ItemName USER_DESCRIPTION_TASK_EXTENSION_PROPERTY = new ItemName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "userDescription"); + private static final ItemName STUDY_GROUP_TASK_EXTENSION_PROPERTY = new ItemName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "studyGroup"); + + // Tests 1xx + private static final String ECHO = "echo"; + private static final String LOG = "log"; + + // Tests 2xx: No "legacy" and "new" versions for these private static final File SEARCH_FOR_USERS_FILE = new File(TEST_DIR, "search-for-users.xml"); private static final File SEARCH_FOR_USERS_WITH_EXPRESSIONS_FILE = new File(TEST_DIR, "search-for-users-with-expressions.xml"); - private static final File SEARCH_FOR_USERS_RESOLVE_NAMES_FOR_ROLE_MEMBERSHIP_REF_FILE = new File(TEST_DIR, "search-for-users-resolve-names-for-roleMembershipRef.xml"); - private static final File SEARCH_FOR_USERS_RESOLVE_ROLE_MEMBERSHIP_REF_FILE = new File(TEST_DIR, "search-for-users-resolve-roleMembershipRef.xml"); private static final File SEARCH_FOR_SHADOWS_FILE = new File(TEST_DIR, "search-for-shadows.xml"); - private static final File SEARCH_FOR_SHADOWS_NOFETCH_FILE = new File(TEST_DIR, "search-for-shadows-nofetch.xml"); + private static final String SEARCH_FOR_SHADOWS_NOFETCH = "search-for-shadows-nofetch"; private static final File SEARCH_FOR_RESOURCES_FILE = new File(TEST_DIR, "search-for-resources.xml"); private static final File SEARCH_FOR_ROLES_FILE = new File(TEST_DIR, "search-for-roles.xml"); private static final File SEARCH_FOR_USERS_ACCOUNTS_FILE = new File(TEST_DIR, "search-for-users-accounts.xml"); private static final File SEARCH_FOR_USERS_ACCOUNTS_NOFETCH_FILE = new File(TEST_DIR, "search-for-users-accounts-nofetch.xml"); - private static final File DISABLE_JACK_FILE = new File(TEST_DIR, "disable-jack.xml"); - private static final File ENABLE_JACK_FILE = new File(TEST_DIR, "enable-jack.xml"); - private static final File DELETE_AND_ADD_JACK_FILE = new File(TEST_DIR, "delete-and-add-jack.xml"); - private static final File MODIFY_JACK_FILE = new File(TEST_DIR, "modify-jack.xml"); - private static final File MODIFY_JACK_BACK_FILE = new File(TEST_DIR, "modify-jack-back.xml"); - private static final File MODIFY_JACK_PASSWORD_FILE = new File(TEST_DIR, "modify-jack-password.xml"); - private static final File MODIFY_JACK_PASSWORD_TASK_FILE = new File(TEST_DIR, "modify-jack-password-task.xml"); - private static final String MODIFY_JACK_PASSWORD_TASK_OID = "9de76345-0f02-48de-86bf-e7a887cb374a"; - private static final File RECOMPUTE_JACK_FILE = new File(TEST_DIR, "recompute-jack.xml"); - private static final File ASSIGN_TO_JACK_FILE = new File(TEST_DIR, "assign-to-jack.xml"); - private static final File ASSIGN_TO_JACK_DRY_AND_RAW_FILE = new File(TEST_DIR, "assign-to-jack-dry-and-raw.xml"); - private static final File ASSIGN_TO_JACK_2_FILE = new File(TEST_DIR, "assign-to-jack-2.xml"); - private static final File UNASSIGN_FROM_WILL_FILE = new File(TEST_DIR, "unassign-from-will.xml"); - private static final File UNASSIGN_FROM_WILL_2_FILE = new File(TEST_DIR, "unassign-from-will-2.xml"); - private static final File UNASSIGN_FROM_WILL_3_FILE = new File(TEST_DIR, "unassign-from-will-3.xml"); - private static final File ASSIGN_TO_WILL_FILE = new File(TEST_DIR, "assign-to-will.xml"); - private static final File ASSIGN_TO_WILL_2_FILE = new File(TEST_DIR, "assign-to-will-2.xml"); - private static final File PURGE_DUMMY_BLACK_SCHEMA_FILE = new File(TEST_DIR, "purge-dummy-black-schema.xml"); - private static final File TEST_DUMMY_RESOURCE_FILE = new File(TEST_DIR, "test-dummy-resource.xml"); - private static final File NOTIFICATION_ABOUT_JACK_FILE = new File(TEST_DIR, "notification-about-jack.xml"); - private static final File NOTIFICATION_ABOUT_JACK_TYPE2_FILE = new File(TEST_DIR, "notification-about-jack-type2.xml"); - private static final File SCRIPTING_USERS_FILE = new File(TEST_DIR, "scripting-users.xml"); - private static final File SCRIPTING_USERS_IN_BACKGROUND_FILE = new File(TEST_DIR, "scripting-users-in-background.xml"); - private static final File SCRIPTING_USERS_IN_BACKGROUND_ASSIGN_FILE = new File(TEST_DIR, "scripting-users-in-background-assign.xml"); + private static final File SEARCH_FOR_USERS_RESOLVE_NAMES_FOR_ROLE_MEMBERSHIP_REF_FILE = new File(TEST_DIR, "search-for-users-resolve-names-for-roleMembershipRef.xml"); + private static final File SEARCH_FOR_USERS_RESOLVE_ROLE_MEMBERSHIP_REF_FILE = new File(TEST_DIR, "search-for-users-resolve-roleMembershipRef.xml"); + + // Tests 300-359 + private static final String DISABLE_JACK = "disable-jack"; + private static final String ENABLE_JACK = "enable-jack"; + private static final String DELETE_AND_ADD_JACK = "delete-and-add-jack"; + private static final String MODIFY_JACK = "modify-jack"; + private static final String MODIFY_JACK_BACK = "modify-jack-back"; + private static final String RECOMPUTE_JACK = "recompute-jack"; + + // Tests 360-399 + private static final String ASSIGN_CAPTAIN_AND_DUMMY_RED_TO_JACK = "assign-captain-and-dummy-red-to-jack"; + private static final String ASSIGN_TO_JACK_DRY_AND_RAW = "assign-to-jack-dry-and-raw"; + private static final String ASSIGN_NICE_PIRATE_BY_NAME_TO_JACK = "assign-nice-pirate-by-name-to-jack"; + + private static final String ASSIGN_PIRATE_MANAGER_TO_WILL = "assign-pirate-manager-to-will"; + private static final String UNASSIGN_PIRATE_DEFAULT_FROM_WILL = "unassign-pirate-default-from-will"; + private static final String UNASSIGN_PIRATE_MANAGER_AND_OWNER_FROM_WILL = "unassign-pirate-manager-and-owner-from-will"; + private static final String UNASSIGN_DUMMY_RESOURCE_FROM_WILL = "unassign-dummy-resource-from-will"; + private static final String ASSIGN_PIRATE_RELATION_CAPTAIN_TO_WILL = "assign-pirate-relation-captain-to-will"; + + // Tests 4xx + private static final String PURGE_DUMMY_BLACK_SCHEMA = "purge-dummy-black-schema"; + + private static final String TEST_DUMMY_RESOURCE = "test-dummy-resource"; + private static final String NOTIFICATION_ABOUT_JACK = "notification-about-jack"; + private static final String NOTIFICATION_ABOUT_JACK_TYPE2 = "notification-about-jack-type2"; + + // Tests 5xx + private static final String SCRIPTING_USERS = "scripting-users"; + private static final String SCRIPTING_USERS_IN_BACKGROUND = "scripting-users-in-background"; + private static final String SCRIPTING_USERS_IN_BACKGROUND_ASSIGN = "scripting-users-in-background-assign"; + + private static final String GENERATE_PASSWORDS = "generate-passwords"; + private static final String GENERATE_PASSWORDS_2 = "generate-passwords-2"; + private static final String GENERATE_PASSWORDS_3 = "generate-passwords-3"; + + private static final String USE_VARIABLES = "use-variables"; + private static final String START_TASKS_FROM_TEMPLATE = "start-tasks-from-template"; private static final File SCRIPTING_USERS_IN_BACKGROUND_TASK_FILE = new File(TEST_DIR, "scripting-users-in-background-task.xml"); - private static final File SCRIPTING_USERS_IN_BACKGROUND_ITERATIVE_TASK_FILE = new File(TEST_DIR, "scripting-users-in-background-iterative-task.xml"); - private static final File START_TASKS_FROM_TEMPLATE_FILE = new File(TEST_DIR, "start-tasks-from-template.xml"); - private static final File GENERATE_PASSWORDS_FILE = new File(TEST_DIR, "generate-passwords.xml"); - private static final File GENERATE_PASSWORDS_2_FILE = new File(TEST_DIR, "generate-passwords-2.xml"); - private static final File GENERATE_PASSWORDS_3_FILE = new File(TEST_DIR, "generate-passwords-3.xml"); - private static final File ECHO_FILE = new File(TEST_DIR, "echo.xml"); - private static final File USE_VARIABLES_FILE = new File(TEST_DIR, "use-variables.xml"); + + private static final String SCRIPTING_USERS_IN_BACKGROUND_ITERATIVE_TASK = "scripting-users-in-background-iterative-task"; + private static final File TASK_TO_RESUME_FILE = new File(TEST_DIR, "task-to-resume.xml"); private static final File TASK_TO_KEEP_SUSPENDED_FILE = new File(TEST_DIR, "task-to-keep-suspended.xml"); - private static final File RESUME_SUSPENDED_TASKS_FILE = new File(TEST_DIR, "resume-suspended-tasks.xml"); - private static final ItemName USER_NAME_TASK_EXTENSION_PROPERTY = new ItemName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "userName"); - private static final ItemName USER_DESCRIPTION_TASK_EXTENSION_PROPERTY = new ItemName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "userDescription"); - private static final ItemName STUDY_GROUP_TASK_EXTENSION_PROPERTY = new ItemName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "studyGroup"); + private static final String RESUME_SUSPENDED_TASKS = "resume-suspended-tasks"; + + // Tests 6xx + private static final String MODIFY_JACK_PASSWORD = "modify-jack-password"; + private static final String MODIFY_JACK_PASSWORD_TASK = "modify-jack-password-task"; + private static final String MODIFY_JACK_PASSWORD_TASK_OID = "9de76345-0f02-48de-86bf-e7a887cb374a"; private static final String PASSWORD_PLAINTEXT_FRAGMENT = "pass1234wor"; private static final String PASSWORD_PLAINTEXT_1 = "pass1234wor1"; private static final String PASSWORD_PLAINTEXT_2 = "pass1234wor2"; private static final String PASSWORD_PLAINTEXT_3 = "pass1234wor3"; - @Autowired - private ScriptingExpressionEvaluator scriptingExpressionEvaluator; + @Autowired ScriptingExpressionEvaluator evaluator; @Override public void initSystem(Task initTask, OperationResult initResult) @@ -132,9 +157,6 @@ public void initSystem(Task initTask, OperationResult initResult) super.initSystem(initTask, initResult); InternalMonitor.reset(); -// InternalMonitor.setTraceShadowFetchOperation(true); -// InternalMonitor.setTraceResourceSchemaOperations(true); - DebugUtil.setPrettyPrintBeansAs(PrismContext.LANG_YAML); } @@ -143,53 +165,62 @@ protected File getSystemConfigurationFile() { return SYSTEM_CONFIGURATION_FILE; } + private File getFile(String name) { + return new File(TEST_DIR, name + getSuffix() + ".xml"); + } + + abstract String getSuffix(); + @Test public void test100EmptySequence() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); + ExpressionSequenceType sequence = new ExpressionSequenceType(); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(sequence, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(sequence, task, result); - // THEN + then(); dumpOutput(output, result); assertNoOutputData(output); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); } @Test public void test110EmptyPipeline() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); + ExpressionPipelineType pipeline = new ExpressionPipelineType(); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(pipeline, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(pipeline, task, result); - // THEN + then(); dumpOutput(output, result); assertNoOutputData(output); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); } @Test public void test112Echo() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ExecuteScriptType executeScript = parseExecuteScript(ECHO_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); + ExecuteScriptType executeScript = parseExecuteScript(ECHO); + + when(); + ExecutionContext output = evaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); + PipelineData data = output.getFinalOutput(); assertEquals("Unexpected # of items in output", 4, data.getData().size()); @@ -198,70 +229,66 @@ public void test112Echo() throws Exception { @Test public void test120Log() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType logAction = parseScriptingExpression(LOG_FILE); + + ScriptingExpressionType logAction = parseScriptingExpression(LOG); LogfileTestTailer tailer = new LogfileTestTailer(LoggingConfigurationManager.AUDIT_LOGGER_NAME); tailer.tail(); tailer.setExpecteMessage("Custom message:"); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(logAction, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(logAction, task, result); - // THEN + then(); dumpOutput(output, result); assertNoOutputData(output); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); tailer.tail(); tailer.assertExpectedMessage(); } - private ScriptingExpressionType parseScriptingExpression(File file) throws IOException, SchemaException { - // we cannot specify explicit type parameter here, as the parsed files contain subtypes of ScriptingExpressionType - return prismContext.parserFor(file).parseRealValue(); - } - - private ExecuteScriptType parseExecuteScript(File file) throws IOException, SchemaException { - return prismContext.parserFor(file).parseRealValue(ExecuteScriptType.class); - } - @Test public void test200SearchUser() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); + + // no legacy/new versions here ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_USERS_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(2, output.getFinalOutput().getData().size()); } @Test public void test202SearchUserWithExpressions() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); + + // no legacy/new versions here ExecuteScriptType executeScript = prismContext.parserFor(SEARCH_FOR_USERS_WITH_EXPRESSIONS_FILE).parseRealValue(); VariablesMap variables = new VariablesMap(); variables.put("value1", "administrator", String.class); variables.put("value2", "jack", String.class); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(executeScript, variables, false, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(executeScript, variables, false, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(2, output.getFinalOutput().getData().size()); assertEquals(new HashSet<>(Arrays.asList("administrator", "jack")), output.getFinalOutput().getData().stream() @@ -271,242 +298,330 @@ public void test202SearchUserWithExpressions() throws Exception { @Test public void test205SearchForResources() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); + + // no legacy/new versions here ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_RESOURCES_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(13, output.getFinalOutput().getData().size()); } @Test public void test206SearchForRoles() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); + + // no legacy/new versions here ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_ROLES_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); } @Test public void test210SearchForShadows() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); + + // no legacy/new versions here ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_SHADOWS_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(5, output.getFinalOutput().getData().size()); assertAttributesFetched(output.getFinalOutput().getData()); } @Test public void test215SearchForShadowsNoFetch() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_SHADOWS_NOFETCH_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_SHADOWS_NOFETCH); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(5, output.getFinalOutput().getData().size()); assertAttributesNotFetched(output.getFinalOutput().getData()); } @Test public void test220SearchForUsersAccounts() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_USERS_ACCOUNTS_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(4, output.getFinalOutput().getData().size()); assertAttributesFetched(output.getFinalOutput().getData()); } @Test public void test225SearchForUsersAccountsNoFetch() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_USERS_ACCOUNTS_NOFETCH_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(4, output.getFinalOutput().getData().size()); assertAttributesNotFetched(output.getFinalOutput().getData()); } + @Test + public void test230SearchUserResolveNamesForRoleMembershipRef() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_USERS_RESOLVE_NAMES_FOR_ROLE_MEMBERSHIP_REF_FILE); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); + dumpOutput(output, result); + + assertSuccess(result); + assertEquals(2, output.getFinalOutput().getData().size()); + //assertEquals("administrator", ((PrismObject) output.getData().get(0)).asObjectable().getName().getOrig()); + + for (PipelineItem item : output.getFinalOutput().getData()) { + PrismAsserts.assertHasTargetName((PrismContainerValue) item.getValue(), UserType.F_ROLE_MEMBERSHIP_REF); + PrismAsserts.assertHasNoTargetName((PrismContainerValue) item.getValue(), UserType.F_LINK_REF); + } + } + + @Test + public void test240SearchUserResolveRoleMembershipRef() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_USERS_RESOLVE_ROLE_MEMBERSHIP_REF_FILE); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); + dumpOutput(output, result); + + assertSuccess(result); + assertEquals(2, output.getFinalOutput().getData().size()); + + for (PipelineItem item : output.getFinalOutput().getData()) { + PrismAsserts.assertHasObject((PrismContainerValue) item.getValue(), UserType.F_ROLE_MEMBERSHIP_REF); + PrismAsserts.assertHasNoObject((PrismContainerValue) item.getValue(), UserType.F_LINK_REF); + } + } + @Test public void test300DisableJack() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(DISABLE_JACK_FILE); + ScriptingExpressionType expression = parseScriptingExpression(DISABLE_JACK); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); assertEquals("Disabled user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertAdministrativeStatusDisabled(searchObjectByName(UserType.class, "jack")); } @Test public void test310EnableJack() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(ENABLE_JACK_FILE); + ScriptingExpressionType expression = parseScriptingExpression(ENABLE_JACK); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals("Enabled user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); assertAdministrativeStatusEnabled(searchObjectByName(UserType.class, "jack")); } @Test public void test320DeleteAndAddJack() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(DELETE_AND_ADD_JACK_FILE); + ScriptingExpressionType expression = parseScriptingExpression(DELETE_AND_ADD_JACK); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals("Deleted user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\nAdded user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); assertAdministrativeStatusEnabled(searchObjectByName(UserType.class, "jack")); } @Test public void test330ModifyJack() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(MODIFY_JACK_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(MODIFY_JACK); - // THEN + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals("Modified user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); - assertEquals("Nowhere", searchObjectByName(UserType.class, "jack").asObjectable().getLocality().getOrig()); + assertUserAfterByUsername(USER_JACK_USERNAME) + .assertLocality("Nowhere"); } @Test public void test340ModifyJackBack() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(MODIFY_JACK_BACK_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(MODIFY_JACK_BACK); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals("Modified user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); - assertEquals("Caribbean", searchObjectByName(UserType.class, "jack").asObjectable().getLocality().getOrig()); + assertUserAfterByUsername(USER_JACK_USERNAME) + .assertLocality("Caribbean"); } @Test public void test350RecomputeJack() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(RECOMPUTE_JACK_FILE); + ScriptingExpressionType expression = parseScriptingExpression(RECOMPUTE_JACK); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); + assertSuccess(result); assertEquals("Recomputed user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); } @Test - public void test360AssignToJack() throws Exception { - // GIVEN + public void test352RecomputeJackTriggerDirect() throws Exception { + throw new SkipException("Only in new scripting tests"); + } + + @Test + public void test353RecomputeJackTriggerOptimized() throws Exception { + throw new SkipException("Only in new scripting tests"); + } + + @Test + public void test360AssignCaptainAndDummyRedToJack() throws Exception { + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_TO_JACK_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_CAPTAIN_AND_DUMMY_RED_TO_JACK); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); //assertEquals("Recomputed user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); - PrismObject jack = getUser(USER_JACK_OID); - display("jack after assignments creation", jack); - assertAssignedAccount(jack, "10000000-0000-0000-0000-000000000104"); - assertAssignedRole(jack, "12345678-d34d-b33f-f00d-55555555cccc"); + assertUserAfterByUsername(USER_JACK_USERNAME) + .assignments() + .assertAssignments(2) + .by() + .targetOid(ROLE_CAPTAIN_OID) + .find() + .end() + .by() + .resourceOid(RESOURCE_DUMMY_RED_OID) + .find() + .end(); + } + + @Test + public void test361UnassignCaptainFromJack() throws Exception { + throw new SkipException("Only in new scripting tests"); + } + + @Test + public void test363AssignCaptainByNameToJack() throws Exception { + throw new SkipException("Only in new scripting tests"); + } + + @Test + public void test364UnassignAllFromJack() throws Exception { + throw new SkipException("Only in new scripting tests"); } /** @@ -517,11 +632,12 @@ public void test365AssignToJackDryAndRaw() throws Exception { given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_TO_JACK_DRY_AND_RAW_FILE); + + ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_TO_JACK_DRY_AND_RAW); when(); try { - scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + evaluator.evaluateExpression(expression, task, result); fail("unexpected success"); } catch (ScriptExecutionException e) { displayExpectedException(e); @@ -530,190 +646,248 @@ public void test365AssignToJackDryAndRaw() throws Exception { } @Test - public void test370AssignToJackInBackground() throws Exception { - // GIVEN + public void test370AssignNicePirateByNameToJackInBackground() throws Exception { + given(); OperationResult result = getTestOperationResult(); - ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_TO_JACK_2_FILE); - // WHEN + ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_NICE_PIRATE_BY_NAME_TO_JACK); + + when(); Task task = taskManager.createTaskInstance(); task.setOwner(getUser(USER_ADMINISTRATOR_OID)); - scriptingExpressionEvaluator.evaluateExpressionInBackground(expression, task, result); + evaluator.evaluateExpressionInBackground(expression, task, result); waitForTaskFinish(task.getOid(), false); task.refresh(result); - // THEN - display(task.getResult()); - TestUtil.assertSuccess(task.getResult()); - PrismObject jack = getUser(USER_JACK_OID); - display("jack after assignment creation", jack); - assertAssignedRole(jack, "12345678-d34d-b33f-f00d-555555556677"); + then(); + assertSuccess(task.getResult()); + assertUserAfterByUsername(USER_JACK_USERNAME) + .assignments() + .assertRole(ROLE_NICE_PIRATE_OID); } @Test - public void test390AssignToWill() throws Exception { - // GIVEN + public void test390AssignToWillRolePirateManager() throws Exception { + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_TO_WILL_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + assertUserBefore(USER_WILL_OID) + .assignments() + .assertAssignments(3); + + ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_PIRATE_MANAGER_TO_WILL); - // THEN + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); - PrismObject will = getUser(USER_WILL_OID); - display("will after assignments creation", will); - MidPointAsserts.assertAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, RelationTypes.MANAGER.getRelation()); + + assertSuccess(result); + assertUserAfter(USER_WILL_OID) + .assignments() + .assertAssignments(4) + .by() + .targetOid(ROLE_PIRATE_OID) + .targetRelation(SchemaConstants.ORG_MANAGER) + .find(); } @Test - public void test391UnassignFromWill() throws Exception { - // GIVEN + public void test391UnassignPirateDefaultFromWill() throws Exception { + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_FROM_WILL_FILE); + ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_PIRATE_DEFAULT_FROM_WILL); + + assertUserBefore(USER_WILL_OID) + .assignments() + .assertAssignments(4) + .by() + .targetOid(ROLE_PIRATE_OID) + .targetRelation(SchemaConstants.ORG_MANAGER) + .find() + .end() + .by() + .targetOid(ROLE_PIRATE_OID) + .targetRelation(SchemaConstants.ORG_DEFAULT) + .find() + .end() + .by() + .targetOid(ROLE_PIRATE_OID) + .targetRelation(SchemaConstants.ORG_OWNER) + .find() + .end() + .by() + .resourceOid(RESOURCE_DUMMY_OID) + .find(); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); - PrismObject will = getUser(USER_WILL_OID); - display("will after unassign assignment", will); - MidPointAsserts.assertNotAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, RelationTypes.MEMBER.getRelation()); - MidPointAsserts.assertAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, RelationTypes.MANAGER.getRelation()); - MidPointAsserts.assertAssignedResource(will, "10000000-0000-0000-0000-000000000004"); + + assertSuccess(result); + assertUserAfter(USER_WILL_OID) + .assignments() + .assertAssignments(3) + .by() + .targetOid(ROLE_PIRATE_OID) + .targetRelation(SchemaConstants.ORG_MANAGER) + .find() + .end() + .by() + .targetOid(ROLE_PIRATE_OID) + .targetRelation(SchemaConstants.ORG_OWNER) + .find() + .end() + .by() + .resourceOid(RESOURCE_DUMMY_OID) + .find(); } @Test - public void test392UnassignFromWill2() throws Exception { - // GIVEN + public void test392UnassignPirateManagerAndOwnerFromWill() throws Exception { + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_FROM_WILL_2_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_PIRATE_MANAGER_AND_OWNER_FROM_WILL); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); - PrismObject will = getUser(USER_WILL_OID); - display("will after unassign assignment", will); - MidPointAsserts.assertNotAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, RelationTypes.MANAGER.getRelation()); - MidPointAsserts.assertNotAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, RelationTypes.OWNER.getRelation()); - MidPointAsserts.assertAssignedResource(will, "10000000-0000-0000-0000-000000000004"); + + assertSuccess(result); + assertUserAfter(USER_WILL_OID) + .assignments() + .assertAssignments(1) + .by() + .resourceOid(RESOURCE_DUMMY_OID) + .find(); } @Test - public void test393UnassignFromWill3() throws Exception { - // GIVEN + public void test393UnassignDummyResourceFromWill() throws Exception { + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_FROM_WILL_3_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_DUMMY_RESOURCE_FROM_WILL); - // THEN + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); - PrismObject will = getUser(USER_WILL_OID); - display("will after unassign assignment", will); - MidPointAsserts.assertNotAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, RelationTypes.MEMBER.getRelation()); - MidPointAsserts.assertNotAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, RelationTypes.MANAGER.getRelation()); - MidPointAsserts.assertNotAssignedResource(will, "10000000-0000-0000-0000-000000000004"); + + assertSuccess(result); + assertUserAfter(USER_WILL_OID) + .assignments() + .assertAssignments(0); } @Test - public void test394AssignToWill2() throws Exception { - // GIVEN + public void test394AssignPirateRelationCaptainToWill() throws Exception { + given(); QName customRelation = new QName("http://midpoint.evolveum.com/xml/ns/samples/piracy", "captain"); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_TO_WILL_2_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_PIRATE_RELATION_CAPTAIN_TO_WILL); - // THEN + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); dumpOutput(output, result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); - result.computeStatus(); - TestUtil.assertSuccess(result); - PrismObject will = getUser(USER_WILL_OID); - display("will after assignments creation", will); - MidPointAsserts.assertAssigned(will, "12345678-d34d-b33f-f00d-555555556666", RoleType.COMPLEX_TYPE, customRelation); + + assertSuccess(result); + assertUserAfter(USER_WILL_OID) + .assignments() + .assertAssignments(1) + .by() + .targetOid(ROLE_PIRATE_OID) + .targetRelation(customRelation) + .find(); } @Test public void test400PurgeSchema() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(PURGE_DUMMY_BLACK_SCHEMA_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(PURGE_DUMMY_BLACK_SCHEMA); + + assertResourceBefore(RESOURCE_DUMMY_BLACK_OID) + .assertHasSchema(); - // THEN + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(1, output.getFinalOutput().getData().size()); - //AssertJUnit.assertNull("Schema is still present", dummy.getSchema()); - // actually, schema gets downloaded just after purging it assertEquals("Purged schema information from resource:10000000-0000-0000-0000-000000000305(Dummy Resource Black)\n", output.getConsoleOutput()); + + PrismObject resourceAfter = modelService.getObject(ResourceType.class, + RESOURCE_DUMMY_BLACK_OID, getOperationOptionsBuilder().noFetch().build(), task, result); + assertResource(resourceAfter, "after (no fetch)") + .display() + .assertHasNoSchema(); } @Test public void test410TestResource() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(TEST_DUMMY_RESOURCE_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(TEST_DUMMY_RESOURCE); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); ResourceType dummy = modelService.getObject(ResourceType.class, RESOURCE_DUMMY_OID, null, taskManager.createTaskInstance(), result).asObjectable(); display("dummy resource after test connection", dummy.asPrismObject()); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertEquals(1, output.getFinalOutput().getData().size()); assertEquals("Tested resource:10000000-0000-0000-0000-000000000004(Dummy Resource): SUCCESS\n", output.getConsoleOutput()); } @Test public void test420NotificationAboutJack() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(NOTIFICATION_ABOUT_JACK_FILE); + + ScriptingExpressionType expression = parseScriptingExpression(NOTIFICATION_ABOUT_JACK); prepareNotifications(); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); assertEquals("Produced 1 event(s)\n", output.getConsoleOutput()); @@ -726,19 +900,20 @@ public void test420NotificationAboutJack() throws Exception { @Test public void test430NotificationAboutJackType2() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(NOTIFICATION_ABOUT_JACK_TYPE2_FILE); + + ScriptingExpressionType expression = parseScriptingExpression(NOTIFICATION_ABOUT_JACK_TYPE2); prepareNotifications(); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); assertOutputData(output, 1, OperationResultStatus.SUCCESS); assertEquals("Produced 1 event(s)\n", output.getConsoleOutput()); @@ -756,18 +931,19 @@ public void test430NotificationAboutJackType2() throws Exception { @Test public void test500ScriptingUsers() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(SCRIPTING_USERS_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + ScriptingExpressionType expression = parseScriptingExpression(SCRIPTING_USERS); - // THEN + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); PipelineData data = output.getFinalOutput(); assertEquals("Unexpected # of items in output", 6, data.getData().size()); Set realOids = new HashSet<>(); @@ -786,13 +962,14 @@ public void test500ScriptingUsers() throws Exception { @Test public void test505ScriptingUsersInBackground() throws Exception { - // GIVEN + given(); Task task = getTestTask(); task.setOwner(getUser(USER_ADMINISTRATOR_OID)); OperationResult result = task.getResult(); - ExecuteScriptType exec = prismContext.parserFor(SCRIPTING_USERS_IN_BACKGROUND_FILE).parseRealValue(); - // WHEN + ExecuteScriptType exec = parseExecuteScript(SCRIPTING_USERS_IN_BACKGROUND); + + when(); task.setExtensionPropertyValue(SchemaConstants.SE_EXECUTE_SCRIPT, exec); task.getExtensionOrClone() @@ -812,7 +989,7 @@ public void test505ScriptingUsersInBackground() throws Exception { waitForTaskFinish(task.getOid(), false); task.refresh(result); - // THEN + then(); display(task.getResult()); TestUtil.assertSuccess(task.getResult()); PrismObject admin = getUser(USER_ADMINISTRATOR_OID); @@ -828,13 +1005,14 @@ public void test505ScriptingUsersInBackground() throws Exception { @Test public void test507ScriptingUsersInBackgroundAssign() throws Exception { - // GIVEN + given(); Task task = getTestTask(); task.setOwner(getUser(USER_ADMINISTRATOR_OID)); OperationResult result = task.getResult(); - ExecuteScriptType exec = prismContext.parserFor(SCRIPTING_USERS_IN_BACKGROUND_ASSIGN_FILE).parseRealValue(); - // WHEN + ExecuteScriptType exec = parseExecuteScript(SCRIPTING_USERS_IN_BACKGROUND_ASSIGN); + + when(); task.setExtensionPropertyValue(SchemaConstants.SE_EXECUTE_SCRIPT, exec); task.setHandlerUri(ModelPublicConstants.SCRIPT_EXECUTION_TASK_HANDLER_URI); @@ -848,7 +1026,7 @@ public void test507ScriptingUsersInBackgroundAssign() throws Exception { waitForTaskFinish(task.getOid(), false); task.refresh(result); - // THEN + then(); display(task.getResult()); TestUtil.assertSuccess(task.getResult()); PrismObject admin = getUser(USER_ADMINISTRATOR_OID); @@ -864,10 +1042,11 @@ public void test507ScriptingUsersInBackgroundAssign() throws Exception { @Test public void test510GeneratePasswords() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(GENERATE_PASSWORDS_FILE); + + ScriptingExpressionType expression = parseScriptingExpression(GENERATE_PASSWORDS); addObject(PASSWORD_POLICY_GLOBAL_FILE); @@ -878,13 +1057,13 @@ public void test510GeneratePasswords() throws Exception { .asItemDeltas(); modifySystemObjectInRepo(SecurityPolicyType.class, SECURITY_POLICY_OID, itemDeltas, result); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); + + assertSuccess(result); PipelineData data = output.getFinalOutput(); assertEquals("Unexpected # of items in output", 6, data.getData().size()); Set realOids = new HashSet<>(); @@ -903,18 +1082,19 @@ public void test510GeneratePasswords() throws Exception { @Test public void test520GeneratePasswordsFullInput() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ExecuteScriptType executeScript = parseExecuteScript(GENERATE_PASSWORDS_2_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); + ExecuteScriptType executeScript = parseExecuteScript(GENERATE_PASSWORDS_2); + + when(); + ExecutionContext output = evaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); - //TestUtil.assertSuccess(result); + + //assertSuccess(result); PipelineData data = output.getFinalOutput(); List items = data.getData(); assertEquals("Unexpected # of items in output", 4, items.size()); @@ -926,17 +1106,18 @@ public void test520GeneratePasswordsFullInput() throws Exception { @Test public void test530GeneratePasswordsReally() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ExecuteScriptType executeScript = parseExecuteScript(GENERATE_PASSWORDS_3_FILE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); + ExecuteScriptType executeScript = parseExecuteScript(GENERATE_PASSWORDS_3); - // THEN + when(); + ExecutionContext output = evaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); + + then(); dumpOutput(output, result); - result.computeStatus(); + PipelineData data = output.getFinalOutput(); List items = data.getData(); assertEquals("Unexpected # of items in output", 3, items.size()); @@ -963,57 +1144,12 @@ private void checkPassword(PipelineItem item, String userOid) assertEquals("Wrong password stored in repository", returnedClearValue, repoClearValue); } - @Test - public void test540SearchUserResolveNamesForRoleMembershipRef() throws Exception { - // GIVEN - Task task = getTestTask(); - OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_USERS_RESOLVE_NAMES_FOR_ROLE_MEMBERSHIP_REF_FILE); - - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); - - // THEN - dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); - assertEquals(2, output.getFinalOutput().getData().size()); - //assertEquals("administrator", ((PrismObject) output.getData().get(0)).asObjectable().getName().getOrig()); - - for (PipelineItem item : output.getFinalOutput().getData()) { - PrismAsserts.assertHasTargetName((PrismContainerValue) item.getValue(), UserType.F_ROLE_MEMBERSHIP_REF); - PrismAsserts.assertHasNoTargetName((PrismContainerValue) item.getValue(), UserType.F_LINK_REF); - } - } - - @Test - public void test545SearchUserResolveRoleMembershipRef() throws Exception { - // GIVEN - Task task = getTestTask(); - OperationResult result = task.getResult(); - ScriptingExpressionType expression = parseScriptingExpression(SEARCH_FOR_USERS_RESOLVE_ROLE_MEMBERSHIP_REF_FILE); - - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(expression, task, result); - - // THEN - dumpOutput(output, result); - result.computeStatus(); - TestUtil.assertSuccess(result); - assertEquals(2, output.getFinalOutput().getData().size()); - - for (PipelineItem item : output.getFinalOutput().getData()) { - PrismAsserts.assertHasObject((PrismContainerValue) item.getValue(), UserType.F_ROLE_MEMBERSHIP_REF); - PrismAsserts.assertHasNoObject((PrismContainerValue) item.getValue(), UserType.F_LINK_REF); - } - } - @Test public void test550UseVariables() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - ExecuteScriptType executeScript = parseExecuteScript(USE_VARIABLES_FILE); + ExecuteScriptType executeScript = parseExecuteScript(USE_VARIABLES); PrismContainer taskExtension = task.getOrCreateExtension(); taskExtension @@ -1023,12 +1159,12 @@ public void test550UseVariables() throws Exception { .findOrCreateProperty(STUDY_GROUP_TASK_EXTENSION_PROPERTY) .addRealValues("group1", "group2", "group3"); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(executeScript, VariablesMap.emptyMap(), false, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); + PipelineData data = output.getFinalOutput(); assertEquals("Unexpected # of items in output", 1, data.getData().size()); @@ -1038,19 +1174,20 @@ public void test550UseVariables() throws Exception { @Test public void test560StartTaskFromTemplate() throws Exception { - // GIVEN + given(); Task task = getTestTask(); task.setOwner(getUser(USER_ADMINISTRATOR_OID)); OperationResult result = task.getResult(); + repoAddObjectFromFile(SCRIPTING_USERS_IN_BACKGROUND_TASK_FILE, result); - ExecuteScriptType exec = prismContext.parserFor(START_TASKS_FROM_TEMPLATE_FILE).parseRealValue(); + ExecuteScriptType exec = parseExecuteScript(START_TASKS_FROM_TEMPLATE); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(exec, VariablesMap.emptyMap(), false, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(exec, VariablesMap.emptyMap(), false, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); + PipelineData data = output.getFinalOutput(); assertEquals("Unexpected # of items in output", 2, data.getData().size()); @@ -1084,15 +1221,16 @@ public void test560StartTaskFromTemplate() throws Exception { @Test public void test570IterativeScriptingTask() throws Exception { - // GIVEN + given(); Task task = getTestTask(); OperationResult result = task.getResult(); - String taskOid = repoAddObjectFromFile(SCRIPTING_USERS_IN_BACKGROUND_ITERATIVE_TASK_FILE, result).getOid(); - // WHEN + String taskOid = repoAddObjectFromFile(getFile(SCRIPTING_USERS_IN_BACKGROUND_ITERATIVE_TASK), result).getOid(); + + when(); waitForTaskFinish(taskOid, false); - // THEN + then(); PrismObject jack = getUser(USER_JACK_OID); PrismObject administrator = getUser(USER_ADMINISTRATOR_OID); display("jack", jack); @@ -1101,54 +1239,52 @@ public void test570IterativeScriptingTask() throws Exception { assertEquals("Wrong administrator description", "hello administrator", administrator.asObjectable().getDescription()); } - @Test(enabled = false) // probably obsolete + @Test public void test575ResumeTask() throws Exception { - // GIVEN + given(); Task task = getTestTask(); task.setOwner(getUser(USER_ADMINISTRATOR_OID)); OperationResult result = task.getResult(); + String taskToResumeOid = addObject(TASK_TO_RESUME_FILE); addObject(TASK_TO_KEEP_SUSPENDED_FILE); - PrismObject taskToResume = prismContext.parseObject(TASK_TO_RESUME_FILE); - //TODO deal with this - //taskToResume.asObjectable().getApprovalContext().setEndTimestamp(fromNow(createDuration(-1000L))); - addObject(taskToResume); - display("task to resume", taskToResume); - - ExecuteScriptType exec = prismContext.parserFor(RESUME_SUSPENDED_TASKS_FILE).parseRealValue(); + ExecuteScriptType exec = parseExecuteScript(RESUME_SUSPENDED_TASKS); - // WHEN - ExecutionContext output = scriptingExpressionEvaluator.evaluateExpression(exec, VariablesMap.emptyMap(), false, task, result); + when(); + ExecutionContext output = evaluator.evaluateExpression(exec, VariablesMap.emptyMap(), false, task, result); - // THEN + then(); dumpOutput(output, result); - result.computeStatus(); + // the task should be there assertEquals("Unexpected # of items in output", 1, output.getFinalOutput().getData().size()); - PrismObject taskAfter = getObject(TaskType.class, taskToResume.getOid()); + PrismObject taskAfter = getObject(TaskType.class, taskToResumeOid); assertNotSame("Task is still suspended", taskAfter.asObjectable().getExecutionStatus(), TaskExecutionStatusType.SUSPENDED); } - // MID-5359 + /** + * MID-5359 + */ @Test public void test600ModifyJackPasswordInBackground() throws Exception { - // GIVEN + given(); OperationResult result = getTestOperationResult(); - ScriptingExpressionType expression = parseScriptingExpression(MODIFY_JACK_PASSWORD_FILE); + + ScriptingExpressionType expression = parseScriptingExpression(MODIFY_JACK_PASSWORD); prepareNotifications(); dummyAuditService.clear(); - // WHEN + when(); Task task = taskManager.createTaskInstance(); task.setOwner(getUser(USER_ADMINISTRATOR_OID)); - scriptingExpressionEvaluator.evaluateExpressionInBackground(expression, task, result); + evaluator.evaluateExpressionInBackground(expression, task, result); waitForTaskFinish(task.getOid(), false); task.refresh(result); - // THEN + then(); display(task.getResult()); TestUtil.assertSuccess(task.getResult()); PrismObject jack = getUser(USER_JACK_OID); @@ -1163,10 +1299,12 @@ public void test600ModifyJackPasswordInBackground() throws Exception { displayDumpable("Audit", dummyAuditService); } - // MID-5359 + /** + * MID-5359 + */ @Test public void test610ModifyJackPasswordImportingTask() throws Exception { - // GIVEN + given(); Task opTask = getTestTask(); opTask.setOwner(getUser(USER_ADMINISTRATOR_OID)); OperationResult result = opTask.getResult(); @@ -1174,17 +1312,16 @@ public void test610ModifyJackPasswordImportingTask() throws Exception { prepareNotifications(); dummyAuditService.clear(); - // WHEN - FileInputStream stream = new FileInputStream(MODIFY_JACK_PASSWORD_TASK_FILE); + when(); + FileInputStream stream = new FileInputStream(getFile(MODIFY_JACK_PASSWORD_TASK)); modelService.importObjectsFromStream(stream, PrismContext.LANG_XML, null, opTask, result); stream.close(); - result.computeStatus(); assertSuccess(result); Task task = waitForTaskFinish(MODIFY_JACK_PASSWORD_TASK_OID, false); - // THEN + then(); display(task.getResult()); TestUtil.assertSuccess(task.getResult()); PrismObject jack = getUser(USER_JACK_OID); @@ -1199,10 +1336,12 @@ public void test610ModifyJackPasswordImportingTask() throws Exception { displayDumpable("Audit", dummyAuditService); } - // not using scripting as such, but related... MID-5359 + /** + * MID-5359 (not using scripting as such, but related) + */ @Test public void test620ModifyJackPasswordViaExecuteChangesAsynchronously() throws Exception { - // GIVEN + given(); Task opTask = getTestTask(); opTask.setOwner(getUser(USER_ADMINISTRATOR_OID)); OperationResult result = opTask.getResult(); @@ -1210,7 +1349,7 @@ public void test620ModifyJackPasswordViaExecuteChangesAsynchronously() throws Ex prepareNotifications(); dummyAuditService.clear(); - // WHEN + when(); ProtectedStringType password = new ProtectedStringType(); password.setClearValue(PASSWORD_PLAINTEXT_3); @@ -1220,12 +1359,11 @@ public void test620ModifyJackPasswordViaExecuteChangesAsynchronously() throws Ex .asObjectDelta(USER_JACK_OID); TaskType newTask = libraryMidpointFunctions.executeChangesAsynchronously(singleton(delta), null, null, opTask, result); - result.computeStatus(); assertSuccess(result); Task task = waitForTaskFinish(newTask.getOid(), false); - // THEN + then(); display(task.getResult()); TestUtil.assertSuccess(task.getResult()); PrismObject jack = getUser(USER_JACK_OID); @@ -1245,7 +1383,7 @@ private void assertNoOutputData(ExecutionContext output) { } @SuppressWarnings("SameParameterValue") - private void assertOutputData(ExecutionContext output, int size, OperationResultStatus status) { + void assertOutputData(ExecutionContext output, int size, OperationResultStatus status) { assertEquals("Wrong # of output items", size, output.getFinalOutput().getData().size()); for (PipelineItem item : output.getFinalOutput().getData()) { assertEquals("Wrong op result status", status, item.getResult().getStatus()); @@ -1274,7 +1412,7 @@ private void assertAttributesFetched(List data) { } } - private void dumpOutput(ExecutionContext output, OperationResult result) throws JAXBException, SchemaException { + void dumpOutput(ExecutionContext output, OperationResult result) throws JAXBException, SchemaException { displayDumpable("output", output.getFinalOutput()); displayValue("stdout", output.getConsoleOutput()); display(result); @@ -1284,4 +1422,21 @@ private void dumpOutput(ExecutionContext output, OperationResult result) throws } } + ScriptingExpressionType parseScriptingExpression(File file) throws IOException, SchemaException { + // we cannot specify explicit type parameter here, as the parsed files contain subtypes of ScriptingExpressionType + return prismContext.parserFor(file).parseRealValue(); + } + + private ScriptingExpressionType parseScriptingExpression(String name) throws IOException, SchemaException { + return parseScriptingExpression(getFile(name)); + } + + private ExecuteScriptType parseExecuteScript(File file) throws IOException, SchemaException { + return prismContext.parserFor(file).parseRealValue(ExecuteScriptType.class); + } + + private ExecuteScriptType parseExecuteScript(String name) throws IOException, SchemaException { + return parseExecuteScript(getFile(name)); + } + } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicLegacy.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicLegacy.java new file mode 100644 index 00000000000..cf3e5724dcb --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicLegacy.java @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.intest.scripting; + +/** + * Tests legacy ("dynamic") versions of scripting expressions. + */ +public class TestScriptingBasicLegacy extends AbstractBasicScriptingTest { + + @Override + String getSuffix() { + return "-legacy"; + } +} diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicNew.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicNew.java new file mode 100644 index 00000000000..e686af77459 --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/TestScriptingBasicNew.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.model.intest.scripting; + +import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.result.OperationResultStatus; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.model.scripting_3.ScriptingExpressionType; + +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.io.File; + +import static org.testng.AssertJUnit.assertEquals; + +/** + * Tests new ("static") versions of scripting expressions. + */ +@Listeners({ com.evolveum.midpoint.tools.testng.AlphabeticalMethodInterceptor.class }) +public class TestScriptingBasicNew extends AbstractBasicScriptingTest { + + private static final File RECOMPUTE_JACK_NEW_TRIGGER_DIRECT_FILE = new File(TEST_DIR, "recompute-jack-new-trigger-direct.xml"); + private static final File RECOMPUTE_JACK_NEW_TRIGGER_OPTIMIZED_FILE = new File(TEST_DIR, "recompute-jack-new-trigger-optimized.xml"); + + private static final File UNASSIGN_CAPTAIN_FROM_JACK_FILE = new File(TEST_DIR, "unassign-captain-from-jack.xml"); + private static final File ASSIGN_CAPTAIN_BY_NAME_TO_JACK_FILE = new File(TEST_DIR, "assign-captain-by-name-to-jack.xml"); + private static final File UNASSIGN_ALL_FROM_JACK_FILE = new File(TEST_DIR, "unassign-all-from-jack.xml"); + + @Override + String getSuffix() { + return ""; + } + + @Test + public void test352RecomputeJackTriggerDirect() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + + ScriptingExpressionType expression = parseScriptingExpression(RECOMPUTE_JACK_NEW_TRIGGER_DIRECT_FILE); + + when(); + evaluator.evaluateExpression(expression, task, result); + Thread.sleep(20); + evaluator.evaluateExpression(expression, task, result); + Thread.sleep(20); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); + dumpOutput(output, result); + assertOutputData(output, 1, OperationResultStatus.SUCCESS); + assertSuccess(result); + assertEquals("Triggered recompute of user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); + + assertUserAfter(USER_JACK_OID) + .triggers() + .assertTriggers(3); + } + + @Test + public void test353RecomputeJackTriggerOptimized() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + ScriptingExpressionType expression = parseScriptingExpression(RECOMPUTE_JACK_NEW_TRIGGER_OPTIMIZED_FILE); + + ObjectDelta delta = deltaFor(UserType.class) + .item(UserType.F_TRIGGER).replace() + .asObjectDelta(USER_JACK_OID); + executeChanges(delta, null, task, result); + + assertUserBefore(USER_JACK_OID) + .triggers() + .assertTriggers(0); + + when(); + evaluator.evaluateExpression(expression, task, result); + Thread.sleep(20); + evaluator.evaluateExpression(expression, task, result); + Thread.sleep(20); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); + dumpOutput(output, result); + assertOutputData(output, 1, OperationResultStatus.SUCCESS); + assertSuccess(result); + assertEquals("Skipped triggering recompute of user:c0c010c0-d34d-b33f-f00d-111111111111(jack) because a trigger was already present\n", output.getConsoleOutput()); + + assertUserAfter(USER_JACK_OID) + .triggers() + .assertTriggers(1); + } + + @Test + public void test361UnassignCaptainFromJack() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_CAPTAIN_FROM_JACK_FILE); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); + dumpOutput(output, result); + assertOutputData(output, 1, OperationResultStatus.SUCCESS); + + assertSuccess(result); + //assertEquals("Recomputed user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); + assertUserAfterByUsername(USER_JACK_USERNAME) + .assignments() + .single() + .assertResource(RESOURCE_DUMMY_RED_OID); + } + + @Test + public void test363AssignCaptainByNameToJack() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + ScriptingExpressionType expression = parseScriptingExpression(ASSIGN_CAPTAIN_BY_NAME_TO_JACK_FILE); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); + dumpOutput(output, result); + assertOutputData(output, 1, OperationResultStatus.SUCCESS); + + assertSuccess(result); + //assertEquals("Recomputed user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); + assertUserAfterByUsername(USER_JACK_USERNAME) + .assignments() + .assertAssignments(2) + .by() + .targetOid(ROLE_CAPTAIN_OID) + .find() + .end() + .by() + .resourceOid(RESOURCE_DUMMY_RED_OID) + .find() + .end(); + } + + @Test + public void test364UnassignAllFromJack() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = task.getResult(); + ScriptingExpressionType expression = parseScriptingExpression(UNASSIGN_ALL_FROM_JACK_FILE); + + when(); + ExecutionContext output = evaluator.evaluateExpression(expression, task, result); + + then(); + dumpOutput(output, result); + assertOutputData(output, 1, OperationResultStatus.SUCCESS); + + assertSuccess(result); + //assertEquals("Recomputed user:c0c010c0-d34d-b33f-f00d-111111111111(jack)\n", output.getConsoleOutput()); + assertUserAfterByUsername(USER_JACK_USERNAME) + .assignments() + .assertNone(); + } + +} diff --git a/model/model-intest/src/test/resources/scripting/assign-to-jack.xml b/model/model-intest/src/test/resources/scripting/assign-captain-and-dummy-red-to-jack-legacy.xml similarity index 100% rename from model/model-intest/src/test/resources/scripting/assign-to-jack.xml rename to model/model-intest/src/test/resources/scripting/assign-captain-and-dummy-red-to-jack-legacy.xml diff --git a/model/model-intest/src/test/resources/scripting/assign-captain-and-dummy-red-to-jack.xml b/model/model-intest/src/test/resources/scripting/assign-captain-and-dummy-red-to-jack.xml new file mode 100644 index 00000000000..f33f408a06e --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/assign-captain-and-dummy-red-to-jack.xml @@ -0,0 +1,22 @@ + + + + + c:UserType + + + c:name + jack + + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/assign-captain-by-name-to-jack.xml b/model/model-intest/src/test/resources/scripting/assign-captain-by-name-to-jack.xml new file mode 100644 index 00000000000..ebc233fbc06 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/assign-captain-by-name-to-jack.xml @@ -0,0 +1,29 @@ + + + + + c:UserType + + + c:name + jack + + + + + + + name + Captain + + + + + diff --git a/model/model-intest/src/test/resources/scripting/assign-to-jack-2.xml b/model/model-intest/src/test/resources/scripting/assign-nice-pirate-by-name-to-jack-legacy.xml similarity index 100% rename from model/model-intest/src/test/resources/scripting/assign-to-jack-2.xml rename to model/model-intest/src/test/resources/scripting/assign-nice-pirate-by-name-to-jack-legacy.xml diff --git a/model/model-intest/src/test/resources/scripting/assign-nice-pirate-by-name-to-jack.xml b/model/model-intest/src/test/resources/scripting/assign-nice-pirate-by-name-to-jack.xml new file mode 100644 index 00000000000..a192a1dc5be --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/assign-nice-pirate-by-name-to-jack.xml @@ -0,0 +1,28 @@ + + + + UserType + + + name + jack + + + + + + + name + Nice Pirate + + + + + diff --git a/model/model-intest/src/test/resources/scripting/assign-to-will.xml b/model/model-intest/src/test/resources/scripting/assign-pirate-manager-to-will-legacy.xml similarity index 100% rename from model/model-intest/src/test/resources/scripting/assign-to-will.xml rename to model/model-intest/src/test/resources/scripting/assign-pirate-manager-to-will-legacy.xml diff --git a/model/model-intest/src/test/resources/scripting/assign-pirate-manager-to-will.xml b/model/model-intest/src/test/resources/scripting/assign-pirate-manager-to-will.xml new file mode 100644 index 00000000000..abaabdae2f9 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/assign-pirate-manager-to-will.xml @@ -0,0 +1,21 @@ + + + + + UserType + + + name + willTurner + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/assign-to-will-2.xml b/model/model-intest/src/test/resources/scripting/assign-pirate-relation-captain-to-will-legacy.xml similarity index 100% rename from model/model-intest/src/test/resources/scripting/assign-to-will-2.xml rename to model/model-intest/src/test/resources/scripting/assign-pirate-relation-captain-to-will-legacy.xml diff --git a/model/model-intest/src/test/resources/scripting/assign-pirate-relation-captain-to-will.xml b/model/model-intest/src/test/resources/scripting/assign-pirate-relation-captain-to-will.xml new file mode 100644 index 00000000000..3312f2c5073 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/assign-pirate-relation-captain-to-will.xml @@ -0,0 +1,22 @@ + + + + + UserType + + + name + willTurner + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw-legacy.xml b/model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw-legacy.xml new file mode 100644 index 00000000000..e0b5b5aba1c --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw-legacy.xml @@ -0,0 +1,38 @@ + + + + + c:UserType + + + c:name + jack + + + + assign + + role + 12345678-d34d-b33f-f00d-55555555cccc + + + + dryRun + true + + + options + + true + + + + diff --git a/model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw.xml b/model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw.xml index 5a545c394df..8b3326eedf8 100644 --- a/model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw.xml +++ b/model/model-intest/src/test/resources/scripting/assign-to-jack-dry-and-raw.xml @@ -5,11 +5,8 @@ ~ This work is dual-licensed under the Apache License 2.0 ~ and European Union Public License. See LICENSE file for details. --> - + xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"> c:UserType @@ -17,21 +14,12 @@ jack - - assign - - role - 12345678-d34d-b33f-f00d-55555555cccc - - - dryRun - true - - - options - - true - - - + + + + true + + true + + diff --git a/model/model-intest/src/test/resources/scripting/delete-and-add-jack-legacy.xml b/model/model-intest/src/test/resources/scripting/delete-and-add-jack-legacy.xml new file mode 100644 index 00000000000..311818a4d0e --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/delete-and-add-jack-legacy.xml @@ -0,0 +1,27 @@ + + + + + c:UserType + + + c:name + jack + + + + + delete + + + add + + + diff --git a/model/model-intest/src/test/resources/scripting/delete-and-add-jack.xml b/model/model-intest/src/test/resources/scripting/delete-and-add-jack.xml index 0dc50da0dc7..ee817ed2657 100644 --- a/model/model-intest/src/test/resources/scripting/delete-and-add-jack.xml +++ b/model/model-intest/src/test/resources/scripting/delete-and-add-jack.xml @@ -7,21 +7,16 @@ --> - c:UserType + xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"> + UserType - - c:name - jack - + + name + jack + - - delete - - - add - + + diff --git a/model/model-intest/src/test/resources/scripting/disable-jack-legacy.xml b/model/model-intest/src/test/resources/scripting/disable-jack-legacy.xml new file mode 100644 index 00000000000..fe0ea91f0d4 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/disable-jack-legacy.xml @@ -0,0 +1,24 @@ + + + + + + c:UserType + + + c:name + jack + + + + + disable + + diff --git a/model/model-intest/src/test/resources/scripting/disable-jack.xml b/model/model-intest/src/test/resources/scripting/disable-jack.xml index 245a3fcfec4..cf1a0c34482 100644 --- a/model/model-intest/src/test/resources/scripting/disable-jack.xml +++ b/model/model-intest/src/test/resources/scripting/disable-jack.xml @@ -7,18 +7,15 @@ --> - - c:UserType + xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"> + + UserType - - c:name - jack - + + name + jack + - - - disable - + + diff --git a/model/model-intest/src/test/resources/scripting/echo-legacy.xml b/model/model-intest/src/test/resources/scripting/echo-legacy.xml new file mode 100644 index 00000000000..9b6ec4132f9 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/echo-legacy.xml @@ -0,0 +1,26 @@ + + + + + + + + s1 + 12345678 + + + Jozef + + + + true + + diff --git a/model/model-intest/src/test/resources/scripting/enable-jack-legacy.xml b/model/model-intest/src/test/resources/scripting/enable-jack-legacy.xml new file mode 100644 index 00000000000..3e9e019efd1 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/enable-jack-legacy.xml @@ -0,0 +1,21 @@ + + + + + c:UserType + + + c:name + jack + + + + enable + + diff --git a/model/model-intest/src/test/resources/scripting/enable-jack.xml b/model/model-intest/src/test/resources/scripting/enable-jack.xml index f05d894f5b2..25f5d93b6f1 100644 --- a/model/model-intest/src/test/resources/scripting/enable-jack.xml +++ b/model/model-intest/src/test/resources/scripting/enable-jack.xml @@ -15,7 +15,5 @@ jack - - enable - + diff --git a/model/model-intest/src/test/resources/scripting/generate-passwords-2-legacy.xml b/model/model-intest/src/test/resources/scripting/generate-passwords-2-legacy.xml new file mode 100644 index 00000000000..6487f557c4b --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/generate-passwords-2-legacy.xml @@ -0,0 +1,33 @@ + + + + + + + resolve + + + generate-value + + + name + credentials/password/value + + + + + + + + + + true + + diff --git a/model/model-intest/src/test/resources/scripting/generate-passwords-2.xml b/model/model-intest/src/test/resources/scripting/generate-passwords-2.xml index 6487f557c4b..d1011ebb0ce 100644 --- a/model/model-intest/src/test/resources/scripting/generate-passwords-2.xml +++ b/model/model-intest/src/test/resources/scripting/generate-passwords-2.xml @@ -1,6 +1,6 @@ - - - generate-value - + + name credentials/password/value diff --git a/model/model-intest/src/test/resources/scripting/generate-passwords-3-legacy.xml b/model/model-intest/src/test/resources/scripting/generate-passwords-3-legacy.xml new file mode 100644 index 00000000000..e9c1de5d71f --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/generate-passwords-3-legacy.xml @@ -0,0 +1,44 @@ + + + + + + + resolve + + + generate-value + + items + + + + credentials/password/value + + true + + + + + + name + credentials/password/value + + + + + + + + + true + + diff --git a/model/model-intest/src/test/resources/scripting/generate-passwords-3.xml b/model/model-intest/src/test/resources/scripting/generate-passwords-3.xml index 1e5ed092ecb..38cb19b4771 100644 --- a/model/model-intest/src/test/resources/scripting/generate-passwords-3.xml +++ b/model/model-intest/src/test/resources/scripting/generate-passwords-3.xml @@ -11,23 +11,17 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:api="http://midpoint.evolveum.com/xml/ns/public/common/api-types-3"> - - resolve - - - generate-value - - items - - - - credentials/password/value - - true - - - - + + + + + + credentials/password/value + + true + + + name credentials/password/value diff --git a/model/model-intest/src/test/resources/scripting/generate-passwords-legacy.xml b/model/model-intest/src/test/resources/scripting/generate-passwords-legacy.xml new file mode 100644 index 00000000000..f560ed384df --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/generate-passwords-legacy.xml @@ -0,0 +1,21 @@ + + + + + + c:UserType + + + generate-value + + + name + credentials/password/value + + diff --git a/model/model-intest/src/test/resources/scripting/generate-passwords.xml b/model/model-intest/src/test/resources/scripting/generate-passwords.xml index b90f3125fab..964b9bf26c4 100644 --- a/model/model-intest/src/test/resources/scripting/generate-passwords.xml +++ b/model/model-intest/src/test/resources/scripting/generate-passwords.xml @@ -11,9 +11,7 @@ c:UserType - - generate-value - + name credentials/password/value diff --git a/model/model-intest/src/test/resources/scripting/log-legacy.xml b/model/model-intest/src/test/resources/scripting/log-legacy.xml new file mode 100644 index 00000000000..58afa845fee --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/log-legacy.xml @@ -0,0 +1,18 @@ + + + + + log + + message + Custom message: + + diff --git a/model/model-intest/src/test/resources/scripting/log.xml b/model/model-intest/src/test/resources/scripting/log.xml index deae1629974..21cad803b14 100644 --- a/model/model-intest/src/test/resources/scripting/log.xml +++ b/model/model-intest/src/test/resources/scripting/log.xml @@ -6,13 +6,6 @@ ~ and European Union Public License. See LICENSE file for details. --> - - log - - message - Custom message: - - + + Custom message: + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack-back-legacy.xml b/model/model-intest/src/test/resources/scripting/modify-jack-back-legacy.xml new file mode 100644 index 00000000000..09a035114f9 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/modify-jack-back-legacy.xml @@ -0,0 +1,36 @@ + + + + + + c:UserType + + + c:name + jack + + + + + modify + + delta + + + + replace + c:locality + Caribbean + + + + + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack-back.xml b/model/model-intest/src/test/resources/scripting/modify-jack-back.xml index 6192e188258..56df944630d 100644 --- a/model/model-intest/src/test/resources/scripting/modify-jack-back.xml +++ b/model/model-intest/src/test/resources/scripting/modify-jack-back.xml @@ -7,30 +7,26 @@ --> - c:UserType + UserType - - c:name - jack - + + name + jack + - - - modify - - delta - + + replace - c:locality + locality Caribbean - - + + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack-legacy.xml b/model/model-intest/src/test/resources/scripting/modify-jack-legacy.xml new file mode 100644 index 00000000000..4187922b022 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/modify-jack-legacy.xml @@ -0,0 +1,35 @@ + + + + + + c:UserType + + + c:name + jack + + + + + modify + + delta + + + replace + c:locality + Nowhere + + + + + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack-password-legacy.xml b/model/model-intest/src/test/resources/scripting/modify-jack-password-legacy.xml new file mode 100644 index 00000000000..79c1c52a937 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/modify-jack-password-legacy.xml @@ -0,0 +1,37 @@ + + + + + + c:UserType + + + c:name + jack + + + + + modify + + delta + + + replace + credentials/password/value + + pass1234wor1 + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack-password-task-legacy.xml b/model/model-intest/src/test/resources/scripting/modify-jack-password-task-legacy.xml new file mode 100644 index 00000000000..4067112a4f5 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/modify-jack-password-task-legacy.xml @@ -0,0 +1,55 @@ + + + + Task 1555581798624-0-1 + + + + + c:UserType + + + c:name + jack + + + + + modify + + delta + + modify + UserType + + replace + credentials/password/value + + pass1234wor2 + + + + + + + + + 1555581798624-0-1 + + + + runnable + BulkActions + http://midpoint.evolveum.com/xml/ns/public/model/scripting/handler-3 + single + tight + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack-password-task.xml b/model/model-intest/src/test/resources/scripting/modify-jack-password-task.xml index 4067112a4f5..6079fad0381 100644 --- a/model/model-intest/src/test/resources/scripting/modify-jack-password-task.xml +++ b/model/model-intest/src/test/resources/scripting/modify-jack-password-task.xml @@ -23,23 +23,19 @@ - - modify - - delta - - modify - UserType - - replace - credentials/password/value - - pass1234wor2 - - - - - + + + modify + UserType + + replace + credentials/password/value + + pass1234wor2 + + + + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack-password.xml b/model/model-intest/src/test/resources/scripting/modify-jack-password.xml index 79c1c52a937..364d1072e80 100644 --- a/model/model-intest/src/test/resources/scripting/modify-jack-password.xml +++ b/model/model-intest/src/test/resources/scripting/modify-jack-password.xml @@ -19,19 +19,15 @@ - - modify - - delta - - - replace - credentials/password/value - - pass1234wor1 - - - - - + + + + replace + credentials/password/value + + pass1234wor1 + + + + diff --git a/model/model-intest/src/test/resources/scripting/modify-jack.xml b/model/model-intest/src/test/resources/scripting/modify-jack.xml index de93142a6c7..2f3859e0754 100644 --- a/model/model-intest/src/test/resources/scripting/modify-jack.xml +++ b/model/model-intest/src/test/resources/scripting/modify-jack.xml @@ -6,30 +6,23 @@ ~ and European Union Public License. See LICENSE file for details. --> - - - c:UserType - - - c:name - jack - - - - - modify - - delta - - - replace - c:locality - Nowhere - - - - - + + UserType + + + name + jack + + + + + + replace + locality + Nowhere + + + + diff --git a/model/model-intest/src/test/resources/scripting/notification-about-jack-legacy.xml b/model/model-intest/src/test/resources/scripting/notification-about-jack-legacy.xml new file mode 100644 index 00000000000..7207da9b0a6 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/notification-about-jack-legacy.xml @@ -0,0 +1,42 @@ + + + + + c:UserType + + + c:name + jack + + + + notify + + handler + + + + recipient@evolveum.com + + + Ad hoc notification + + + + + dummy:Custom + + + + + diff --git a/model/model-intest/src/test/resources/scripting/notification-about-jack-type2-legacy.xml b/model/model-intest/src/test/resources/scripting/notification-about-jack-type2-legacy.xml new file mode 100644 index 00000000000..577d38f0628 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/notification-about-jack-type2-legacy.xml @@ -0,0 +1,58 @@ + + + + + c:UserType + + + c:name + jack + + + + notify + + subtype + type2 + + + operation + delete + + + status + failure + + + forWholeInput + true + + + handler + + + + recipient@evolveum.com + + + Ad hoc notification 2 + + + + + dummy:Custom + + + + + diff --git a/model/model-intest/src/test/resources/scripting/notification-about-jack-type2.xml b/model/model-intest/src/test/resources/scripting/notification-about-jack-type2.xml index b2b457af8dc..6680a708b37 100644 --- a/model/model-intest/src/test/resources/scripting/notification-about-jack-type2.xml +++ b/model/model-intest/src/test/resources/scripting/notification-about-jack-type2.xml @@ -7,8 +7,8 @@ --> + xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3" + xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"> c:UserType @@ -16,43 +16,27 @@ jack - - notify - - subtype - type2 - - - operation - delete - - - status - failure - - - forWholeInput - true - - - handler - - - - recipient@evolveum.com - - - Ad hoc notification 2 - - - - - dummy:Custom - - - - + + type2 + failure + delete + true + + + + recipient@evolveum.com + + + Ad hoc notification 2 + + + + + dummy:Custom + + + diff --git a/model/model-intest/src/test/resources/scripting/notification-about-jack.xml b/model/model-intest/src/test/resources/scripting/notification-about-jack.xml index f77bc7d4060..a9c1ca17b4f 100644 --- a/model/model-intest/src/test/resources/scripting/notification-about-jack.xml +++ b/model/model-intest/src/test/resources/scripting/notification-about-jack.xml @@ -6,37 +6,32 @@ ~ and European Union Public License. See LICENSE file for details. --> - - c:UserType + + UserType - c:name + name jack - - notify - - handler - - - - recipient@evolveum.com - - - Ad hoc notification - - - - - dummy:Custom - - - - + + + + + recipient@evolveum.com + + + Ad hoc notification + + + + + dummy:Custom + + + diff --git a/model/model-intest/src/test/resources/scripting/purge-dummy-black-schema-legacy.xml b/model/model-intest/src/test/resources/scripting/purge-dummy-black-schema-legacy.xml new file mode 100644 index 00000000000..1959d70c22a --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/purge-dummy-black-schema-legacy.xml @@ -0,0 +1,21 @@ + + + + + c:ResourceType + + + c:name + Dummy Resource Black + + + + purge-schema + + diff --git a/model/model-intest/src/test/resources/scripting/purge-dummy-black-schema.xml b/model/model-intest/src/test/resources/scripting/purge-dummy-black-schema.xml index c5cef055f6e..45f9e61f4b6 100644 --- a/model/model-intest/src/test/resources/scripting/purge-dummy-black-schema.xml +++ b/model/model-intest/src/test/resources/scripting/purge-dummy-black-schema.xml @@ -6,16 +6,13 @@ ~ and European Union Public License. See LICENSE file for details. --> - - c:ResourceType + + ResourceType - c:name + name Dummy Resource Black - - purge-schema - + diff --git a/model/model-intest/src/test/resources/scripting/recompute-jack-legacy.xml b/model/model-intest/src/test/resources/scripting/recompute-jack-legacy.xml new file mode 100644 index 00000000000..491279fe82a --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/recompute-jack-legacy.xml @@ -0,0 +1,21 @@ + + + + + c:UserType + + + c:name + jack + + + + recompute + + diff --git a/model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-direct.xml b/model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-direct.xml new file mode 100644 index 00000000000..57fe5a90fd5 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-direct.xml @@ -0,0 +1,22 @@ + + + + + UserType + + + + name + jack + + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-optimized.xml b/model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-optimized.xml new file mode 100644 index 00000000000..bbbd05a20f0 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/recompute-jack-new-trigger-optimized.xml @@ -0,0 +1,24 @@ + + + + + UserType + + + + name + jack + + + + + + PT5M + + + diff --git a/model/model-intest/src/test/resources/scripting/recompute-jack.xml b/model/model-intest/src/test/resources/scripting/recompute-jack.xml index 6e58636662c..f6a38a9c56a 100644 --- a/model/model-intest/src/test/resources/scripting/recompute-jack.xml +++ b/model/model-intest/src/test/resources/scripting/recompute-jack.xml @@ -6,16 +6,15 @@ ~ and European Union Public License. See LICENSE file for details. --> - - c:UserType - - - c:name - jack - - - - recompute - + + UserType + + + + name + jack + + + + diff --git a/model/model-intest/src/test/resources/scripting/resume-suspended-tasks-legacy.xml b/model/model-intest/src/test/resources/scripting/resume-suspended-tasks-legacy.xml new file mode 100644 index 00000000000..74adc3decf7 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/resume-suspended-tasks-legacy.xml @@ -0,0 +1,25 @@ + + + + + TaskType + + + + name + Task to resume + + + + + resume + + + diff --git a/model/model-intest/src/test/resources/scripting/resume-suspended-tasks.xml b/model/model-intest/src/test/resources/scripting/resume-suspended-tasks.xml index f35941db8f0..9a0484b1948 100644 --- a/model/model-intest/src/test/resources/scripting/resume-suspended-tasks.xml +++ b/model/model-intest/src/test/resources/scripting/resume-suspended-tasks.xml @@ -7,31 +7,17 @@ --> TaskType - - - category - Workflow - - - handlerUri - - http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3 - - - executionStatus - suspended - - + + name + Task to resume + - - resume - + diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign-legacy.xml b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign-legacy.xml new file mode 100644 index 00000000000..4421471d64a --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign-legacy.xml @@ -0,0 +1,61 @@ + + + + + + + c:UserType + + + + name + administrator + + + + + + execute-script + + script + + + import com.evolveum.midpoint.schema.constants.* + import com.evolveum.midpoint.xml.ns._public.common.common_3.* + import com.evolveum.midpoint.model.api.* + def roleToAssignOid = '12345111-1111-2222-1111-121212111112' // role-empty + + log.info('### {} - {}', input.name, input.fullName) + + assignmentsToAdd = [] + newAssignment = new AssignmentType() + targetRef = new ObjectReferenceType() + targetRef.setOid(roleToAssignOid) + targetRef.setType(RoleType.COMPLEX_TYPE) + newAssignment.setTargetRef(targetRef) + assignmentsToAdd.add(newAssignment.asPrismContainerValue()) + + // Suspicious code, see MID-4234. It is working here almost by chance. + // E.g. if executed for user jack, it would fail because of SchemaException. + // More correct is to replace FocusType.class with input.class here. + def delta = midpoint.deltaFor(FocusType.class) + .item(FocusType.F_ASSIGNMENT).add(assignmentsToAdd) + .asObjectDelta(input.oid) + + log.info('### Going to execute delta = {}', delta.debugDump()) + midpoint.modifyObject(delta) + log.info('### Finished: {} - {}', input.getName(), input?.getFullName()) + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign.xml b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign.xml index cf1d9b73505..f666d163685 100644 --- a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign.xml +++ b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-assign.xml @@ -8,8 +8,8 @@ + xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3" + xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"> c:UserType @@ -22,40 +22,36 @@ - - execute-script - - script - - - import com.evolveum.midpoint.schema.constants.* - import com.evolveum.midpoint.xml.ns._public.common.common_3.* - import com.evolveum.midpoint.model.api.* - def roleToAssignOid = '12345111-1111-2222-1111-121212111112' // role-empty + + + + import com.evolveum.midpoint.schema.constants.* + import com.evolveum.midpoint.xml.ns._public.common.common_3.* + import com.evolveum.midpoint.model.api.* + def roleToAssignOid = '12345111-1111-2222-1111-121212111112' // role-empty - log.info('### {} - {}', input.name, input.fullName) + log.info('### {} - {}', input.name, input.fullName) - assignmentsToAdd = [] - newAssignment = new AssignmentType() - targetRef = new ObjectReferenceType() - targetRef.setOid(roleToAssignOid) - targetRef.setType(RoleType.COMPLEX_TYPE) - newAssignment.setTargetRef(targetRef) - assignmentsToAdd.add(newAssignment.asPrismContainerValue()) + assignmentsToAdd = [] + newAssignment = new AssignmentType() + targetRef = new ObjectReferenceType() + targetRef.setOid(roleToAssignOid) + targetRef.setType(RoleType.COMPLEX_TYPE) + newAssignment.setTargetRef(targetRef) + assignmentsToAdd.add(newAssignment.asPrismContainerValue()) - // Suspicious code, see MID-4234. It is working here almost by chance. - // E.g. if executed for user jack, it would fail because of SchemaException. - // More correct is to replace FocusType.class with input.class here. - def delta = midpoint.deltaFor(FocusType.class) - .item(FocusType.F_ASSIGNMENT).add(assignmentsToAdd) - .asObjectDelta(input.oid) + // Suspicious code, see MID-4234. It is working here almost by chance. + // E.g. if executed for user jack, it would fail because of SchemaException. + // More correct is to replace FocusType.class with input.class here. + def delta = midpoint.deltaFor(FocusType.class) + .item(FocusType.F_ASSIGNMENT).add(assignmentsToAdd) + .asObjectDelta(input.oid) - log.info('### Going to execute delta = {}', delta.debugDump()) - midpoint.modifyObject(delta) - log.info('### Finished: {} - {}', input.getName(), input?.getFullName()) - - - - + log.info('### Going to execute delta = {}', delta.debugDump()) + midpoint.modifyObject(delta) + log.info('### Finished: {} - {}', input.getName(), input?.getFullName()) + + + diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task-legacy.xml b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task-legacy.xml new file mode 100644 index 00000000000..5dbcbf0c579 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task-legacy.xml @@ -0,0 +1,44 @@ + + + + Change description task template (iterative) + + + + execute-script + + script + + + import com.evolveum.midpoint.xml.ns._public.common.common_3.* + + log.info('Modifying user {}', input) + def deltas = midpoint.deltaFor(UserType.class) + .item(UserType.F_DESCRIPTION) + .replace('hello ' + basic.stringify(input.name)) + .asObjectDeltas(input.oid) + log.info('Change:\n{}', deltas.iterator().next().debugDump()) + midpoint.executeChanges(deltas, null) + + + + + + UserType + + + runnable + BulkActions + http://midpoint.evolveum.com/xml/ns/public/model/iterative-scripting/handler-3 + single + diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task.xml b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task.xml index 5dbcbf0c579..0e94ec5662f 100644 --- a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task.xml +++ b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-iterative-task.xml @@ -6,33 +6,26 @@ ~ and European Union Public License. See LICENSE file for details. --> Change description task template (iterative) - - execute-script - - script - - - import com.evolveum.midpoint.xml.ns._public.common.common_3.* + + + + import com.evolveum.midpoint.xml.ns._public.common.common_3.* - log.info('Modifying user {}', input) - def deltas = midpoint.deltaFor(UserType.class) - .item(UserType.F_DESCRIPTION) - .replace('hello ' + basic.stringify(input.name)) - .asObjectDeltas(input.oid) - log.info('Change:\n{}', deltas.iterator().next().debugDump()) - midpoint.executeChanges(deltas, null) - - - - + log.info('Modifying user {}', input) + def deltas = midpoint.deltaFor(UserType.class) + .item(UserType.F_DESCRIPTION) + .replace('hello ' + basic.stringify(input.name)) + .asObjectDeltas(input.oid) + log.info('Change:\n{}', deltas.iterator().next().debugDump()) + midpoint.executeChanges(deltas, null) + + + UserType diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-legacy.xml b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-legacy.xml new file mode 100644 index 00000000000..bba7508724b --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-legacy.xml @@ -0,0 +1,61 @@ + + + + + + + c:UserType + + + + name + + $userName + + + + + + + execute-script + + script + + + import com.evolveum.midpoint.xml.ns._public.common.common_3.* + + log.info('Modifying user {}', input) + def deltas = midpoint.deltaFor(UserType.class) + .item(UserType.F_DESCRIPTION) + .replace(userDescription) + .asObjectDeltas(input.oid) + log.info('Change:\n{}', deltas.iterator().next().debugDump()) + midpoint.executeChanges(deltas, null) + + + + + + + + userName + + $task/extension/userName + + + + userDescription + + $task/extension/userDescription + + + + diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-task.xml b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-task.xml index f8e04737b80..c769d930078 100644 --- a/model/model-intest/src/test/resources/scripting/scripting-users-in-background-task.xml +++ b/model/model-intest/src/test/resources/scripting/scripting-users-in-background-task.xml @@ -28,25 +28,21 @@ - - execute-script - - script - - - import com.evolveum.midpoint.xml.ns._public.common.common_3.* + + + + import com.evolveum.midpoint.xml.ns._public.common.common_3.* - log.info('Modifying user {}', input) - def deltas = midpoint.deltaFor(UserType.class) - .item(UserType.F_DESCRIPTION) - .replace(userDescription) - .asObjectDeltas(input.oid) - log.info('Change:\n{}', deltas.iterator().next().debugDump()) - midpoint.executeChanges(deltas, null) - - - - + log.info('Modifying user {}', input) + def deltas = midpoint.deltaFor(UserType.class) + .item(UserType.F_DESCRIPTION) + .replace(userDescription) + .asObjectDeltas(input.oid) + log.info('Change:\n{}', deltas.iterator().next().debugDump()) + midpoint.executeChanges(deltas, null) + + + diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-in-background.xml b/model/model-intest/src/test/resources/scripting/scripting-users-in-background.xml index 0a5ea29cc2f..d92a1760fbd 100644 --- a/model/model-intest/src/test/resources/scripting/scripting-users-in-background.xml +++ b/model/model-intest/src/test/resources/scripting/scripting-users-in-background.xml @@ -7,54 +7,49 @@ --> + xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3" + xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"> - c:UserType + UserType name - - $userName - + + $userName + - - execute-script - - script - - - import com.evolveum.midpoint.xml.ns._public.common.common_3.* + + + + import com.evolveum.midpoint.xml.ns._public.common.common_3.* - log.info('Modifying user {}', input) - def deltas = midpoint.deltaFor(UserType.class) - .item(UserType.F_DESCRIPTION) - .replace(userDescription) - .asObjectDeltas(input.oid) - log.info('Change:\n{}', deltas.iterator().next().debugDump()) - midpoint.executeChanges(deltas, null) - - - - + log.info('Modifying user {}', input) + def deltas = midpoint.deltaFor(UserType.class) + .item(UserType.F_DESCRIPTION) + .replace(userDescription) + .asObjectDeltas(input.oid) + log.info('Change:\n{}', deltas.iterator().next().debugDump()) + midpoint.executeChanges(deltas, null) + + + userName - $task/extension/userName + $task/extension/userName userDescription - $task/extension/userDescription + $task/extension/userDescription diff --git a/model/model-intest/src/test/resources/scripting/scripting-users-legacy.xml b/model/model-intest/src/test/resources/scripting/scripting-users-legacy.xml new file mode 100644 index 00000000000..3fdd6accb4a --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/scripting-users-legacy.xml @@ -0,0 +1,32 @@ + + + + + + c:UserType + + + execute-script + + script + + + cloned = input.clone() + cloned.setDescription('Test') + cloned + + + + + outputItem + UserType + + + diff --git a/model/model-intest/src/test/resources/scripting/scripting-users.xml b/model/model-intest/src/test/resources/scripting/scripting-users.xml index b2122bd89d2..0281468cc1e 100644 --- a/model/model-intest/src/test/resources/scripting/scripting-users.xml +++ b/model/model-intest/src/test/resources/scripting/scripting-users.xml @@ -7,26 +7,18 @@ --> - - c:UserType - - - execute-script - - script - - - cloned = input.clone() - cloned.setDescription('Test') - cloned - - - - - outputItem - UserType - - + xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"> + + UserType + + + + + cloned = input.clone() + cloned.setDescription('Test') + cloned + + + UserType + diff --git a/model/model-intest/src/test/resources/scripting/search-for-shadows-nofetch-legacy.xml b/model/model-intest/src/test/resources/scripting/search-for-shadows-nofetch-legacy.xml new file mode 100644 index 00000000000..ec620d45401 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/search-for-shadows-nofetch-legacy.xml @@ -0,0 +1,32 @@ + + + + + c:ShadowType + + + + c:resourceRef + + + + c:objectClass + ri:AccountObjectClass + + + + + + + true + + + + diff --git a/model/model-intest/src/test/resources/scripting/start-tasks-from-template-legacy.xml b/model/model-intest/src/test/resources/scripting/start-tasks-from-template-legacy.xml new file mode 100644 index 00000000000..9111e13df43 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/start-tasks-from-template-legacy.xml @@ -0,0 +1,37 @@ + + + + + + execute-script + + script + + + import javax.xml.namespace.* + def newTask1 = midpoint.submitTaskFromTemplate('79dacdf7-2bc9-4eef-9c63-08d53d2392c3', + [(new QName('userName')):'jack', (new QName('userDescription')):'new desc jack']) + def newTask2 = midpoint.submitTaskFromTemplate('79dacdf7-2bc9-4eef-9c63-08d53d2392c3', + [(new QName('userName')):'administrator', (new QName('userDescription')):'new desc admin']) + return [newTask1, newTask2] + + + + + outputItem + http://midpoint.evolveum.com/xml/ns/public/common/common-3#task + + + forWholeInput + true + + + diff --git a/model/model-intest/src/test/resources/scripting/start-tasks-from-template.xml b/model/model-intest/src/test/resources/scripting/start-tasks-from-template.xml index 9111e13df43..dd1e42de3e6 100644 --- a/model/model-intest/src/test/resources/scripting/start-tasks-from-template.xml +++ b/model/model-intest/src/test/resources/scripting/start-tasks-from-template.xml @@ -8,30 +8,19 @@ - - execute-script - - script - - - import javax.xml.namespace.* - def newTask1 = midpoint.submitTaskFromTemplate('79dacdf7-2bc9-4eef-9c63-08d53d2392c3', - [(new QName('userName')):'jack', (new QName('userDescription')):'new desc jack']) - def newTask2 = midpoint.submitTaskFromTemplate('79dacdf7-2bc9-4eef-9c63-08d53d2392c3', - [(new QName('userName')):'administrator', (new QName('userDescription')):'new desc admin']) - return [newTask1, newTask2] - - - - - outputItem - http://midpoint.evolveum.com/xml/ns/public/common/common-3#task - - - forWholeInput - true - - + xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"> + + + + import javax.xml.namespace.* + def newTask1 = midpoint.submitTaskFromTemplate('79dacdf7-2bc9-4eef-9c63-08d53d2392c3', + [(new QName('userName')):'jack', (new QName('userDescription')):'new desc jack']) + def newTask2 = midpoint.submitTaskFromTemplate('79dacdf7-2bc9-4eef-9c63-08d53d2392c3', + [(new QName('userName')):'administrator', (new QName('userDescription')):'new desc admin']) + return [newTask1, newTask2] + + + true + task + diff --git a/model/model-intest/src/test/resources/scripting/task-to-keep-suspended.xml b/model/model-intest/src/test/resources/scripting/task-to-keep-suspended.xml index 31bdcd63aaa..3f81c4472da 100644 --- a/model/model-intest/src/test/resources/scripting/task-to-keep-suspended.xml +++ b/model/model-intest/src/test/resources/scripting/task-to-keep-suspended.xml @@ -11,22 +11,17 @@ xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3" oid="e6ad137e-23f6-444d-9478-c10a0b255b68" version="1"> - Approval and execution of: Assigning role "abc" to user "def" (old) + Task to keep suspended 2222222222222-0-1 http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user suspended - Workflow http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3 - partial_error - 2018-01-02T12:24:33.635+02:00 - 2018-01-02T12:25:46.587+02:00 + 2018-04-11T12:24:33.635+02:00 + 2018-04-11T12:25:46.587+02:00 single tight - - primary - diff --git a/model/model-intest/src/test/resources/scripting/task-to-resume.xml b/model/model-intest/src/test/resources/scripting/task-to-resume.xml index 261c109d2d1..ad2b0ef26b7 100644 --- a/model/model-intest/src/test/resources/scripting/task-to-resume.xml +++ b/model/model-intest/src/test/resources/scripting/task-to-resume.xml @@ -11,22 +11,17 @@ xmlns:org="http://midpoint.evolveum.com/xml/ns/public/common/org-3" oid="c77c458d-0719-4bef-aa8d-dbf0460cd77c" version="35"> - Approval and execution of: Assigning role "abc" to user "def" + Task to resume 1522830887681-0-1 http://midpoint.evolveum.com/xml/ns/public/gui/channels-3#user suspended - Workflow http://midpoint.evolveum.com/xml/ns/public/task/noop/handler-3 - partial_error 2018-04-11T12:24:33.635+02:00 2018-04-11T12:25:46.587+02:00 single tight - - primary - diff --git a/model/model-intest/src/test/resources/scripting/test-dummy-resource-legacy.xml b/model/model-intest/src/test/resources/scripting/test-dummy-resource-legacy.xml new file mode 100644 index 00000000000..9627548d1e8 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/test-dummy-resource-legacy.xml @@ -0,0 +1,21 @@ + + + + + c:ResourceType + + + c:name + Dummy Resource + + + + test-resource + + diff --git a/model/model-intest/src/test/resources/scripting/test-dummy-resource.xml b/model/model-intest/src/test/resources/scripting/test-dummy-resource.xml index 92dde3f9941..bd0950860c6 100644 --- a/model/model-intest/src/test/resources/scripting/test-dummy-resource.xml +++ b/model/model-intest/src/test/resources/scripting/test-dummy-resource.xml @@ -15,7 +15,5 @@ Dummy Resource - - test-resource - + diff --git a/model/model-intest/src/test/resources/scripting/unassign-all-from-jack.xml b/model/model-intest/src/test/resources/scripting/unassign-all-from-jack.xml new file mode 100644 index 00000000000..8de65d911b2 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/unassign-all-from-jack.xml @@ -0,0 +1,23 @@ + + + + + UserType + + + name + jack + + + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/unassign-captain-from-jack.xml b/model/model-intest/src/test/resources/scripting/unassign-captain-from-jack.xml new file mode 100644 index 00000000000..87f5adc0718 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/unassign-captain-from-jack.xml @@ -0,0 +1,26 @@ + + + + + UserType + + + name + jack + + + + + + targetRef + + + + + diff --git a/model/model-intest/src/test/resources/scripting/unassign-from-will-3.xml b/model/model-intest/src/test/resources/scripting/unassign-dummy-resource-from-will-legacy.xml similarity index 100% rename from model/model-intest/src/test/resources/scripting/unassign-from-will-3.xml rename to model/model-intest/src/test/resources/scripting/unassign-dummy-resource-from-will-legacy.xml diff --git a/model/model-intest/src/test/resources/scripting/unassign-dummy-resource-from-will.xml b/model/model-intest/src/test/resources/scripting/unassign-dummy-resource-from-will.xml new file mode 100644 index 00000000000..85b6345e713 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/unassign-dummy-resource-from-will.xml @@ -0,0 +1,26 @@ + + + + + UserType + + + name + willTurner + + + + + + construction/resourceRef + + + + + diff --git a/model/model-intest/src/test/resources/scripting/unassign-from-will.xml b/model/model-intest/src/test/resources/scripting/unassign-pirate-default-from-will-legacy.xml similarity index 95% rename from model/model-intest/src/test/resources/scripting/unassign-from-will.xml rename to model/model-intest/src/test/resources/scripting/unassign-pirate-default-from-will-legacy.xml index 9fef792f053..cb332731f1b 100644 --- a/model/model-intest/src/test/resources/scripting/unassign-from-will.xml +++ b/model/model-intest/src/test/resources/scripting/unassign-pirate-default-from-will-legacy.xml @@ -23,5 +23,6 @@ role 12345678-d34d-b33f-f00d-555555556666 + diff --git a/model/model-intest/src/test/resources/scripting/unassign-pirate-default-from-will.xml b/model/model-intest/src/test/resources/scripting/unassign-pirate-default-from-will.xml new file mode 100644 index 00000000000..e5500627da2 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/unassign-pirate-default-from-will.xml @@ -0,0 +1,25 @@ + + + + UserType + + + name + willTurner + + + + + + targetRef + + + + + diff --git a/model/model-intest/src/test/resources/scripting/unassign-from-will-2.xml b/model/model-intest/src/test/resources/scripting/unassign-pirate-manager-and-owner-from-will-legacy.xml similarity index 100% rename from model/model-intest/src/test/resources/scripting/unassign-from-will-2.xml rename to model/model-intest/src/test/resources/scripting/unassign-pirate-manager-and-owner-from-will-legacy.xml diff --git a/model/model-intest/src/test/resources/scripting/unassign-pirate-manager-and-owner-from-will.xml b/model/model-intest/src/test/resources/scripting/unassign-pirate-manager-and-owner-from-will.xml new file mode 100644 index 00000000000..e9d567e16e6 --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/unassign-pirate-manager-and-owner-from-will.xml @@ -0,0 +1,32 @@ + + + + + UserType + + + name + willTurner + + + + + + + targetRef + + + + targetRef + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/use-variables-legacy.xml b/model/model-intest/src/test/resources/scripting/use-variables-legacy.xml new file mode 100644 index 00000000000..53b1411cd0f --- /dev/null +++ b/model/model-intest/src/test/resources/scripting/use-variables-legacy.xml @@ -0,0 +1,88 @@ + + + + + + + execute-script + + script + + + log.info('=== Variables ===') + this.binding.variables.each {k,v -> log.info('{} = {}', k, v)} + + if (userName != 'user1') { + return 'Wrong username: ' + userName + } + if (new java.util.HashSet(groups) != ['group1', 'group2', 'group3'] as Set) { + return 'Wrong groups: ' + groups + } + if (academicYear != '2017/2018') { + return 'Wrong academic year: ' + academicYear + } + if (calendarYear != 2017) { + return 'Wrong calendar year: ' + calendarYear + } + if (!(deadline instanceof javax.xml.datatype.XMLGregorianCalendar)) { + return 'deadline missing or of wrong type: ' + deadline?.class + } + return 'ok' + + + + + forWholeInput + true + + + outputItem + http://midpoint.evolveum.com/xml/ns/public/common/common-3#description + + + + + userName + + $task/extension/userName + + + + groups + + $task/extension/studyGroup + + + + academicYear + + 2017/2018 + + + + calendarYear + + 2017 + + + + deadline + xsd:dateTime + + + + basic.fromNow("P3D") + + + + + + diff --git a/model/model-intest/src/test/resources/scripting/use-variables.xml b/model/model-intest/src/test/resources/scripting/use-variables.xml index 53b1411cd0f..a43402c82fa 100644 --- a/model/model-intest/src/test/resources/scripting/use-variables.xml +++ b/model/model-intest/src/test/resources/scripting/use-variables.xml @@ -9,45 +9,36 @@ - - execute-script - - script - - - log.info('=== Variables ===') - this.binding.variables.each {k,v -> log.info('{} = {}', k, v)} + + + + log.info('=== Variables ===') + this.binding.variables.each {k,v -> log.info('{} = {}', k, v)} - if (userName != 'user1') { - return 'Wrong username: ' + userName - } - if (new java.util.HashSet(groups) != ['group1', 'group2', 'group3'] as Set) { - return 'Wrong groups: ' + groups - } - if (academicYear != '2017/2018') { - return 'Wrong academic year: ' + academicYear - } - if (calendarYear != 2017) { - return 'Wrong calendar year: ' + calendarYear - } - if (!(deadline instanceof javax.xml.datatype.XMLGregorianCalendar)) { - return 'deadline missing or of wrong type: ' + deadline?.class - } - return 'ok' - - - - - forWholeInput - true - - - outputItem - http://midpoint.evolveum.com/xml/ns/public/common/common-3#description - - + if (userName != 'user1') { + return 'Wrong username: ' + userName + } + if (new java.util.HashSet(groups) != ['group1', 'group2', 'group3'] as Set) { + return 'Wrong groups: ' + groups + } + if (academicYear != '2017/2018') { + return 'Wrong academic year: ' + academicYear + } + if (calendarYear != 2017) { + return 'Wrong calendar year: ' + calendarYear + } + if (!(deadline instanceof javax.xml.datatype.XMLGregorianCalendar)) { + return 'deadline missing or of wrong type: ' + deadline?.class + } + return 'ok' + + + true + xsd:string + userName diff --git a/model/model-intest/testng-integration-full.xml b/model/model-intest/testng-integration-full.xml index 4b53fe97af3..fd87bbd1215 100644 --- a/model/model-intest/testng-integration-full.xml +++ b/model/model-intest/testng-integration-full.xml @@ -105,7 +105,8 @@ - + + diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index 077c4edaaeb..7354c6ee80d 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -3880,9 +3880,9 @@ protected void addTask(File file) throws SchemaException, IOException, ObjectAlr taskManager.addTask(prismContext.parseObject(file), new OperationResult("addTask")); } - protected void addObject(File file) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException, IOException { + protected String addObject(File file) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException, IOException { PrismObject object = prismContext.parseObject(file); - addObject(object); + return addObject(object); } protected PrismObject addObject(File file, Task task, OperationResult result) throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ExpressionEvaluationException, CommunicationException, ConfigurationException, PolicyViolationException, SecurityViolationException, IOException { @@ -5980,8 +5980,8 @@ protected ResourceAsserter assertResource(String oid, String message) thro return assertResource(resource, message); } - protected ResourceAsserter assertResource(PrismObject user, String message) { - ResourceAsserter asserter = ResourceAsserter.forResource(user, message); + protected ResourceAsserter assertResource(PrismObject resource, String message) { + ResourceAsserter asserter = ResourceAsserter.forResource(resource, message); initializeAsserter(asserter); return asserter; } @@ -5991,6 +5991,11 @@ protected ResourceAsserter assertResourceAfter(String oid) throws ObjectNo .display(); } + protected ResourceAsserter assertResourceBefore(String oid) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + return assertResource(oid, "before") + .display(); + } + // Change to PrismObjectDefinitionAsserter later protected PrismContainerDefinitionAsserter assertObjectDefinition(PrismObjectDefinition objectDef) { return assertContainerDefinition(objectDef); diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java index f8fb87a2b14..01a01db660c 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceCache.java @@ -111,20 +111,20 @@ synchronized PrismObject get(@NotNull String oid, String requested PrismObject resourceToReturn; PrismObject cachedResource = cache.get(oid); if (cachedResource == null) { - LOGGER.debug("MISS(not cached) for {}", oid); + LOGGER.debug("MISS(not cached) for {} (get)", oid); resourceToReturn = null; } else if (!compareVersion(requestedVersion, cachedResource.getVersion())) { - LOGGER.debug("MISS(wrong version) for {}", oid); + LOGGER.debug("MISS(wrong version) for {} (req={}, actual={})", oid, requestedVersion, cachedResource.getVersion()); LOGGER.trace("Cached resource version {} does not match requested resource version {}, purging from cache", cachedResource.getVersion(), requestedVersion); cache.remove(oid); resourceToReturn = null; } else if (readOnly) { cachedResource.checkImmutable(); - LOGGER.trace("HIT(read only) for {}", cachedResource); + LOGGER.trace("HIT(read only) for {} (v{})", cachedResource, cachedResource.getVersion()); resourceToReturn = cachedResource; } else { - LOGGER.debug("HIT(returning clone) for {}", cachedResource); + LOGGER.debug("HIT(returning clone) for {} (v{})", cachedResource, cachedResource.getVersion()); resourceToReturn = cachedResource.clone(); } @@ -151,7 +151,7 @@ PrismObject getIfLatest(@NotNull String oid, boolean readonly, Ope String version = repositoryService.getVersion(ResourceType.class, oid, parentResult); return get(oid, version, readonly); } else { - LOGGER.debug("MISS(not cached) for {}", oid); + LOGGER.debug("MISS(not cached) for {} (getIfLatest)", oid); CachePerformanceCollector.INSTANCE.registerMiss(ResourceCache.class, ResourceType.class, PER_CACHE); InternalMonitor.getResourceCacheStats().recordMiss(); return null; diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java index 93525a7b1e1..d78a8110ef7 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/ResourceManager.java @@ -1,1481 +1,1454 @@ -/* - * Copyright (c) 2010-2018 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ - -package com.evolveum.midpoint.provisioning.impl; - -import java.util.*; - -import javax.xml.namespace.QName; - -import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; -import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl; -import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.delta.*; -import com.evolveum.midpoint.prism.path.ItemPath; -import com.evolveum.midpoint.prism.schema.MutablePrismSchema; -import com.evolveum.midpoint.schema.processor.*; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.prism.xml.ns._public.types_3.SchemaDefinitionType; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.Validate; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import com.evolveum.midpoint.prism.query.ObjectQuery; -import com.evolveum.midpoint.prism.schema.PrismSchema; -import com.evolveum.midpoint.provisioning.api.GenericConnectorException; -import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance; -import com.evolveum.midpoint.provisioning.ucf.api.ExecuteProvisioningScriptOperation; -import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; -import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; -import com.evolveum.midpoint.repo.api.RepositoryService; -import com.evolveum.midpoint.repo.common.expression.Expression; -import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluationContext; -import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; -import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; -import com.evolveum.midpoint.schema.CapabilityUtil; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.constants.ConnectorTestOperation; -import com.evolveum.midpoint.schema.internals.InternalCounters; -import com.evolveum.midpoint.schema.internals.InternalMonitor; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus; -import com.evolveum.midpoint.schema.util.ConnectorTypeUtil; -import com.evolveum.midpoint.schema.util.MiscSchemaUtil; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.schema.util.ResourceTypeUtil; -import com.evolveum.midpoint.task.api.StateReporter; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.DOMUtil; -import com.evolveum.midpoint.util.DebugUtil; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.util.exception.SystemException; -import com.evolveum.midpoint.util.exception.TunnelException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationCapabilityType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CapabilityType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.SchemaCapabilityType; -import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ScriptCapabilityType; - -@Component -public class ResourceManager { - - @Autowired @Qualifier("cacheRepositoryService") - private RepositoryService repositoryService; - - @Autowired private ResourceCache resourceCache; - @Autowired private ConnectorManager connectorManager; - @Autowired private PrismContext prismContext; - @Autowired private ExpressionFactory expressionFactory; - @Autowired private ResourceOperationalStateManager operationalStateManager; - - private static final Trace LOGGER = TraceManager.getTrace(ResourceManager.class); - - private static final String OP_COMPLETE_RESOURCE = ResourceManager.class.getName() + ".completeResource"; - - /** - * Completes a resource that has been - we expect - just retrieved from the repository, usually by a search operation. - */ - PrismObject completeResource(PrismObject repositoryObject, GetOperationOptions options, Task task, - OperationResult parentResult) - throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { - - String oid = repositoryObject.getOid(); - boolean readonly = GetOperationOptions.isReadOnly(options); - - PrismObject cachedResource = resourceCache.get(oid, repositoryObject.getVersion(), readonly); - if (cachedResource != null) { - return cachedResource; - } else { - LOGGER.debug("Storing fetched resource {}, version {} to cache (previously cached version {})", - oid, repositoryObject.getVersion(), resourceCache.getVersion(oid)); - PrismObject mutableRepositoryObject = repositoryObject.cloneIfImmutable(); - return completeAndCacheResource(mutableRepositoryObject, options, task, parentResult); - } - } - - /** - * Gets a resource. - */ - public PrismObject getResource(String oid, GetOperationOptions options, Task task, OperationResult parentResult) - throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { - boolean readonly = GetOperationOptions.isReadOnly(options); - PrismObject cachedResource = resourceCache.getIfLatest(oid, readonly, parentResult); - if (cachedResource != null) { - LOGGER.trace("Returning resource from cache:\n{}", cachedResource.debugDumpLazily()); - return cachedResource; - } else { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Fetching resource {} and storing to cache (previously cached version {})", - oid, resourceCache.getVersion(oid)); - } - // We must obviously NOT fetch resource from repo as read-only. We are going to modify it. - PrismObject repositoryObject = readResourceFromRepository(oid, parentResult); - return completeAndCacheResource(repositoryObject, options, task, parentResult); - } - } - - private PrismObject readResourceFromRepository(String oid, OperationResult parentResult) - throws ObjectNotFoundException, SchemaException { - InternalMonitor.recordCount(InternalCounters.RESOURCE_REPOSITORY_READ_COUNT); - return repositoryService.getObject(ResourceType.class, oid, null, parentResult); - } - - /** - * Here we complete the resource and cache it. - * - * @param repositoryObject Up-to-date repository object. Must be mutable. - */ - private PrismObject completeAndCacheResource(PrismObject repositoryObject, - GetOperationOptions options, Task task, OperationResult parentResult) - throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { - - checkMutable(repositoryObject); - - PrismObject completedResource = completeResourceInternal(repositoryObject, null, - false, null, options, task, parentResult); - - logResourceAfterCompletion(completedResource); - if (!isComplete(completedResource)) { - // No not cache non-complete resources (e.g. those retrieved with noFetch) - LOGGER.trace("Not putting {} into cache because it's not complete", repositoryObject); - } else { - OperationResult completeResourceResult = parentResult.findSubresult(OP_COMPLETE_RESOURCE); - if (!completeResourceResult.isSuccess()) { - LOGGER.trace("Not putting {} into cache because the completeResource operation status is {}", - ObjectTypeUtil.toShortString(repositoryObject), completeResourceResult.getStatus()); - } else { - // Cache only resources that are completely OK - resourceCache.put(completedResource); - } - } - return completedResource; - } - - private void logResourceAfterCompletion(PrismObject completedResource) { - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Resource after completion, before putting into cache:\n{}", completedResource.debugDump()); - Element xsdSchemaElement = ResourceTypeUtil.getResourceXsdSchema(completedResource); - if (xsdSchemaElement == null) { - LOGGER.trace("Schema: null"); - } else { - LOGGER.trace("Schema:\n{}", - DOMUtil.serializeDOMToString(ResourceTypeUtil.getResourceXsdSchema(completedResource))); - } - } - } - - void deleteResource(String oid, OperationResult parentResult) throws ObjectNotFoundException { - resourceCache.remove(oid); - repositoryService.deleteObject(ResourceType.class, oid, parentResult); - } - - /** - * Make sure that the resource is complete. - * - * It will check if the resource has a sufficiently fresh schema, etc. - * - * Returned resource may be the same or may be a different instance, but it - * is guaranteed that it will be "fresher" and will correspond to the - * repository state (assuming that the provided resource also corresponded - * to the repository state). - * - * The connector schema that was fetched before can be supplied to this - * method. This is just an optimization. It comes handy e.g. in test - * connection case. - * - * Note: This is not really the best place for this method. Need to figure - * out correct place later. - * - * @param repoResource - * Resource to check - * @param resourceSchema - * schema that was freshly pre-fetched (or null) - * - * @return completed resource - */ - private PrismObject completeResourceInternal(PrismObject repoResource, ResourceSchema resourceSchema, - boolean fetchedSchema, Map> capabilityMap, GetOperationOptions options, Task task, OperationResult parentResult) - throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { - - checkMutable(repoResource); - - OperationResult result = parentResult.createMinorSubresult(OP_COMPLETE_RESOURCE); - try { - try { - applyConnectorSchemasToResource(repoResource, task, result); - } catch (Throwable t) { - String message = - "An error occurred while applying connector schema to connector configuration of " + repoResource + ": " - + t.getMessage(); - result.recordPartialError(message, t); - LOGGER.warn(message, t); - return repoResource; - } - - PrismObject newResource; - - if (isComplete(repoResource)) { - // The resource is complete. - newResource = repoResource; - - } else { - // The resource is NOT complete. Try to fetch schema and capabilities - - if (GetOperationOptions.isNoFetch(options)) { - // We need to fetch schema, but the noFetch option is specified. Therefore return whatever we have. - result.recordSuccessIfUnknown(); - return repoResource; - } - - try { - - completeSchemaAndCapabilities(repoResource, resourceSchema, fetchedSchema, capabilityMap, result); - - } catch (Exception ex) { - // Catch the exceptions. There are not critical. We need to catch them all because the connector may - // throw even undocumented runtime exceptions. - // Even non-complete resource may still be usable. The fetchResult indicates that there was an error - result.recordPartialError("Cannot complete resource schema and capabilities: " + ex.getMessage(), ex); - return repoResource; - } - - try { - // Now we need to re-read the resource from the repository and re-apply the schemas. This ensures that we will - // cache the correct version and that we avoid race conditions, etc. - - newResource = readResourceFromRepository(repoResource.getOid(), result); - applyConnectorSchemasToResource(newResource, task, result); - - } catch (SchemaException | ObjectNotFoundException | RuntimeException e) { - result.recordFatalError(e); - throw e; - } - } - - try { - // make sure it has parsed resource and refined schema. We are going to cache - // it, so we want to cache it with the parsed schemas - RefinedResourceSchemaImpl.getResourceSchema(newResource, prismContext); - RefinedResourceSchemaImpl.getRefinedSchema(newResource); - - } catch (SchemaException e) { - String message = "Schema error while processing schemaHandling section of " + newResource + ": " + e.getMessage(); - result.recordPartialError(message, e); - LOGGER.warn(message, e); - return newResource; - } catch (RuntimeException e) { - String message = - "Unexpected error while processing schemaHandling section of " + newResource + ": " + e.getMessage(); - result.recordPartialError(message, e); - LOGGER.warn(message, e); - return newResource; - } - - result.recordSuccessIfUnknown(); - - return newResource; - } catch (Throwable t) { - result.recordFatalError(t); - throw t; - } finally { - result.computeStatusIfUnknown(); - } - } - - private boolean isComplete(PrismObject resource) { - ResourceType resourceType = resource.asObjectable(); - if (ResourceTypeUtil.getResourceXsdSchema(resource) == null) { - return false; - } - CapabilitiesType capabilitiesType = resourceType.getCapabilities(); - return capabilitiesType != null && capabilitiesType.getCachingMetadata() != null; - } - - - private void completeSchemaAndCapabilities(PrismObject resource, ResourceSchema resourceSchema, boolean fetchedSchema, - Map> capabilityMap, OperationResult result) - throws SchemaException, CommunicationException, ObjectNotFoundException, GenericFrameworkException, ConfigurationException { - - Collection> modifications = new ArrayList<>(); - - // Capabilities - // we need to process capabilities first. Schema is one of the connector capabilities. - // We need to determine this capability to select the right connector for schema retrieval. - completeCapabilities(resource, capabilityMap != null, capabilityMap, modifications, result); - - if (resourceSchema == null) { - // Try to get existing schema from resource. We do not want to override this if it exists - // (but we still want to refresh the capabilities, that happens below) - resourceSchema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); - } - - if (resourceSchema == null || resourceSchema.isEmpty()) { - - LOGGER.trace("Fetching resource schema for {}", resource); - - resourceSchema = fetchResourceSchema(resource, capabilityMap, result); - - if (resourceSchema == null) { - LOGGER.warn("No resource schema fetched from {}", resource); - } else if (resourceSchema.isEmpty()) { - LOGGER.warn("Empty resource schema fetched from {}", resource); - } else { - LOGGER.debug("Fetched resource schema for {}: {} definitions", resource, resourceSchema.getDefinitions().size()); - fetchedSchema = true; - } - } - - if (resourceSchema != null) { - if (fetchedSchema) { - adjustSchemaForSimulatedCapabilities(resource, resourceSchema); - modifications.add(createSchemaUpdateDelta(resource, resourceSchema)); - AvailabilityStatusType previousStatus = ResourceTypeUtil.getLastAvailabilityStatus(resource.asObjectable()); - if (previousStatus != AvailabilityStatusType.UP) { - modifications.addAll(operationalStateManager.createAndLogOperationalStateDeltas(previousStatus, - AvailabilityStatusType.UP, resource.toString(), - "resource schema was successfully fetched", resource)); - } else { - // just for sure (if the status changed in the meanwhile) - modifications.add(operationalStateManager.createAvailabilityStatusDelta(AvailabilityStatusType.UP)); - } - } else { - CachingMetadataType schemaCachingMetadata = resource.asObjectable().getSchema().getCachingMetadata(); - if (schemaCachingMetadata == null) { - schemaCachingMetadata = MiscSchemaUtil.generateCachingMetadata(); - modifications.add( - prismContext.deltaFactory().property().createModificationReplaceProperty( - ItemPath.create(ResourceType.F_SCHEMA, CapabilitiesType.F_CACHING_METADATA), - resource.getDefinition(), - schemaCachingMetadata) - ); - } - } - } - - if (!modifications.isEmpty()) { - try { - LOGGER.trace("Completing {}:\n{}", resource, DebugUtil.debugDumpLazily(modifications, 1)); - repositoryService.modifyObject(ResourceType.class, resource.getOid(), modifications, result); - InternalMonitor.recordCount(InternalCounters.RESOURCE_REPOSITORY_MODIFY_COUNT); - } catch (ObjectAlreadyExistsException ex) { - // This should not happen - throw new SystemException(ex); - } - } - } - - private void completeCapabilities(PrismObject resource, boolean forceRefresh, Map> capabilityMap, Collection> modifications, - OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { - ResourceType resourceType = resource.asObjectable(); - ConnectorSpec defaultConnectorSpec = getDefaultConnectorSpec(resource); - CapabilitiesType resourceCapType = resourceType.getCapabilities(); - if (resourceCapType == null) { - resourceCapType = new CapabilitiesType(); - resourceType.setCapabilities(resourceCapType); - } - completeConnectorCapabilities(defaultConnectorSpec, resourceCapType, ResourceType.F_CAPABILITIES, forceRefresh, - capabilityMap==null?null:capabilityMap.get(null), - modifications, result); - - for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { - ConnectorSpec connectorSpec = getConnectorSpec(resource, additionalConnectorType); - CapabilitiesType connectorCapType = additionalConnectorType.getCapabilities(); - if (connectorCapType == null) { - connectorCapType = new CapabilitiesType(); - additionalConnectorType.setCapabilities(connectorCapType); - } - ItemPath itemPath = additionalConnectorType.asPrismContainerValue().getPath().append(ConnectorInstanceSpecificationType.F_CAPABILITIES); - completeConnectorCapabilities(connectorSpec, connectorCapType, itemPath, forceRefresh, - capabilityMap==null?null:capabilityMap.get(additionalConnectorType.getName()), - modifications, result); - } - } - - private void completeConnectorCapabilities(ConnectorSpec connectorSpec, CapabilitiesType capType, ItemPath itemPath, boolean forceRefresh, - Collection retrievedCapabilities, Collection> modifications, OperationResult result) - throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { - - if (capType.getNative() != null && !capType.getNative().getAny().isEmpty()) { - if (!forceRefresh) { - CachingMetadataType cachingMetadata = capType.getCachingMetadata(); - if (cachingMetadata == null) { - cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); - modifications.add( - prismContext.deltaFactory().property().createModificationReplaceProperty( - ItemPath.create(ResourceType.F_CAPABILITIES, CapabilitiesType.F_CACHING_METADATA), - connectorSpec.getResource().getDefinition(), - cachingMetadata) - ); - } - return; - } - } - - if (retrievedCapabilities == null) { - try { - - InternalMonitor.recordCount(InternalCounters.CONNECTOR_CAPABILITIES_FETCH_COUNT); - - ConnectorInstance connector = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); - retrievedCapabilities = connector.fetchCapabilities(result); - - } catch (GenericFrameworkException e) { - throw new GenericConnectorException("Generic error in connector " + connectorSpec + ": " - + e.getMessage(), e); - } - } - - CapabilityCollectionType nativeCapType = new CapabilityCollectionType(); - capType.setNative(nativeCapType); - nativeCapType.getAny().addAll(retrievedCapabilities); - - CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); - capType.setCachingMetadata(cachingMetadata); - - //noinspection unchecked - ObjectDelta capabilitiesReplaceDelta = prismContext.deltaFactory().object() - .createModificationReplaceContainer(ResourceType.class, connectorSpec.getResource().getOid(), - itemPath, capType.asPrismContainerValue().clone()); - - modifications.addAll(capabilitiesReplaceDelta.getModifications()); - } - - private ContainerDelta createSchemaUpdateDelta(PrismObject resource, ResourceSchema resourceSchema) throws SchemaException { - Document xsdDoc; - try { - xsdDoc = resourceSchema.serializeToXsd(); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("Serialized XSD resource schema for {}:\n{}", resource, DOMUtil.serializeDOMToString(xsdDoc)); - } - } catch (SchemaException e) { - throw new SchemaException("Error processing resource schema for " + resource + ": " + e.getMessage(), e); - } - - Element xsdElement = DOMUtil.getFirstChildElement(xsdDoc); - if (xsdElement == null) { - throw new SchemaException("No schema was generated for " + resource); - } - CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); - - ContainerDelta schemaContainerDelta = prismContext.deltaFactory().container().createDelta( - ResourceType.F_SCHEMA, ResourceType.class); - PrismContainerValue cval = prismContext.itemFactory().createContainerValue(); - schemaContainerDelta.setValueToReplace(cval); - PrismProperty cachingMetadataProperty = cval - .createProperty(XmlSchemaType.F_CACHING_METADATA); - cachingMetadataProperty.setRealValue(cachingMetadata); - List objectClasses = ResourceTypeUtil.getSchemaGenerationConstraints(resource); - if (objectClasses != null) { - PrismProperty generationConstraints = cval - .createProperty(XmlSchemaType.F_GENERATION_CONSTRAINTS); - SchemaGenerationConstraintsType constraints = new SchemaGenerationConstraintsType(); - constraints.getGenerateObjectClass().addAll(objectClasses); - generationConstraints.setRealValue(constraints); - } - PrismProperty definitionProperty = cval.createProperty(XmlSchemaType.F_DEFINITION); - ObjectTypeUtil.setXsdSchemaDefinition(definitionProperty, xsdElement); - - return schemaContainerDelta; - } - - /** - * Apply proper definition (connector schema) to the resource. - */ - private void applyConnectorSchemasToResource(PrismObject resource, Task task, OperationResult result) - throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { - checkMutable(resource); - PrismObjectDefinition newResourceDefinition = resource.getDefinition().clone(); - for (ConnectorSpec connectorSpec : getAllConnectorSpecs(resource)) { - try { - applyConnectorSchemaToResource(connectorSpec, newResourceDefinition, resource, task, result); - } catch (CommunicationException | ConfigurationException | SecurityViolationException e) { - throw new IllegalStateException("Unexpected exception: " + e.getMessage(), e); // fixme temporary solution - } - } - resource.setDefinition(newResourceDefinition); - } - - /** - * Apply proper definition (connector schema) to the resource. - */ - private void applyConnectorSchemaToResource(ConnectorSpec connectorSpec, PrismObjectDefinition resourceDefinition, - PrismObject resource, Task task, OperationResult result) - throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - - ConnectorType connectorType = connectorManager.getConnector(connectorSpec, result); - PrismSchema connectorSchema = connectorManager.getAttachedConnectorSchema(connectorType); - PrismContainerDefinition configurationContainerDefinition = ConnectorTypeUtil - .findConfigurationContainerDefinition(connectorType, connectorSchema); - if (configurationContainerDefinition == null) { - throw new SchemaException("No configuration container definition in schema of " + connectorType); - } - - configurationContainerDefinition = configurationContainerDefinition.clone(); - PrismContainer configurationContainer = connectorSpec.getConnectorConfiguration(); - // We want element name, minOccurs/maxOccurs and similar definition to be taken from the original, not the schema - // the element is global in the connector schema. therefore it does not have correct maxOccurs - if (configurationContainer != null) { - configurationContainerDefinition.adoptElementDefinitionFrom(configurationContainer.getDefinition()); - configurationContainer.applyDefinition(configurationContainerDefinition, true); - - try { - //noinspection unchecked - configurationContainer.accept(visitable -> { - if ((visitable instanceof PrismProperty)) { - try { - evaluateExpression((PrismProperty)visitable, resource, task, result); - } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | ConfigurationException | SecurityViolationException e) { - throw new TunnelException(e); - } - } - // TODO treat configuration items that are containers themselves - }); - } catch (TunnelException te) { - Throwable e = te.getCause(); - if (e instanceof SchemaException) { - throw (SchemaException)e; - } else if (e instanceof ObjectNotFoundException) { - throw (ObjectNotFoundException)e; - } else if (e instanceof ExpressionEvaluationException) { - throw (ExpressionEvaluationException)e; - } else if (e instanceof CommunicationException) { - throw (CommunicationException)e; - } else if (e instanceof ConfigurationException) { - throw (ConfigurationException)e; - } else if (e instanceof SecurityViolationException) { - throw (SecurityViolationException)e; - } else if (e instanceof RuntimeException) { - throw (RuntimeException)e; - } else if (e instanceof Error) { - throw (Error)e; - } else { - throw new SystemException(e); - } - } - - } else { - configurationContainerDefinition.adoptElementDefinitionFrom( - resourceDefinition.findContainerDefinition(ResourceType.F_CONNECTOR_CONFIGURATION)); - } - - if (connectorSpec.getConnectorName() == null) { - // Default connector, for compatibility - // It does not make sense to update this for any other connectors. - // We cannot have one definition for additionalConnector[1]/connectorConfiguration and - // different definition for additionalConnector[2]/connectorConfiguration in the object definition. - // The way to go is to set up definitions on the container level. - resourceDefinition.replaceDefinition(ResourceType.F_CONNECTOR_CONFIGURATION, configurationContainerDefinition); - } - - } - - private void evaluateExpression(PrismProperty configurationProperty, PrismObject resource, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { - PrismPropertyDefinition propDef = configurationProperty.getDefinition(); - String shortDesc = "connector configuration property "+configurationProperty+" in "+resource; - List> extraValues = new ArrayList<>(); - for (PrismPropertyValue configurationPropertyValue: configurationProperty.getValues()) { - ExpressionWrapper expressionWrapper = configurationPropertyValue.getExpression(); - if (expressionWrapper == null) { - return; - } - Object expressionObject = expressionWrapper.getExpression(); - if (!(expressionObject instanceof ExpressionType)) { - throw new IllegalStateException("Expected that expression in "+configurationPropertyValue+" will be ExpressionType, but it was "+expressionObject); - } - ExpressionType expressionType = (ExpressionType) expressionWrapper.getExpression(); - - Expression, PrismPropertyDefinition> expression = expressionFactory.makeExpression(expressionType, propDef, MiscSchemaUtil.getExpressionProfile(), shortDesc, task, result); - ExpressionVariables variables = new ExpressionVariables(); - - // TODO: populate variables - - ExpressionEvaluationContext expressionContext = new ExpressionEvaluationContext(null, variables, shortDesc, task); - PrismValueDeltaSetTriple> expressionOutputTriple = expression.evaluate(expressionContext, - result); - Collection> expressionOutputValues = expressionOutputTriple.getNonNegativeValues(); - if (!expressionOutputValues.isEmpty()) { - Iterator> iterator = expressionOutputValues.iterator(); - PrismPropertyValue firstValue = iterator.next(); - configurationPropertyValue.setValue(firstValue.getValue()); - while (iterator.hasNext()) { - extraValues.add(iterator.next()); - } - } - } - for (PrismPropertyValue extraValue: extraValues) { - configurationProperty.add(extraValue); - } - } - - private ResourceSchema fetchResourceSchema(PrismObject resource, Map> capabilityMap, OperationResult parentResult) - throws CommunicationException, GenericFrameworkException, ConfigurationException, ObjectNotFoundException, SchemaException { - ConnectorSpec connectorSpec = selectConnectorSpec(resource, capabilityMap, SchemaCapabilityType.class); - if (connectorSpec == null) { - LOGGER.debug("No connector has schema capability, cannot fetch resource schema"); - return null; - } - InternalMonitor.recordCount(InternalCounters.RESOURCE_SCHEMA_FETCH_COUNT); - List generateObjectClasses = ResourceTypeUtil.getSchemaGenerationConstraints(resource); - ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, parentResult); - - LOGGER.debug("Trying to get schema from {}, objectClasses to generate: {}", connectorSpec, generateObjectClasses); - ResourceSchema resourceSchema = connectorInstance.fetchResourceSchema(parentResult); - - if (ResourceTypeUtil.isValidateSchema(resource.asObjectable())) { - ResourceTypeUtil.validateSchema(resourceSchema, resource); - } - return resourceSchema; - - } - - /** - * Test the connection. - * - * @param resource Resource object as fetched from the repository. Must NOT be immutable! - * - * @throws ObjectNotFoundException If the resource object cannot be found in repository (e.g. when trying to set its - * availability status). - */ - public void testConnection(PrismObject resource, Task task, OperationResult parentResult) - throws ObjectNotFoundException { - - checkMutable(resource); - - String resourceOid = resource.getOid(); - - String operationDesc = "test resource " + resourceOid + "connection"; - - List allConnectorSpecs; - try { - allConnectorSpecs = getAllConnectorSpecs(resource); - } catch (SchemaException e) { - String statusChangeReason = operationDesc + ", getting all connectors failed: " + e.getMessage(); - if (LOGGER.isTraceEnabled()) { - LOGGER.error("Configuration error: {}", e.getMessage(), e); - } - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - parentResult.recordFatalError("Configuration error: " + e.getMessage(), e); - return; - } - - Map> capabilityMap = new HashMap<>(); - for (ConnectorSpec connectorSpec: allConnectorSpecs) { - - OperationResult connectorTestResult = parentResult - .createSubresult(ConnectorTestOperation.CONNECTOR_TEST.getOperation()); - connectorTestResult.addParam(OperationResult.PARAM_NAME, connectorSpec.getConnectorName()); - connectorTestResult.addParam(OperationResult.PARAM_OID, connectorSpec.getConnectorOid()); - - testConnectionConnector(connectorSpec, capabilityMap, task, connectorTestResult); - - connectorTestResult.computeStatus(); - - if (!connectorTestResult.isAcceptable()) { - //nothing more to do.. if it failed while testing connection, status is set. - // we do not need to continue and waste the time. - return; - } - } - - // === test SCHEMA === - - OperationResult schemaResult = parentResult.createSubresult(ConnectorTestOperation.RESOURCE_SCHEMA.getOperation()); - - ResourceSchema schema; - try { - - schema = fetchResourceSchema(resource, capabilityMap, schemaResult); - - } catch (CommunicationException e) { - String statusChangeReason = operationDesc + " failed while fetching schema: " + e.getMessage(); - if (LOGGER.isTraceEnabled()) { - LOGGER.error("Communication error: {}", e.getMessage(), e); - } - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError("Communication error: " + e.getMessage(), e); - return; - } catch (GenericFrameworkException | ConfigurationException | ObjectNotFoundException | SchemaException | RuntimeException e) { - String statusChangeReason = operationDesc + " failed while fetching schema: " + e.getMessage(); - if (LOGGER.isTraceEnabled()) { - LOGGER.error("Error: {}", e.getMessage(), e); - } - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError("Error: " + e.getMessage(), e); - return; - } - - if (schema == null || schema.isEmpty()) { - // Resource does not support schema - // If there is a static schema in resource definition this may still be OK - try { - schema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); - } catch (SchemaException e) { - String statusChangeReason = operationDesc + " failed while parsing refined schema: " + e.getMessage(); - if (LOGGER.isTraceEnabled()) { - LOGGER.error("Error: {}", e.getMessage(), e); - } - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError(e); - return; - } - - if (schema == null || schema.isEmpty()) { - String msg = "Connector does not support schema and no static schema available"; - String statusChangeReason = operationDesc + ". " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError(msg); - return; - } - } - - // Invoke completeResource(). This will store the fetched schema to the ResourceType if there is no - // definition already. Therefore the testResource() can be used to generate the resource schema - until we - // have full schema caching capability. - PrismObject completedResource; - try { - // Re-fetching from repository to get up-to-date availability status (to avoid phantom state change records). - PrismObject repoResource = repositoryService.getObject(ResourceType.class, resourceOid, null, schemaResult); - completedResource = completeResourceInternal(repoResource, schema, true, capabilityMap, null, task, schemaResult); - } catch (ObjectNotFoundException e) { - String msg = "Object not found (unexpected error, probably a bug): " + e.getMessage(); - String statusChangeReason = operationDesc + " failed while completing resource. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError(msg, e); - return; - } catch (SchemaException e) { - String msg = "Schema processing error (probably connector bug): " + e.getMessage(); - String statusChangeReason = operationDesc + " failed while completing resource. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError(msg, e); - return; - } catch (ExpressionEvaluationException e) { - String msg = "Expression error: " + e.getMessage(); - String statusChangeReason = operationDesc + " failed while completing resource. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError(msg, e); - return; - } catch (RuntimeException e) { - String msg = "Unspecified exception: " + e.getMessage(); - String statusChangeReason = operationDesc + " failed while completing resource. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - schemaResult.recordFatalError(msg, e); - return; - } - - schemaResult.recordSuccess(); - - try { - updateResourceSchema(allConnectorSpecs, parentResult, completedResource); - } catch (SchemaException | ObjectNotFoundException | CommunicationException | ConfigurationException | RuntimeException e) { - String statusChangeReason = operationDesc + " failed while updating resource schema: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); - parentResult.recordFatalError("Couldn't update resource schema: " + e.getMessage(), e); - return; - } - - // TODO: connector sanity (e.g. refined schema, at least one account type, identifiers - // in schema, etc.) - - } - - private void checkMutable(PrismObject resource) { - if (resource.isImmutable()) { - throw new IllegalArgumentException("Got immutable resource object, while expecting mutable one: " + resource); - } - } - - private void updateResourceSchema(List allConnectorSpecs, OperationResult parentResult, - PrismObject resource) - throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { - ResourceSchema resourceSchema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); - if (resourceSchema != null) { - for (ConnectorSpec connectorSpec : allConnectorSpecs) { - ConnectorInstance instance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, parentResult); - instance.updateSchema(resourceSchema); - } - } - } - - private void testConnectionConnector(ConnectorSpec connectorSpec, Map> capabilityMap, Task task, - OperationResult parentResult) throws ObjectNotFoundException { - - // === test INITIALIZATION === - - OperationResult initResult = parentResult - .createSubresult(ConnectorTestOperation.CONNECTOR_INITIALIZATION.getOperation()); - - LOGGER.debug("Testing connection using {}", connectorSpec); - String resourceOid = connectorSpec.getResource().getOid(); - - String operationCtx = "testing connection using " + connectorSpec; - - ConfiguredConnectorInstanceEntry connectorInstanceCacheEntry; - try { - // Make sure we are getting non-configured instance. - connectorInstanceCacheEntry = connectorManager.getConnectorInstanceCacheEntry(connectorSpec, initResult); - initResult.recordSuccess(); - } catch (ObjectNotFoundException e) { - // The connector was not found. The resource definition is either - // wrong or the connector is not installed. - String msg = "The connector was not found: "+e.getMessage(); - operationCtx += " failed while getting connector instance. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - initResult.recordFatalError(msg, e); - return; - } catch (SchemaException e) { - String msg = "Schema error while dealing with the connector definition: "+e.getMessage(); - operationCtx += " failed while getting connector instance. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - initResult.recordFatalError(msg, e); - return; - } catch (RuntimeException | Error e) { - String msg = "Unexpected runtime error: "+e.getMessage(); - operationCtx += " failed while getting connector instance. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - initResult.recordFatalError(msg, e); - return; - } catch (CommunicationException e) { - String msg = "Communication error: "+e.getMessage(); - operationCtx += " failed while getting connector instance. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); - initResult.recordFatalError(msg, e); - return; - } catch (ConfigurationException e) { - String msg = "Configuration error: "+e.getMessage(); - operationCtx += " failed while getting connector instance. " + msg; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - initResult.recordFatalError(msg, e); - return; - } - - ConnectorInstance connector = connectorInstanceCacheEntry.getConnectorInstance(); - - - // === test CONFIGURATION === - - OperationResult configResult = parentResult - .createSubresult(ConnectorTestOperation.CONNECTOR_CONFIGURATION.getOperation()); - - try { - PrismObject resource = connectorSpec.getResource(); - PrismObjectDefinition newResourceDefinition = resource.getDefinition().clone(); - applyConnectorSchemaToResource(connectorSpec, newResourceDefinition, resource, task, configResult); - PrismContainerValue connectorConfiguration = connectorSpec.getConnectorConfiguration().getValue(); - - InternalMonitor.recordCount(InternalCounters.CONNECTOR_INSTANCE_CONFIGURATION_COUNT); - - connector.configure(connectorConfiguration, ResourceTypeUtil.getSchemaGenerationConstraints(resource), configResult); - - // We need to explicitly initialize the instance, e.g. in case that the schema and capabilities - // cannot be detected by the connector and therefore are provided in the resource - // - // NOTE: the capabilities and schema that are used here are NOT necessarily those that are detected by the resource. - // The detected schema will come later. The schema here is the one that is stored in the resource - // definition (ResourceType). This may be schema that was detected previously. But it may also be a schema - // that was manually defined. This is needed to be passed to the connector in case that the connector - // cannot detect the schema and needs schema/capabilities definition to establish a connection. - // Most connectors will just ignore the schema and capabilities that are provided here. - // But some connectors may need it (e.g. CSV connector working with CSV file without a header). - // - ResourceSchema previousResourceSchema = RefinedResourceSchemaImpl.getResourceSchema(connectorSpec.getResource(), prismContext); - Collection previousCapabilities = ResourceTypeUtil.getNativeCapabilitiesCollection(connectorSpec.getResource().asObjectable()); - connector.initialize(previousResourceSchema, previousCapabilities, - ResourceTypeUtil.isCaseIgnoreAttributeNames(connectorSpec.getResource().asObjectable()), configResult); - - configResult.recordSuccess(); - } catch (CommunicationException e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Communication error", e); - return; - } catch (GenericFrameworkException e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Generic error", e); - return; - } catch (SchemaException e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Schema error", e); - return; - } catch (ConfigurationException e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Configuration error", e); - return; - } catch (ObjectNotFoundException e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Object not found", e); - return; - } catch (ExpressionEvaluationException e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Expression error", e); - return; - } catch (SecurityViolationException e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Security violation", e); - return; - } catch (RuntimeException | Error e) { - operationCtx += " failed while testing configuration: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - configResult.recordFatalError("Unexpected runtime error", e); - return; - } - - // === test CONNECTION === - - // delegate the main part of the test to the connector - connector.test(parentResult); - - parentResult.computeStatus(); - if (!parentResult.isAcceptable()) { - operationCtx += ". Connector test failed: " + parentResult.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); - // No point in going on. Following tests will fail anyway, they will - // just produce misleading - // messages. - return; - } else { - operationCtx += ". Connector test successful."; - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.UP, operationCtx, task, parentResult, false); - } - - // === test CAPABILITIES === - - OperationResult capabilitiesResult = parentResult - .createSubresult(ConnectorTestOperation.CONNECTOR_CAPABILITIES.getOperation()); - try { - InternalMonitor.recordCount(InternalCounters.CONNECTOR_CAPABILITIES_FETCH_COUNT); - Collection retrievedCapabilities = connector.fetchCapabilities(capabilitiesResult); - - capabilityMap.put(connectorSpec.getConnectorName(), retrievedCapabilities); - capabilitiesResult.recordSuccess(); - } catch (CommunicationException e) { - operationCtx += " failed while testing capabilities: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); - capabilitiesResult.recordFatalError("Communication error", e); - return; - } catch (GenericFrameworkException e) { - operationCtx += " failed while testing capabilities: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - capabilitiesResult.recordFatalError("Generic error", e); - return; - } catch (ConfigurationException e) { - operationCtx += " failed while testing capabilities: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - capabilitiesResult.recordFatalError("Configuration error", e); - return; - } catch (SchemaException e) { - operationCtx += " failed while testing capabilities: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - capabilitiesResult.recordFatalError("Schema error", e); - return; - } catch (RuntimeException | Error e) { - operationCtx += " failed while testing capabilities: " + e.getMessage(); - modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); - capabilitiesResult.recordFatalError("Unexpected runtime error", e); - return; - } - - // Connector instance is fully configured at this point. - // But the connector cache entry may not be set up properly and it is not yet placed into the cache. - // Therefore make sure the caching bit is completed. - // Place the connector to cache even if it was configured at the beginning. The connector is reconfigured now. - connectorManager.cacheConfiguredConnector(connectorInstanceCacheEntry, connectorSpec); - } - - /** - * Modifies resource availability status in the repository (if needed). - * - * The necessity of status modification is determined against the current version of the resource - unless "skipGetResource" - * is set. The resource is hopefully cached ResourceCache, so the performance impact should be almost ponone. - * - * Also note that in-memory representation of the resource is not modified. As a side effect, the cached resource - * is invalidated because of the modification. But it will be loaded on the next occasion. This should be quite harmless, - * as we do not expect availability changes to occur frequently. - * - * @param statusChangeReason Description of the reason of changing the availability status. - * @param skipGetResource True if we want to skip "getResource" operation and therefore apply the change regardless of - * the current resource availability status. This is to be used in situations where we expect that - * the resource might not be successfully retrievable (e.g. if it's broken). - * - * @throws ObjectNotFoundException If the resource object does not exist in repository. - */ - public void modifyResourceAvailabilityStatus(String resourceOid, AvailabilityStatusType newStatus, String statusChangeReason, - Task task, OperationResult result, boolean skipGetResource) throws ObjectNotFoundException { - - AvailabilityStatusType currentStatus; - String resourceDesc; - PrismObject resource; - if (skipGetResource) { - resource = null; - currentStatus = null; - resourceDesc = "resource " + resourceOid; - } else { - try { - resource = getResource(resourceOid, GetOperationOptions.createNoFetch(), task, result); - } catch (SchemaException | ExpressionEvaluationException e) { - // We actually do not expect any of these exceptions here. The resource is most probably in use - result.recordFatalError("Unexpected exception: " + e.getMessage(), e); - throw new SystemException("Unexpected exception: " + e.getMessage(), e); - } - ResourceType resourceBean = resource.asObjectable(); - currentStatus = ResourceTypeUtil.getLastAvailabilityStatus(resourceBean); - resourceDesc = resource.toString(); - } - - if (newStatus != currentStatus) { - try { - List> modifications = operationalStateManager.createAndLogOperationalStateDeltas(currentStatus, newStatus, - resourceDesc, statusChangeReason, resource); - repositoryService.modifyObject(ResourceType.class, resourceOid, modifications, result); - result.computeStatusIfUnknown(); - InternalMonitor.recordCount(InternalCounters.RESOURCE_REPOSITORY_MODIFY_COUNT); - } catch (SchemaException | ObjectAlreadyExistsException e) { - throw new SystemException("Unexpected exception while recording operation state change: " + e.getMessage(), e); - } - } - } - - /** - * Adjust scheme with respect to capabilities. E.g. disable attributes that - * are used for special purpose (such as account activation simulation). - * - * TODO treat also objectclass-specific capabilities here - */ - private void adjustSchemaForSimulatedCapabilities(PrismObject resource, ResourceSchema resourceSchema) { - ResourceType resourceType = resource.asObjectable(); - if (resourceType.getCapabilities() == null || resourceType.getCapabilities().getConfigured() == null) { - return; - } - ActivationCapabilityType activationCapability = CapabilityUtil.getCapability(resourceType - .getCapabilities().getConfigured().getAny(), ActivationCapabilityType.class); - if (CapabilityUtil.getEffectiveActivationStatus(activationCapability) != null) { - QName attributeName = activationCapability.getStatus().getAttribute(); - Boolean ignore = activationCapability.getStatus().isIgnoreAttribute(); - if (attributeName != null) { - // The attribute used for enable/disable simulation should be ignored in the schema - // otherwise strange things may happen, such as changing the same attribute both from - // activation/enable and from the attribute using its native name. - for (ObjectClassComplexTypeDefinition objectClassDefinition : resourceSchema - .getDefinitions(ObjectClassComplexTypeDefinition.class)) { - ResourceAttributeDefinition attributeDefinition = objectClassDefinition - .findAttributeDefinition(attributeName); - if (attributeDefinition != null) { - if (ignore == null || ignore) { - ((MutableItemDefinition) attributeDefinition).setProcessing(ItemProcessing.IGNORE); - } - } else { - // simulated activation attribute points to something that is not in the schema - // technically, this is an error. But it looks to be quite common in connectors. - // The enable/disable is using operational attributes that are not exposed in the - // schema, but they work if passed to the connector. - // Therefore we don't want to break anything. We could log an warning here, but the - // warning would be quite frequent. Maybe a better place to warn user would be import - // of the object. - LOGGER.debug("Simulated activation attribute " - + attributeName - + " for objectclass " - + objectClassDefinition.getTypeName() - + " in " - + resource - + " does not exist in the resource schema. This may work well, but it is not clean. Connector exposing such schema should be fixed."); - } - } - } - } - } - - // TODO should this one be used? - private void checkSchema(PrismSchema schema) throws SchemaException { - // This is resource schema, it should contain only - // ResourceObjectDefinitions - for (Definition def : schema.getDefinitions()) { - if (def instanceof ComplexTypeDefinition) { - // This is OK - } else if (def instanceof ResourceAttributeContainerDefinition) { - checkResourceObjectDefinition((ResourceAttributeContainerDefinition) def); - } else { - throw new SchemaException("Unexpected definition in resource schema: " + def); - } - } - } - - private void checkResourceObjectDefinition(ResourceAttributeContainerDefinition rod) - throws SchemaException { - for (ItemDefinition def : rod.getDefinitions()) { - if (!(def instanceof ResourceAttributeDefinition)) { - throw new SchemaException("Unexpected definition in resource schema object " + rod + ": " - + def); - } - } - } - - public void applyDefinition(ObjectDelta delta, ResourceType resourceWhenNoOid, GetOperationOptions options, Task task, OperationResult objectResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { - - if (delta.isAdd()) { - PrismObject resource = delta.getObjectToAdd(); - applyConnectorSchemasToResource(resource, task, objectResult); - return; - - } else if (delta.isModify()) { - // Go on - } else { - return; - } - - if (delta.hasCompleteDefinition()){ - //nothing to do, all modifications has definitions..just aplly this deltas.. - return; - } - - - PrismObject resource; - String resourceOid = delta.getOid(); - if (resourceOid == null) { - Validate.notNull(resourceWhenNoOid, "Resource oid not specified in the object delta, and resource is not specified as well. Could not apply definition."); - resource = resourceWhenNoOid.asPrismObject(); - } else { - resource = getResource(resourceOid, options, task, objectResult); - } - - ResourceType resourceType = resource.asObjectable(); -// ResourceType resourceType = completeResource(resource.asObjectable(), null, objectResult); - //TODO TODO TODO FIXME FIXME FIXME copied from ObjectImprted..union this two cases - PrismContainer configurationContainer = ResourceTypeUtil.getConfigurationContainer(resourceType); - if (configurationContainer == null || configurationContainer.isEmpty()) { - // Nothing to check - objectResult.recordWarning("The resource has no configuration"); - return; - } - - // Check the resource configuration. The schema is in connector, so fetch the connector first - String connectorOid = resourceType.getConnectorRef().getOid(); - if (StringUtils.isBlank(connectorOid)) { - objectResult.recordFatalError("The connector reference (connectorRef) is null or empty"); - return; - } - - //ItemDelta.findItemDelta(delta.getModifications(), ResourceType.F_SCHEMA, ContainerDelta.class) == null || - - ReferenceDelta connectorRefDelta = ItemDeltaCollectionsUtil.findReferenceModification(delta.getModifications(), ResourceType.F_CONNECTOR_REF); - if (connectorRefDelta != null){ - Item connectorRefNew = connectorRefDelta.getItemNewMatchingPath(null); - if (connectorRefNew.getValues().size() == 1){ - PrismReferenceValue connectorRefValue = connectorRefNew.getValues().iterator().next(); - if (connectorRefValue.getOid() != null && !connectorOid.equals(connectorRefValue.getOid())){ - connectorOid = connectorRefValue.getOid(); - } - } - } - - PrismObject connector; - ConnectorType connectorType; - try { - connector = repositoryService.getObject(ConnectorType.class, connectorOid, null, objectResult); - connectorType = connector.asObjectable(); - } catch (ObjectNotFoundException e) { - // No connector, no fun. We can't check the schema. But this is referential integrity problem. - // Mark the error ... there is nothing more to do - objectResult.recordFatalError("Connector (OID:" + connectorOid + ") referenced from the resource is not in the repository", e); - return; - } catch (SchemaException e) { - // Probably a malformed connector. To be kind of robust, lets allow the import. - // Mark the error ... there is nothing more to do - objectResult.recordPartialError("Connector (OID:" + connectorOid + ") referenced from the resource has schema problems: " + e.getMessage(), e); - LOGGER.error("Connector (OID:{}) referenced from the imported resource \"{}\" has schema problems: {}-{}", - connectorOid, resourceType.getName(), e.getMessage(), e); - return; - } - - Element connectorSchemaElement = ConnectorTypeUtil.getConnectorXsdSchema(connector); - MutablePrismSchema connectorSchema; - if (connectorSchemaElement == null) { - // No schema to validate with - return; - } - try { - connectorSchema = prismContext.schemaFactory().createPrismSchema(DOMUtil.getSchemaTargetNamespace(connectorSchemaElement)); - connectorSchema.parseThis(connectorSchemaElement, true, "schema for " + connector, prismContext); - } catch (SchemaException e) { - objectResult.recordFatalError("Error parsing connector schema for " + connector + ": "+e.getMessage(), e); - return; - } - QName configContainerQName = new QName(connectorType.getNamespace(), ResourceType.F_CONNECTOR_CONFIGURATION.getLocalPart()); - PrismContainerDefinition configContainerDef = - connectorSchema.findContainerDefinitionByElementName(configContainerQName); - if (configContainerDef == null) { - objectResult.recordFatalError("Definition of configuration container " + configContainerQName + " not found in the schema of of " + connector); - return; - } - - try { - configurationContainer.applyDefinition(configContainerDef); - } catch (SchemaException e) { - objectResult.recordFatalError("Configuration error in " + resource + ": "+e.getMessage(), e); - return; - } - - PrismContainer configContainer = resourceType.asPrismObject().findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); - //noinspection unchecked - configContainer.applyDefinition(configContainerDef); - - for (ItemDelta itemDelta : delta.getModifications()){ - applyItemDefinition(itemDelta, configContainerDef, objectResult); - } - } - - private void applyItemDefinition(ItemDelta itemDelta, - PrismContainerDefinition configContainerDef, OperationResult objectResult) throws SchemaException { - if (itemDelta.getParentPath() == null){ - LOGGER.trace("No parent path defined for item delta {}", itemDelta); - return; - } - - QName first = itemDelta.getParentPath().firstToNameOrNull(); - if (first == null) { - return; - } - - if (itemDelta.getDefinition() == null && (ResourceType.F_CONNECTOR_CONFIGURATION.equals(first) || ResourceType.F_SCHEMA.equals(first))){ - ItemPath path = itemDelta.getPath().rest(); - D itemDef = configContainerDef.findItemDefinition(path); - if (itemDef == null){ - LOGGER.warn("No definition found for item {}. Check your namespaces?", path); - objectResult.recordWarning("No definition found for item delta: " + itemDelta +". Check your namespaces?" ); -// throw new SchemaException("No definition found for item " + path+ ". Check your namespaces?" ); - return; - } - itemDelta.applyDefinition(itemDef); - - } - } - - public void applyDefinition(PrismObject resource, Task task, OperationResult parentResult) - throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { - applyConnectorSchemasToResource(resource, task, parentResult); - } - - public void applyDefinition(ObjectQuery query, OperationResult result) { - // TODO: not implemented yet - } - - public Object executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException { - PrismObject resource = getResource(resourceOid, null, task, result); - ConnectorSpec connectorSpec = selectConnectorSpec(resource, ScriptCapabilityType.class); - if (connectorSpec == null) { - throw new UnsupportedOperationException("No connector supports script capability"); - } - ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); - ExecuteProvisioningScriptOperation scriptOperation = ProvisioningUtil.convertToScriptOperation(script, "script on "+resource, prismContext); - try { - StateReporter reporter = new StateReporter(resourceOid, task); - return connectorInstance.executeScript(scriptOperation, reporter, result); - } catch (GenericFrameworkException e) { - // Not expected. Transform to system exception - result.recordFatalError("Generic provisioning framework error", e); - throw new SystemException("Generic provisioning framework error: " + e.getMessage(), e); - } - } - - public List getConnectorOperationalStatus(PrismObject resource, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { - List statuses = new ArrayList<>(); - for (ConnectorSpec connectorSpec: getAllConnectorSpecs(resource)) { - ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); - ConnectorOperationalStatus operationalStatus = connectorInstance.getOperationalStatus(); - if (operationalStatus != null) { - operationalStatus.setConnectorName(connectorSpec.getConnectorName()); - statuses.add(operationalStatus); - } - } - return statuses; - } - - private List getAllConnectorSpecs(PrismObject resource) throws SchemaException { - List connectorSpecs = new ArrayList<>(); - connectorSpecs.add(getDefaultConnectorSpec(resource)); - for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { - connectorSpecs.add(getConnectorSpec(resource, additionalConnectorType)); - } - return connectorSpecs; - } - - // Should be used only internally (private). But it is public, because it is accessed from the tests. - public ConnectorInstance getConfiguredConnectorInstance(PrismObject resource, - Class operationCapabilityClass, boolean forceFresh, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { - ConnectorSpec connectorSpec = selectConnectorSpec(resource, operationCapabilityClass); - if (connectorSpec == null) { - return null; - } - return connectorManager.getConfiguredConnectorInstance(connectorSpec, forceFresh, parentResult); - } - - // Used by the tests. Does not change anything. - ConnectorInstance getConfiguredConnectorInstanceFromCache(PrismObject resource, - Class operationCapabilityClass) throws SchemaException { - ConnectorSpec connectorSpec = selectConnectorSpec(resource, operationCapabilityClass); - return connectorSpec != null ? connectorManager.getConfiguredConnectorInstanceFromCache(connectorSpec) : null; - } - - public CapabilitiesType getConnectorCapabilities(ResourceType resource, - RefinedObjectClassDefinition objectClassDefinition, Class operationCapabilityClass) { - if (resource == null) { - return null; - } - - CapabilitiesType connectorCapabilities = null; - for (ConnectorInstanceSpecificationType additionalConnectorType : resource.getAdditionalConnector()) { - if (supportsCapability(additionalConnectorType, operationCapabilityClass)) { - connectorCapabilities = additionalConnectorType.getCapabilities(); - } - } - - if (connectorCapabilities == null) { - connectorCapabilities = resource.getCapabilities(); - } - - - CapabilitiesType finalCapabilities = applyObjectClassCapabilities(connectorCapabilities, objectClassDefinition); - LOGGER.trace("Returning final capabilities:\n{} ", finalCapabilities); - return finalCapabilities; - - } - - private CapabilitiesType applyObjectClassCapabilities(CapabilitiesType connectorCapabilities, RefinedObjectClassDefinition objectClassDefinition) { - - if (objectClassDefinition == null) { - return connectorCapabilities; - } - - CapabilitiesType objectClassCapabilities = objectClassDefinition.getCapabilities(); - if (objectClassCapabilities == null) { - LOGGER.trace("No capabilities for {} specified, skipping merge.", objectClassDefinition); - return connectorCapabilities; - } - - CapabilityCollectionType configured = objectClassCapabilities.getConfigured(); - if (configured == null) { - LOGGER.trace("Empty capabilities in {} specified, skipping merge", objectClassDefinition); - return connectorCapabilities; - } - - CapabilitiesType finalCapabilities = new CapabilitiesType(); - if (connectorCapabilities.getNative() != null) { - finalCapabilities.setNative(connectorCapabilities.getNative()); - } - - if (!hasConfiguredCapabilities(connectorCapabilities)) { - LOGGER.trace("No configured capabilities found for connector, replacing with capabilities defined for {}", objectClassDefinition); - finalCapabilities.setConfigured(configured); - return finalCapabilities; - } - - for (Object capability : connectorCapabilities.getConfigured().getAny()) { - - if (!CapabilityUtil.containsCapabilityWithSameElementName(configured.getAny(), capability)) { - configured.getAny().add(capability); - } - } - - finalCapabilities.setConfigured(configured); - return finalCapabilities; - } - - private boolean hasConfiguredCapabilities(CapabilitiesType supportedCapabilities) { - CapabilityCollectionType configured = supportedCapabilities.getConfigured(); - return configured != null && !configured.getAny().isEmpty(); - } - - private ConnectorSpec selectConnectorSpec(PrismObject resource, Map> capabilityMap, Class capabilityClass) throws SchemaException { - if (capabilityMap == null) { - return selectConnectorSpec(resource, capabilityClass); - } - for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { - if (supportsCapability(additionalConnectorType, capabilityMap.get(additionalConnectorType.getName()), capabilityClass)) { - return getConnectorSpec(resource, additionalConnectorType); - } - } - return getDefaultConnectorSpec(resource); - } - - private ConnectorSpec selectConnectorSpec(PrismObject resource, Class operationCapabilityClass) throws SchemaException { - for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { - if (supportsCapability(additionalConnectorType, operationCapabilityClass)) { - return getConnectorSpec(resource, additionalConnectorType); - } - } - return getDefaultConnectorSpec(resource); - } - - private boolean supportsCapability(ConnectorInstanceSpecificationType additionalConnectorType, Class capabilityClass) { - T cap = CapabilityUtil.getEffectiveCapability(additionalConnectorType.getCapabilities(), capabilityClass); - if (cap == null) { - return false; - } - return CapabilityUtil.isCapabilityEnabled(cap); - } - - private boolean supportsCapability(ConnectorInstanceSpecificationType additionalConnectorType, Collection nativeCapabilities, Class capabilityClass) { - CapabilitiesType specifiedCapabilitiesType = additionalConnectorType.getCapabilities(); - if (specifiedCapabilitiesType != null) { - CapabilityCollectionType configuredCapCollectionType = specifiedCapabilitiesType.getConfigured(); - if (configuredCapCollectionType != null) { - T configuredCap = CapabilityUtil.getCapability(configuredCapCollectionType.getAny(), capabilityClass); - if (configuredCap != null && !CapabilityUtil.isCapabilityEnabled(configuredCap)) { - return false; - } - } - - } - T cap = CapabilityUtil.getCapability(nativeCapabilities, capabilityClass); - if (cap == null) { - return false; - } - return CapabilityUtil.isCapabilityEnabled(cap); - } - - private ConnectorSpec getDefaultConnectorSpec(PrismObject resource) { - PrismContainer connectorConfiguration = resource.findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); - return new ConnectorSpec(resource, null, ResourceTypeUtil.getConnectorOid(resource), connectorConfiguration); - } - - private ConnectorSpec getConnectorSpec(PrismObject resource, ConnectorInstanceSpecificationType additionalConnectorType) throws SchemaException { - if (additionalConnectorType.getConnectorRef() == null) { - throw new SchemaException("No connector reference in additional connector in "+resource); - } - String connectorOid = additionalConnectorType.getConnectorRef().getOid(); - if (StringUtils.isBlank(connectorOid)) { - throw new SchemaException("No connector OID in additional connector in "+resource); - } - //noinspection unchecked - PrismContainer connectorConfiguration = additionalConnectorType.asPrismContainerValue().findContainer(ConnectorInstanceSpecificationType.F_CONNECTOR_CONFIGURATION); - String connectorName = additionalConnectorType.getName(); - if (StringUtils.isBlank(connectorName)) { - throw new SchemaException("No connector name in additional connector in "+resource); - } - return new ConnectorSpec(resource, connectorName, connectorOid, connectorConfiguration); - } - - public PrismContext getPrismContext() { - return prismContext; - } -} +/* + * Copyright (c) 2010-2018 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.provisioning.impl; + +import java.util.*; + +import javax.xml.namespace.QName; + +import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition; +import com.evolveum.midpoint.common.refinery.RefinedResourceSchemaImpl; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.prism.delta.*; +import com.evolveum.midpoint.prism.path.ItemPath; +import com.evolveum.midpoint.prism.schema.MutablePrismSchema; +import com.evolveum.midpoint.schema.processor.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.SchemaDefinitionType; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.evolveum.midpoint.prism.query.ObjectQuery; +import com.evolveum.midpoint.prism.schema.PrismSchema; +import com.evolveum.midpoint.provisioning.api.GenericConnectorException; +import com.evolveum.midpoint.provisioning.ucf.api.ConnectorInstance; +import com.evolveum.midpoint.provisioning.ucf.api.ExecuteProvisioningScriptOperation; +import com.evolveum.midpoint.provisioning.ucf.api.GenericFrameworkException; +import com.evolveum.midpoint.provisioning.util.ProvisioningUtil; +import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.repo.common.expression.Expression; +import com.evolveum.midpoint.repo.common.expression.ExpressionEvaluationContext; +import com.evolveum.midpoint.repo.common.expression.ExpressionFactory; +import com.evolveum.midpoint.repo.common.expression.ExpressionVariables; +import com.evolveum.midpoint.schema.CapabilityUtil; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.constants.ConnectorTestOperation; +import com.evolveum.midpoint.schema.internals.InternalCounters; +import com.evolveum.midpoint.schema.internals.InternalMonitor; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.statistics.ConnectorOperationalStatus; +import com.evolveum.midpoint.schema.util.ConnectorTypeUtil; +import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.schema.util.ResourceTypeUtil; +import com.evolveum.midpoint.task.api.StateReporter; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.DOMUtil; +import com.evolveum.midpoint.util.DebugUtil; +import com.evolveum.midpoint.util.exception.CommunicationException; +import com.evolveum.midpoint.util.exception.ConfigurationException; +import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; +import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.util.exception.TunnelException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ActivationCapabilityType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.CapabilityType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.SchemaCapabilityType; +import com.evolveum.midpoint.xml.ns._public.resource.capabilities_3.ScriptCapabilityType; + +@Component +public class ResourceManager { + + @Autowired @Qualifier("cacheRepositoryService") + private RepositoryService repositoryService; + + @Autowired private ResourceCache resourceCache; + @Autowired private ConnectorManager connectorManager; + @Autowired private PrismContext prismContext; + @Autowired private ExpressionFactory expressionFactory; + @Autowired private ResourceOperationalStateManager operationalStateManager; + + private static final Trace LOGGER = TraceManager.getTrace(ResourceManager.class); + + private static final String OP_COMPLETE_RESOURCE = ResourceManager.class.getName() + ".completeResource"; + + /** + * Completes a resource that has been - we expect - just retrieved from the repository, usually by a search operation. + */ + PrismObject completeResource(PrismObject repositoryObject, GetOperationOptions options, Task task, + OperationResult parentResult) + throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { + + String oid = repositoryObject.getOid(); + boolean readonly = GetOperationOptions.isReadOnly(options); + + PrismObject cachedResource = resourceCache.get(oid, repositoryObject.getVersion(), readonly); + if (cachedResource != null) { + return cachedResource; + } else { + LOGGER.debug("Storing fetched resource {}, version {} to cache (previously cached version {})", + oid, repositoryObject.getVersion(), resourceCache.getVersion(oid)); + PrismObject mutableRepositoryObject = repositoryObject.cloneIfImmutable(); + return completeAndCacheResource(mutableRepositoryObject, options, task, parentResult); + } + } + + /** + * Gets a resource. + */ + public PrismObject getResource(String oid, GetOperationOptions options, Task task, OperationResult parentResult) + throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { + boolean readonly = GetOperationOptions.isReadOnly(options); + PrismObject cachedResource = resourceCache.getIfLatest(oid, readonly, parentResult); + if (cachedResource != null) { + LOGGER.trace("Returning resource from cache:\n{}", cachedResource.debugDumpLazily()); + return cachedResource; + } else { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Fetching resource {} and storing to cache (previously cached version {}) (options={})", + oid, resourceCache.getVersion(oid), options); + } + // We must obviously NOT fetch resource from repo as read-only. We are going to modify it. + PrismObject repositoryObject = readResourceFromRepository(oid, parentResult); + return completeAndCacheResource(repositoryObject, options, task, parentResult); + } + } + + private PrismObject readResourceFromRepository(String oid, OperationResult parentResult) + throws ObjectNotFoundException, SchemaException { + InternalMonitor.recordCount(InternalCounters.RESOURCE_REPOSITORY_READ_COUNT); + return repositoryService.getObject(ResourceType.class, oid, null, parentResult); + } + + /** + * Here we complete the resource and cache it. + * + * @param repositoryObject Up-to-date repository object. Must be mutable. + */ + private PrismObject completeAndCacheResource(PrismObject repositoryObject, + GetOperationOptions options, Task task, OperationResult parentResult) + throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { + + checkMutable(repositoryObject); + + PrismObject completedResource = completeResourceInternal(repositoryObject, null, + false, null, options, task, parentResult); + + logResourceAfterCompletion(completedResource); + if (!isComplete(completedResource)) { + // No not cache non-complete resources (e.g. those retrieved with noFetch) + LOGGER.debug("Not putting {} into cache because it's not complete", repositoryObject); + } else { + OperationResult completeResourceResult = parentResult.findSubresult(OP_COMPLETE_RESOURCE); + if (!completeResourceResult.isSuccess()) { + LOGGER.debug("Not putting {} into cache because the completeResource operation status is {}", + ObjectTypeUtil.toShortString(repositoryObject), completeResourceResult.getStatus()); + } else { + LOGGER.debug("Putting {} into cache", repositoryObject); + // Cache only resources that are completely OK + resourceCache.put(completedResource); + } + } + return completedResource; + } + + private void logResourceAfterCompletion(PrismObject completedResource) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Resource after completion, before putting into cache:\n{}", completedResource.debugDump()); + Element xsdSchemaElement = ResourceTypeUtil.getResourceXsdSchema(completedResource); + if (xsdSchemaElement == null) { + LOGGER.trace("Schema: null"); + } else { + LOGGER.trace("Schema:\n{}", + DOMUtil.serializeDOMToString(ResourceTypeUtil.getResourceXsdSchema(completedResource))); + } + } + } + + void deleteResource(String oid, OperationResult parentResult) throws ObjectNotFoundException { + resourceCache.remove(oid); + repositoryService.deleteObject(ResourceType.class, oid, parentResult); + } + + /** + * Make sure that the resource is complete. + * + * It will check if the resource has a sufficiently fresh schema, etc. + * + * Returned resource may be the same or may be a different instance, but it + * is guaranteed that it will be "fresher" and will correspond to the + * repository state (assuming that the provided resource also corresponded + * to the repository state). + * + * The connector schema that was fetched before can be supplied to this + * method. This is just an optimization. It comes handy e.g. in test + * connection case. + * + * Note: This is not really the best place for this method. Need to figure + * out correct place later. + * + * @param repoResource + * Resource to check + * @param resourceSchema + * schema that was freshly pre-fetched (or null) + * + * @return completed resource + */ + private PrismObject completeResourceInternal(PrismObject repoResource, ResourceSchema resourceSchema, + boolean fetchedSchema, Map> capabilityMap, GetOperationOptions options, Task task, OperationResult parentResult) + throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { + + checkMutable(repoResource); + + OperationResult result = parentResult.createMinorSubresult(OP_COMPLETE_RESOURCE); + try { + try { + applyConnectorSchemasToResource(repoResource, task, result); + } catch (Throwable t) { + String message = + "An error occurred while applying connector schema to connector configuration of " + repoResource + ": " + + t.getMessage(); + result.recordPartialError(message, t); + LOGGER.warn(message, t); + return repoResource; + } + + PrismObject newResource; + + if (isComplete(repoResource)) { + // The resource is complete. + newResource = repoResource; + + } else { + // The resource is NOT complete. Try to fetch schema and capabilities + + if (GetOperationOptions.isNoFetch(options)) { + // We need to fetch schema, but the noFetch option is specified. Therefore return whatever we have. + result.recordSuccessIfUnknown(); + return repoResource; + } + + try { + + completeSchemaAndCapabilities(repoResource, resourceSchema, fetchedSchema, capabilityMap, result); + + } catch (Exception ex) { + // Catch the exceptions. There are not critical. We need to catch them all because the connector may + // throw even undocumented runtime exceptions. + // Even non-complete resource may still be usable. The fetchResult indicates that there was an error + result.recordPartialError("Cannot complete resource schema and capabilities: " + ex.getMessage(), ex); + return repoResource; + } + + try { + // Now we need to re-read the resource from the repository and re-apply the schemas. This ensures that we will + // cache the correct version and that we avoid race conditions, etc. + + newResource = readResourceFromRepository(repoResource.getOid(), result); + applyConnectorSchemasToResource(newResource, task, result); + + } catch (SchemaException | ObjectNotFoundException | RuntimeException e) { + result.recordFatalError(e); + throw e; + } + } + + try { + // make sure it has parsed resource and refined schema. We are going to cache + // it, so we want to cache it with the parsed schemas + RefinedResourceSchemaImpl.getResourceSchema(newResource, prismContext); + RefinedResourceSchemaImpl.getRefinedSchema(newResource); + + } catch (SchemaException e) { + String message = "Schema error while processing schemaHandling section of " + newResource + ": " + e.getMessage(); + result.recordPartialError(message, e); + LOGGER.warn(message, e); + return newResource; + } catch (RuntimeException e) { + String message = + "Unexpected error while processing schemaHandling section of " + newResource + ": " + e.getMessage(); + result.recordPartialError(message, e); + LOGGER.warn(message, e); + return newResource; + } + + result.recordSuccessIfUnknown(); + + return newResource; + } catch (Throwable t) { + result.recordFatalError(t); + throw t; + } finally { + result.computeStatusIfUnknown(); + } + } + + private boolean isComplete(PrismObject resource) { + ResourceType resourceType = resource.asObjectable(); + if (ResourceTypeUtil.getResourceXsdSchema(resource) == null) { + return false; + } + CapabilitiesType capabilitiesType = resourceType.getCapabilities(); + return capabilitiesType != null && capabilitiesType.getCachingMetadata() != null; + } + + + private void completeSchemaAndCapabilities(PrismObject resource, ResourceSchema resourceSchema, boolean fetchedSchema, + Map> capabilityMap, OperationResult result) + throws SchemaException, CommunicationException, ObjectNotFoundException, GenericFrameworkException, ConfigurationException { + + Collection> modifications = new ArrayList<>(); + + // Capabilities + // we need to process capabilities first. Schema is one of the connector capabilities. + // We need to determine this capability to select the right connector for schema retrieval. + completeCapabilities(resource, capabilityMap != null, capabilityMap, modifications, result); + + if (resourceSchema == null) { + // Try to get existing schema from resource. We do not want to override this if it exists + // (but we still want to refresh the capabilities, that happens below) + resourceSchema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); + } + + if (resourceSchema == null || resourceSchema.isEmpty()) { + + LOGGER.trace("Fetching resource schema for {}", resource); + + resourceSchema = fetchResourceSchema(resource, capabilityMap, result); + + if (resourceSchema == null) { + LOGGER.warn("No resource schema fetched from {}", resource); + } else if (resourceSchema.isEmpty()) { + LOGGER.warn("Empty resource schema fetched from {}", resource); + } else { + LOGGER.debug("Fetched resource schema for {}: {} definitions", resource, resourceSchema.getDefinitions().size()); + fetchedSchema = true; + } + } + + if (resourceSchema != null) { + if (fetchedSchema) { + adjustSchemaForSimulatedCapabilities(resource, resourceSchema); + modifications.add(createSchemaUpdateDelta(resource, resourceSchema)); + AvailabilityStatusType previousStatus = ResourceTypeUtil.getLastAvailabilityStatus(resource.asObjectable()); + if (previousStatus != AvailabilityStatusType.UP) { + modifications.addAll(operationalStateManager.createAndLogOperationalStateDeltas(previousStatus, + AvailabilityStatusType.UP, resource.toString(), + "resource schema was successfully fetched", resource)); + } else { + // just for sure (if the status changed in the meanwhile) + modifications.add(operationalStateManager.createAvailabilityStatusDelta(AvailabilityStatusType.UP)); + } + } else { + CachingMetadataType schemaCachingMetadata = resource.asObjectable().getSchema().getCachingMetadata(); + if (schemaCachingMetadata == null) { + schemaCachingMetadata = MiscSchemaUtil.generateCachingMetadata(); + modifications.add( + prismContext.deltaFactory().property().createModificationReplaceProperty( + ItemPath.create(ResourceType.F_SCHEMA, CapabilitiesType.F_CACHING_METADATA), + resource.getDefinition(), + schemaCachingMetadata) + ); + } + } + } + + if (!modifications.isEmpty()) { + try { + LOGGER.trace("Completing {}:\n{}", resource, DebugUtil.debugDumpLazily(modifications, 1)); + repositoryService.modifyObject(ResourceType.class, resource.getOid(), modifications, result); + InternalMonitor.recordCount(InternalCounters.RESOURCE_REPOSITORY_MODIFY_COUNT); + } catch (ObjectAlreadyExistsException ex) { + // This should not happen + throw new SystemException(ex); + } + } + } + + private void completeCapabilities(PrismObject resource, boolean forceRefresh, Map> capabilityMap, Collection> modifications, + OperationResult result) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { + ResourceType resourceType = resource.asObjectable(); + ConnectorSpec defaultConnectorSpec = getDefaultConnectorSpec(resource); + CapabilitiesType resourceCapType = resourceType.getCapabilities(); + if (resourceCapType == null) { + resourceCapType = new CapabilitiesType(); + resourceType.setCapabilities(resourceCapType); + } + completeConnectorCapabilities(defaultConnectorSpec, resourceCapType, ResourceType.F_CAPABILITIES, forceRefresh, + capabilityMap==null?null:capabilityMap.get(null), + modifications, result); + + for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { + ConnectorSpec connectorSpec = getConnectorSpec(resource, additionalConnectorType); + CapabilitiesType connectorCapType = additionalConnectorType.getCapabilities(); + if (connectorCapType == null) { + connectorCapType = new CapabilitiesType(); + additionalConnectorType.setCapabilities(connectorCapType); + } + ItemPath itemPath = additionalConnectorType.asPrismContainerValue().getPath().append(ConnectorInstanceSpecificationType.F_CAPABILITIES); + completeConnectorCapabilities(connectorSpec, connectorCapType, itemPath, forceRefresh, + capabilityMap==null?null:capabilityMap.get(additionalConnectorType.getName()), + modifications, result); + } + } + + private void completeConnectorCapabilities(ConnectorSpec connectorSpec, CapabilitiesType capType, ItemPath itemPath, boolean forceRefresh, + Collection retrievedCapabilities, Collection> modifications, OperationResult result) + throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { + + if (capType.getNative() != null && !capType.getNative().getAny().isEmpty()) { + if (!forceRefresh) { + CachingMetadataType cachingMetadata = capType.getCachingMetadata(); + if (cachingMetadata == null) { + cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); + modifications.add( + prismContext.deltaFactory().property().createModificationReplaceProperty( + ItemPath.create(ResourceType.F_CAPABILITIES, CapabilitiesType.F_CACHING_METADATA), + connectorSpec.getResource().getDefinition(), + cachingMetadata) + ); + } + return; + } + } + + if (retrievedCapabilities == null) { + try { + + InternalMonitor.recordCount(InternalCounters.CONNECTOR_CAPABILITIES_FETCH_COUNT); + + ConnectorInstance connector = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); + retrievedCapabilities = connector.fetchCapabilities(result); + + } catch (GenericFrameworkException e) { + throw new GenericConnectorException("Generic error in connector " + connectorSpec + ": " + + e.getMessage(), e); + } + } + + CapabilityCollectionType nativeCapType = new CapabilityCollectionType(); + capType.setNative(nativeCapType); + nativeCapType.getAny().addAll(retrievedCapabilities); + + CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); + capType.setCachingMetadata(cachingMetadata); + + //noinspection unchecked + ObjectDelta capabilitiesReplaceDelta = prismContext.deltaFactory().object() + .createModificationReplaceContainer(ResourceType.class, connectorSpec.getResource().getOid(), + itemPath, capType.asPrismContainerValue().clone()); + + modifications.addAll(capabilitiesReplaceDelta.getModifications()); + } + + private ContainerDelta createSchemaUpdateDelta(PrismObject resource, ResourceSchema resourceSchema) throws SchemaException { + Document xsdDoc; + try { + xsdDoc = resourceSchema.serializeToXsd(); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Serialized XSD resource schema for {}:\n{}", resource, DOMUtil.serializeDOMToString(xsdDoc)); + } + } catch (SchemaException e) { + throw new SchemaException("Error processing resource schema for " + resource + ": " + e.getMessage(), e); + } + + Element xsdElement = DOMUtil.getFirstChildElement(xsdDoc); + if (xsdElement == null) { + throw new SchemaException("No schema was generated for " + resource); + } + CachingMetadataType cachingMetadata = MiscSchemaUtil.generateCachingMetadata(); + + ContainerDelta schemaContainerDelta = prismContext.deltaFactory().container().createDelta( + ResourceType.F_SCHEMA, ResourceType.class); + PrismContainerValue cval = prismContext.itemFactory().createContainerValue(); + schemaContainerDelta.setValueToReplace(cval); + PrismProperty cachingMetadataProperty = cval + .createProperty(XmlSchemaType.F_CACHING_METADATA); + cachingMetadataProperty.setRealValue(cachingMetadata); + List objectClasses = ResourceTypeUtil.getSchemaGenerationConstraints(resource); + if (objectClasses != null) { + PrismProperty generationConstraints = cval + .createProperty(XmlSchemaType.F_GENERATION_CONSTRAINTS); + SchemaGenerationConstraintsType constraints = new SchemaGenerationConstraintsType(); + constraints.getGenerateObjectClass().addAll(objectClasses); + generationConstraints.setRealValue(constraints); + } + PrismProperty definitionProperty = cval.createProperty(XmlSchemaType.F_DEFINITION); + ObjectTypeUtil.setXsdSchemaDefinition(definitionProperty, xsdElement); + + return schemaContainerDelta; + } + + /** + * Apply proper definition (connector schema) to the resource. + */ + private void applyConnectorSchemasToResource(PrismObject resource, Task task, OperationResult result) + throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { + checkMutable(resource); + PrismObjectDefinition newResourceDefinition = resource.getDefinition().clone(); + for (ConnectorSpec connectorSpec : getAllConnectorSpecs(resource)) { + try { + applyConnectorSchemaToResource(connectorSpec, newResourceDefinition, resource, task, result); + } catch (CommunicationException | ConfigurationException | SecurityViolationException e) { + throw new IllegalStateException("Unexpected exception: " + e.getMessage(), e); // fixme temporary solution + } + } + resource.setDefinition(newResourceDefinition); + } + + /** + * Apply proper definition (connector schema) to the resource. + */ + private void applyConnectorSchemaToResource(ConnectorSpec connectorSpec, PrismObjectDefinition resourceDefinition, + PrismObject resource, Task task, OperationResult result) + throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + + ConnectorType connectorType = connectorManager.getConnector(connectorSpec, result); + PrismSchema connectorSchema = connectorManager.getAttachedConnectorSchema(connectorType); + PrismContainerDefinition configurationContainerDefinition = ConnectorTypeUtil + .findConfigurationContainerDefinition(connectorType, connectorSchema); + if (configurationContainerDefinition == null) { + throw new SchemaException("No configuration container definition in schema of " + connectorType); + } + + configurationContainerDefinition = configurationContainerDefinition.clone(); + PrismContainer configurationContainer = connectorSpec.getConnectorConfiguration(); + // We want element name, minOccurs/maxOccurs and similar definition to be taken from the original, not the schema + // the element is global in the connector schema. therefore it does not have correct maxOccurs + if (configurationContainer != null) { + configurationContainerDefinition.adoptElementDefinitionFrom(configurationContainer.getDefinition()); + configurationContainer.applyDefinition(configurationContainerDefinition, true); + + try { + //noinspection unchecked + configurationContainer.accept(visitable -> { + if ((visitable instanceof PrismProperty)) { + try { + evaluateExpression((PrismProperty)visitable, resource, task, result); + } catch (SchemaException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | ConfigurationException | SecurityViolationException e) { + throw new TunnelException(e); + } + } + // TODO treat configuration items that are containers themselves + }); + } catch (TunnelException te) { + Throwable e = te.getCause(); + if (e instanceof SchemaException) { + throw (SchemaException)e; + } else if (e instanceof ObjectNotFoundException) { + throw (ObjectNotFoundException)e; + } else if (e instanceof ExpressionEvaluationException) { + throw (ExpressionEvaluationException)e; + } else if (e instanceof CommunicationException) { + throw (CommunicationException)e; + } else if (e instanceof ConfigurationException) { + throw (ConfigurationException)e; + } else if (e instanceof SecurityViolationException) { + throw (SecurityViolationException)e; + } else if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } else if (e instanceof Error) { + throw (Error)e; + } else { + throw new SystemException(e); + } + } + + } else { + configurationContainerDefinition.adoptElementDefinitionFrom( + resourceDefinition.findContainerDefinition(ResourceType.F_CONNECTOR_CONFIGURATION)); + } + + if (connectorSpec.getConnectorName() == null) { + // Default connector, for compatibility + // It does not make sense to update this for any other connectors. + // We cannot have one definition for additionalConnector[1]/connectorConfiguration and + // different definition for additionalConnector[2]/connectorConfiguration in the object definition. + // The way to go is to set up definitions on the container level. + resourceDefinition.replaceDefinition(ResourceType.F_CONNECTOR_CONFIGURATION, configurationContainerDefinition); + } + + } + + private void evaluateExpression(PrismProperty configurationProperty, PrismObject resource, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException { + PrismPropertyDefinition propDef = configurationProperty.getDefinition(); + String shortDesc = "connector configuration property "+configurationProperty+" in "+resource; + List> extraValues = new ArrayList<>(); + for (PrismPropertyValue configurationPropertyValue: configurationProperty.getValues()) { + ExpressionWrapper expressionWrapper = configurationPropertyValue.getExpression(); + if (expressionWrapper == null) { + return; + } + Object expressionObject = expressionWrapper.getExpression(); + if (!(expressionObject instanceof ExpressionType)) { + throw new IllegalStateException("Expected that expression in "+configurationPropertyValue+" will be ExpressionType, but it was "+expressionObject); + } + ExpressionType expressionType = (ExpressionType) expressionWrapper.getExpression(); + + Expression, PrismPropertyDefinition> expression = expressionFactory.makeExpression(expressionType, propDef, MiscSchemaUtil.getExpressionProfile(), shortDesc, task, result); + ExpressionVariables variables = new ExpressionVariables(); + + // TODO: populate variables + + ExpressionEvaluationContext expressionContext = new ExpressionEvaluationContext(null, variables, shortDesc, task); + PrismValueDeltaSetTriple> expressionOutputTriple = expression.evaluate(expressionContext, + result); + Collection> expressionOutputValues = expressionOutputTriple.getNonNegativeValues(); + if (!expressionOutputValues.isEmpty()) { + Iterator> iterator = expressionOutputValues.iterator(); + PrismPropertyValue firstValue = iterator.next(); + configurationPropertyValue.setValue(firstValue.getValue()); + while (iterator.hasNext()) { + extraValues.add(iterator.next()); + } + } + } + for (PrismPropertyValue extraValue: extraValues) { + configurationProperty.add(extraValue); + } + } + + private ResourceSchema fetchResourceSchema(PrismObject resource, Map> capabilityMap, OperationResult parentResult) + throws CommunicationException, GenericFrameworkException, ConfigurationException, ObjectNotFoundException, SchemaException { + ConnectorSpec connectorSpec = selectConnectorSpec(resource, capabilityMap, SchemaCapabilityType.class); + if (connectorSpec == null) { + LOGGER.debug("No connector has schema capability, cannot fetch resource schema"); + return null; + } + InternalMonitor.recordCount(InternalCounters.RESOURCE_SCHEMA_FETCH_COUNT); + List generateObjectClasses = ResourceTypeUtil.getSchemaGenerationConstraints(resource); + ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, parentResult); + + LOGGER.debug("Trying to get schema from {}, objectClasses to generate: {}", connectorSpec, generateObjectClasses); + ResourceSchema resourceSchema = connectorInstance.fetchResourceSchema(parentResult); + + if (ResourceTypeUtil.isValidateSchema(resource.asObjectable())) { + ResourceTypeUtil.validateSchema(resourceSchema, resource); + } + return resourceSchema; + + } + + /** + * Test the connection. + * + * @param resource Resource object as fetched from the repository. Must NOT be immutable! + * + * @throws ObjectNotFoundException If the resource object cannot be found in repository (e.g. when trying to set its + * availability status). + */ + public void testConnection(PrismObject resource, Task task, OperationResult parentResult) + throws ObjectNotFoundException { + + checkMutable(resource); + + String resourceOid = resource.getOid(); + + String operationDesc = "test resource " + resourceOid + "connection"; + + List allConnectorSpecs; + try { + allConnectorSpecs = getAllConnectorSpecs(resource); + } catch (SchemaException e) { + String statusChangeReason = operationDesc + ", getting all connectors failed: " + e.getMessage(); + if (LOGGER.isTraceEnabled()) { + LOGGER.error("Configuration error: {}", e.getMessage(), e); + } + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + parentResult.recordFatalError("Configuration error: " + e.getMessage(), e); + return; + } + + Map> capabilityMap = new HashMap<>(); + for (ConnectorSpec connectorSpec: allConnectorSpecs) { + + OperationResult connectorTestResult = parentResult + .createSubresult(ConnectorTestOperation.CONNECTOR_TEST.getOperation()); + connectorTestResult.addParam(OperationResult.PARAM_NAME, connectorSpec.getConnectorName()); + connectorTestResult.addParam(OperationResult.PARAM_OID, connectorSpec.getConnectorOid()); + + testConnectionConnector(connectorSpec, capabilityMap, task, connectorTestResult); + + connectorTestResult.computeStatus(); + + if (!connectorTestResult.isAcceptable()) { + //nothing more to do.. if it failed while testing connection, status is set. + // we do not need to continue and waste the time. + return; + } + } + + // === test SCHEMA === + + OperationResult schemaResult = parentResult.createSubresult(ConnectorTestOperation.RESOURCE_SCHEMA.getOperation()); + + ResourceSchema schema; + try { + + schema = fetchResourceSchema(resource, capabilityMap, schemaResult); + + } catch (CommunicationException e) { + String statusChangeReason = operationDesc + " failed while fetching schema: " + e.getMessage(); + if (LOGGER.isTraceEnabled()) { + LOGGER.error("Communication error: {}", e.getMessage(), e); + } + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError("Communication error: " + e.getMessage(), e); + return; + } catch (GenericFrameworkException | ConfigurationException | ObjectNotFoundException | SchemaException | RuntimeException e) { + String statusChangeReason = operationDesc + " failed while fetching schema: " + e.getMessage(); + if (LOGGER.isTraceEnabled()) { + LOGGER.error("Error: {}", e.getMessage(), e); + } + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError("Error: " + e.getMessage(), e); + return; + } + + if (schema == null || schema.isEmpty()) { + // Resource does not support schema + // If there is a static schema in resource definition this may still be OK + try { + schema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); + } catch (SchemaException e) { + String statusChangeReason = operationDesc + " failed while parsing refined schema: " + e.getMessage(); + if (LOGGER.isTraceEnabled()) { + LOGGER.error("Error: {}", e.getMessage(), e); + } + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError(e); + return; + } + + if (schema == null || schema.isEmpty()) { + String msg = "Connector does not support schema and no static schema available"; + String statusChangeReason = operationDesc + ". " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError(msg); + return; + } + } + + // Invoke completeResource(). This will store the fetched schema to the ResourceType if there is no + // definition already. Therefore the testResource() can be used to generate the resource schema - until we + // have full schema caching capability. + PrismObject completedResource; + try { + // Re-fetching from repository to get up-to-date availability status (to avoid phantom state change records). + PrismObject repoResource = repositoryService.getObject(ResourceType.class, resourceOid, null, schemaResult); + completedResource = completeResourceInternal(repoResource, schema, true, capabilityMap, null, task, schemaResult); + } catch (ObjectNotFoundException e) { + String msg = "Object not found (unexpected error, probably a bug): " + e.getMessage(); + String statusChangeReason = operationDesc + " failed while completing resource. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError(msg, e); + return; + } catch (SchemaException e) { + String msg = "Schema processing error (probably connector bug): " + e.getMessage(); + String statusChangeReason = operationDesc + " failed while completing resource. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError(msg, e); + return; + } catch (ExpressionEvaluationException e) { + String msg = "Expression error: " + e.getMessage(); + String statusChangeReason = operationDesc + " failed while completing resource. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError(msg, e); + return; + } catch (RuntimeException e) { + String msg = "Unspecified exception: " + e.getMessage(); + String statusChangeReason = operationDesc + " failed while completing resource. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + schemaResult.recordFatalError(msg, e); + return; + } + + schemaResult.recordSuccess(); + + try { + updateResourceSchema(allConnectorSpecs, parentResult, completedResource); + } catch (SchemaException | ObjectNotFoundException | CommunicationException | ConfigurationException | RuntimeException e) { + String statusChangeReason = operationDesc + " failed while updating resource schema: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, statusChangeReason, task, parentResult, true); + parentResult.recordFatalError("Couldn't update resource schema: " + e.getMessage(), e); + //noinspection UnnecessaryReturnStatement + return; + } + + // TODO: connector sanity (e.g. refined schema, at least one account type, identifiers + // in schema, etc.) + + } + + private void checkMutable(PrismObject resource) { + if (resource.isImmutable()) { + throw new IllegalArgumentException("Got immutable resource object, while expecting mutable one: " + resource); + } + } + + private void updateResourceSchema(List allConnectorSpecs, OperationResult parentResult, + PrismObject resource) + throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { + ResourceSchema resourceSchema = RefinedResourceSchemaImpl.getResourceSchema(resource, prismContext); + if (resourceSchema != null) { + for (ConnectorSpec connectorSpec : allConnectorSpecs) { + ConnectorInstance instance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, parentResult); + instance.updateSchema(resourceSchema); + } + } + } + + private void testConnectionConnector(ConnectorSpec connectorSpec, Map> capabilityMap, Task task, + OperationResult parentResult) throws ObjectNotFoundException { + + // === test INITIALIZATION === + + OperationResult initResult = parentResult + .createSubresult(ConnectorTestOperation.CONNECTOR_INITIALIZATION.getOperation()); + + LOGGER.debug("Testing connection using {}", connectorSpec); + String resourceOid = connectorSpec.getResource().getOid(); + + String operationCtx = "testing connection using " + connectorSpec; + + ConfiguredConnectorInstanceEntry connectorInstanceCacheEntry; + try { + // Make sure we are getting non-configured instance. + connectorInstanceCacheEntry = connectorManager.getConnectorInstanceCacheEntry(connectorSpec, initResult); + initResult.recordSuccess(); + } catch (ObjectNotFoundException e) { + // The connector was not found. The resource definition is either + // wrong or the connector is not installed. + String msg = "The connector was not found: "+e.getMessage(); + operationCtx += " failed while getting connector instance. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + initResult.recordFatalError(msg, e); + return; + } catch (SchemaException e) { + String msg = "Schema error while dealing with the connector definition: "+e.getMessage(); + operationCtx += " failed while getting connector instance. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + initResult.recordFatalError(msg, e); + return; + } catch (RuntimeException | Error e) { + String msg = "Unexpected runtime error: "+e.getMessage(); + operationCtx += " failed while getting connector instance. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + initResult.recordFatalError(msg, e); + return; + } catch (CommunicationException e) { + String msg = "Communication error: "+e.getMessage(); + operationCtx += " failed while getting connector instance. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); + initResult.recordFatalError(msg, e); + return; + } catch (ConfigurationException e) { + String msg = "Configuration error: "+e.getMessage(); + operationCtx += " failed while getting connector instance. " + msg; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + initResult.recordFatalError(msg, e); + return; + } + + ConnectorInstance connector = connectorInstanceCacheEntry.getConnectorInstance(); + + + // === test CONFIGURATION === + + OperationResult configResult = parentResult + .createSubresult(ConnectorTestOperation.CONNECTOR_CONFIGURATION.getOperation()); + + try { + PrismObject resource = connectorSpec.getResource(); + PrismObjectDefinition newResourceDefinition = resource.getDefinition().clone(); + applyConnectorSchemaToResource(connectorSpec, newResourceDefinition, resource, task, configResult); + PrismContainerValue connectorConfiguration = connectorSpec.getConnectorConfiguration().getValue(); + + InternalMonitor.recordCount(InternalCounters.CONNECTOR_INSTANCE_CONFIGURATION_COUNT); + + connector.configure(connectorConfiguration, ResourceTypeUtil.getSchemaGenerationConstraints(resource), configResult); + + // We need to explicitly initialize the instance, e.g. in case that the schema and capabilities + // cannot be detected by the connector and therefore are provided in the resource + // + // NOTE: the capabilities and schema that are used here are NOT necessarily those that are detected by the resource. + // The detected schema will come later. The schema here is the one that is stored in the resource + // definition (ResourceType). This may be schema that was detected previously. But it may also be a schema + // that was manually defined. This is needed to be passed to the connector in case that the connector + // cannot detect the schema and needs schema/capabilities definition to establish a connection. + // Most connectors will just ignore the schema and capabilities that are provided here. + // But some connectors may need it (e.g. CSV connector working with CSV file without a header). + // + ResourceSchema previousResourceSchema = RefinedResourceSchemaImpl.getResourceSchema(connectorSpec.getResource(), prismContext); + Collection previousCapabilities = ResourceTypeUtil.getNativeCapabilitiesCollection(connectorSpec.getResource().asObjectable()); + connector.initialize(previousResourceSchema, previousCapabilities, + ResourceTypeUtil.isCaseIgnoreAttributeNames(connectorSpec.getResource().asObjectable()), configResult); + + configResult.recordSuccess(); + } catch (CommunicationException e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Communication error", e); + return; + } catch (GenericFrameworkException e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Generic error", e); + return; + } catch (SchemaException e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Schema error", e); + return; + } catch (ConfigurationException e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Configuration error", e); + return; + } catch (ObjectNotFoundException e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Object not found", e); + return; + } catch (ExpressionEvaluationException e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Expression error", e); + return; + } catch (SecurityViolationException e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Security violation", e); + return; + } catch (RuntimeException | Error e) { + operationCtx += " failed while testing configuration: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + configResult.recordFatalError("Unexpected runtime error", e); + return; + } + + // === test CONNECTION === + + // delegate the main part of the test to the connector + connector.test(parentResult); + + parentResult.computeStatus(); + if (!parentResult.isAcceptable()) { + operationCtx += ". Connector test failed: " + parentResult.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); + // No point in going on. Following tests will fail anyway, they will + // just produce misleading + // messages. + return; + } else { + operationCtx += ". Connector test successful."; + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.UP, operationCtx, task, parentResult, false); + } + + // === test CAPABILITIES === + + OperationResult capabilitiesResult = parentResult + .createSubresult(ConnectorTestOperation.CONNECTOR_CAPABILITIES.getOperation()); + try { + InternalMonitor.recordCount(InternalCounters.CONNECTOR_CAPABILITIES_FETCH_COUNT); + Collection retrievedCapabilities = connector.fetchCapabilities(capabilitiesResult); + + capabilityMap.put(connectorSpec.getConnectorName(), retrievedCapabilities); + capabilitiesResult.recordSuccess(); + } catch (CommunicationException e) { + operationCtx += " failed while testing capabilities: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.DOWN, operationCtx, task, parentResult, true); + capabilitiesResult.recordFatalError("Communication error", e); + return; + } catch (GenericFrameworkException e) { + operationCtx += " failed while testing capabilities: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + capabilitiesResult.recordFatalError("Generic error", e); + return; + } catch (ConfigurationException e) { + operationCtx += " failed while testing capabilities: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + capabilitiesResult.recordFatalError("Configuration error", e); + return; + } catch (SchemaException e) { + operationCtx += " failed while testing capabilities: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + capabilitiesResult.recordFatalError("Schema error", e); + return; + } catch (RuntimeException | Error e) { + operationCtx += " failed while testing capabilities: " + e.getMessage(); + modifyResourceAvailabilityStatus(resourceOid, AvailabilityStatusType.BROKEN, operationCtx, task, parentResult, true); + capabilitiesResult.recordFatalError("Unexpected runtime error", e); + return; + } + + // Connector instance is fully configured at this point. + // But the connector cache entry may not be set up properly and it is not yet placed into the cache. + // Therefore make sure the caching bit is completed. + // Place the connector to cache even if it was configured at the beginning. The connector is reconfigured now. + connectorManager.cacheConfiguredConnector(connectorInstanceCacheEntry, connectorSpec); + } + + /** + * Modifies resource availability status in the repository (if needed). + * + * The necessity of status modification is determined against the current version of the resource - unless "skipGetResource" + * is set. The resource is hopefully cached ResourceCache, so the performance impact should be almost ponone. + * + * Also note that in-memory representation of the resource is not modified. As a side effect, the cached resource + * is invalidated because of the modification. But it will be loaded on the next occasion. This should be quite harmless, + * as we do not expect availability changes to occur frequently. + * + * @param statusChangeReason Description of the reason of changing the availability status. + * @param skipGetResource True if we want to skip "getResource" operation and therefore apply the change regardless of + * the current resource availability status. This is to be used in situations where we expect that + * the resource might not be successfully retrievable (e.g. if it's broken). + * + * @throws ObjectNotFoundException If the resource object does not exist in repository. + */ + public void modifyResourceAvailabilityStatus(String resourceOid, AvailabilityStatusType newStatus, String statusChangeReason, + Task task, OperationResult result, boolean skipGetResource) throws ObjectNotFoundException { + + AvailabilityStatusType currentStatus; + String resourceDesc; + PrismObject resource; + if (skipGetResource) { + resource = null; + currentStatus = null; + resourceDesc = "resource " + resourceOid; + } else { + try { + resource = getResource(resourceOid, GetOperationOptions.createNoFetch(), task, result); + } catch (SchemaException | ExpressionEvaluationException e) { + // We actually do not expect any of these exceptions here. The resource is most probably in use + result.recordFatalError("Unexpected exception: " + e.getMessage(), e); + throw new SystemException("Unexpected exception: " + e.getMessage(), e); + } + ResourceType resourceBean = resource.asObjectable(); + currentStatus = ResourceTypeUtil.getLastAvailabilityStatus(resourceBean); + resourceDesc = resource.toString(); + } + + if (newStatus != currentStatus) { + try { + List> modifications = operationalStateManager.createAndLogOperationalStateDeltas(currentStatus, newStatus, + resourceDesc, statusChangeReason, resource); + repositoryService.modifyObject(ResourceType.class, resourceOid, modifications, result); + result.computeStatusIfUnknown(); + InternalMonitor.recordCount(InternalCounters.RESOURCE_REPOSITORY_MODIFY_COUNT); + } catch (SchemaException | ObjectAlreadyExistsException e) { + throw new SystemException("Unexpected exception while recording operation state change: " + e.getMessage(), e); + } + } + } + + /** + * Adjust scheme with respect to capabilities. E.g. disable attributes that + * are used for special purpose (such as account activation simulation). + * + * TODO treat also objectclass-specific capabilities here + */ + private void adjustSchemaForSimulatedCapabilities(PrismObject resource, ResourceSchema resourceSchema) { + ResourceType resourceType = resource.asObjectable(); + if (resourceType.getCapabilities() == null || resourceType.getCapabilities().getConfigured() == null) { + return; + } + ActivationCapabilityType activationCapability = CapabilityUtil.getCapability(resourceType + .getCapabilities().getConfigured().getAny(), ActivationCapabilityType.class); + if (CapabilityUtil.getEffectiveActivationStatus(activationCapability) != null) { + QName attributeName = activationCapability.getStatus().getAttribute(); + Boolean ignore = activationCapability.getStatus().isIgnoreAttribute(); + if (attributeName != null) { + // The attribute used for enable/disable simulation should be ignored in the schema + // otherwise strange things may happen, such as changing the same attribute both from + // activation/enable and from the attribute using its native name. + for (ObjectClassComplexTypeDefinition objectClassDefinition : resourceSchema + .getDefinitions(ObjectClassComplexTypeDefinition.class)) { + ResourceAttributeDefinition attributeDefinition = objectClassDefinition + .findAttributeDefinition(attributeName); + if (attributeDefinition != null) { + if (ignore == null || ignore) { + ((MutableItemDefinition) attributeDefinition).setProcessing(ItemProcessing.IGNORE); + } + } else { + // simulated activation attribute points to something that is not in the schema + // technically, this is an error. But it looks to be quite common in connectors. + // The enable/disable is using operational attributes that are not exposed in the + // schema, but they work if passed to the connector. + // Therefore we don't want to break anything. We could log an warning here, but the + // warning would be quite frequent. Maybe a better place to warn user would be import + // of the object. + LOGGER.debug("Simulated activation attribute " + + attributeName + + " for objectclass " + + objectClassDefinition.getTypeName() + + " in " + + resource + + " does not exist in the resource schema. This may work well, but it is not clean. Connector exposing such schema should be fixed."); + } + } + } + } + } + + public void applyDefinition(ObjectDelta delta, ResourceType resourceWhenNoOid, GetOperationOptions options, Task task, OperationResult objectResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException { + + if (delta.isAdd()) { + PrismObject resource = delta.getObjectToAdd(); + applyConnectorSchemasToResource(resource, task, objectResult); + return; + + } else if (delta.isModify()) { + // Go on + } else { + return; + } + + if (delta.hasCompleteDefinition()){ + //nothing to do, all modifications has definitions..just aplly this deltas.. + return; + } + + + PrismObject resource; + String resourceOid = delta.getOid(); + if (resourceOid == null) { + Validate.notNull(resourceWhenNoOid, "Resource oid not specified in the object delta, and resource is not specified as well. Could not apply definition."); + resource = resourceWhenNoOid.asPrismObject(); + } else { + resource = getResource(resourceOid, options, task, objectResult); + } + + ResourceType resourceType = resource.asObjectable(); +// ResourceType resourceType = completeResource(resource.asObjectable(), null, objectResult); + //TODO TODO TODO FIXME FIXME FIXME copied from ObjectImprted..union this two cases + PrismContainer configurationContainer = ResourceTypeUtil.getConfigurationContainer(resourceType); + if (configurationContainer == null || configurationContainer.isEmpty()) { + // Nothing to check + objectResult.recordWarning("The resource has no configuration"); + return; + } + + // Check the resource configuration. The schema is in connector, so fetch the connector first + String connectorOid = resourceType.getConnectorRef().getOid(); + if (StringUtils.isBlank(connectorOid)) { + objectResult.recordFatalError("The connector reference (connectorRef) is null or empty"); + return; + } + + //ItemDelta.findItemDelta(delta.getModifications(), ResourceType.F_SCHEMA, ContainerDelta.class) == null || + + ReferenceDelta connectorRefDelta = ItemDeltaCollectionsUtil.findReferenceModification(delta.getModifications(), ResourceType.F_CONNECTOR_REF); + if (connectorRefDelta != null){ + Item connectorRefNew = connectorRefDelta.getItemNewMatchingPath(null); + if (connectorRefNew.getValues().size() == 1){ + PrismReferenceValue connectorRefValue = connectorRefNew.getValues().iterator().next(); + if (connectorRefValue.getOid() != null && !connectorOid.equals(connectorRefValue.getOid())){ + connectorOid = connectorRefValue.getOid(); + } + } + } + + PrismObject connector; + ConnectorType connectorType; + try { + connector = repositoryService.getObject(ConnectorType.class, connectorOid, null, objectResult); + connectorType = connector.asObjectable(); + } catch (ObjectNotFoundException e) { + // No connector, no fun. We can't check the schema. But this is referential integrity problem. + // Mark the error ... there is nothing more to do + objectResult.recordFatalError("Connector (OID:" + connectorOid + ") referenced from the resource is not in the repository", e); + return; + } catch (SchemaException e) { + // Probably a malformed connector. To be kind of robust, lets allow the import. + // Mark the error ... there is nothing more to do + objectResult.recordPartialError("Connector (OID:" + connectorOid + ") referenced from the resource has schema problems: " + e.getMessage(), e); + LOGGER.error("Connector (OID:{}) referenced from the imported resource \"{}\" has schema problems: {}-{}", + connectorOid, resourceType.getName(), e.getMessage(), e); + return; + } + + Element connectorSchemaElement = ConnectorTypeUtil.getConnectorXsdSchema(connector); + MutablePrismSchema connectorSchema; + if (connectorSchemaElement == null) { + // No schema to validate with + return; + } + try { + connectorSchema = prismContext.schemaFactory().createPrismSchema(DOMUtil.getSchemaTargetNamespace(connectorSchemaElement)); + connectorSchema.parseThis(connectorSchemaElement, true, "schema for " + connector, prismContext); + } catch (SchemaException e) { + objectResult.recordFatalError("Error parsing connector schema for " + connector + ": "+e.getMessage(), e); + return; + } + QName configContainerQName = new QName(connectorType.getNamespace(), ResourceType.F_CONNECTOR_CONFIGURATION.getLocalPart()); + PrismContainerDefinition configContainerDef = + connectorSchema.findContainerDefinitionByElementName(configContainerQName); + if (configContainerDef == null) { + objectResult.recordFatalError("Definition of configuration container " + configContainerQName + " not found in the schema of of " + connector); + return; + } + + try { + configurationContainer.applyDefinition(configContainerDef); + } catch (SchemaException e) { + objectResult.recordFatalError("Configuration error in " + resource + ": "+e.getMessage(), e); + return; + } + + PrismContainer configContainer = resourceType.asPrismObject().findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); + //noinspection unchecked + configContainer.applyDefinition(configContainerDef); + + for (ItemDelta itemDelta : delta.getModifications()){ + applyItemDefinition(itemDelta, configContainerDef, objectResult); + } + } + + private void applyItemDefinition(ItemDelta itemDelta, + PrismContainerDefinition configContainerDef, OperationResult objectResult) throws SchemaException { + if (itemDelta.getParentPath() == null){ + LOGGER.trace("No parent path defined for item delta {}", itemDelta); + return; + } + + QName first = itemDelta.getParentPath().firstToNameOrNull(); + if (first == null) { + return; + } + + if (itemDelta.getDefinition() == null && (ResourceType.F_CONNECTOR_CONFIGURATION.equals(first) || ResourceType.F_SCHEMA.equals(first))){ + ItemPath path = itemDelta.getPath().rest(); + D itemDef = configContainerDef.findItemDefinition(path); + if (itemDef == null){ + LOGGER.warn("No definition found for item {}. Check your namespaces?", path); + objectResult.recordWarning("No definition found for item delta: " + itemDelta +". Check your namespaces?" ); +// throw new SchemaException("No definition found for item " + path+ ". Check your namespaces?" ); + return; + } + itemDelta.applyDefinition(itemDef); + + } + } + + public void applyDefinition(PrismObject resource, Task task, OperationResult parentResult) + throws ObjectNotFoundException, SchemaException, ExpressionEvaluationException { + applyConnectorSchemasToResource(resource, task, parentResult); + } + + @SuppressWarnings("unused") + public void applyDefinition(ObjectQuery query, OperationResult result) { + // TODO: not implemented yet + } + + public Object executeScript(String resourceOid, ProvisioningScriptType script, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException, ExpressionEvaluationException { + PrismObject resource = getResource(resourceOid, null, task, result); + ConnectorSpec connectorSpec = selectConnectorSpec(resource, ScriptCapabilityType.class); + if (connectorSpec == null) { + throw new UnsupportedOperationException("No connector supports script capability"); + } + ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); + ExecuteProvisioningScriptOperation scriptOperation = ProvisioningUtil.convertToScriptOperation(script, "script on "+resource, prismContext); + try { + StateReporter reporter = new StateReporter(resourceOid, task); + return connectorInstance.executeScript(scriptOperation, reporter, result); + } catch (GenericFrameworkException e) { + // Not expected. Transform to system exception + result.recordFatalError("Generic provisioning framework error", e); + throw new SystemException("Generic provisioning framework error: " + e.getMessage(), e); + } + } + + public List getConnectorOperationalStatus(PrismObject resource, OperationResult result) throws ObjectNotFoundException, SchemaException, CommunicationException, ConfigurationException { + List statuses = new ArrayList<>(); + for (ConnectorSpec connectorSpec: getAllConnectorSpecs(resource)) { + ConnectorInstance connectorInstance = connectorManager.getConfiguredConnectorInstance(connectorSpec, false, result); + ConnectorOperationalStatus operationalStatus = connectorInstance.getOperationalStatus(); + if (operationalStatus != null) { + operationalStatus.setConnectorName(connectorSpec.getConnectorName()); + statuses.add(operationalStatus); + } + } + return statuses; + } + + private List getAllConnectorSpecs(PrismObject resource) throws SchemaException { + List connectorSpecs = new ArrayList<>(); + connectorSpecs.add(getDefaultConnectorSpec(resource)); + for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { + connectorSpecs.add(getConnectorSpec(resource, additionalConnectorType)); + } + return connectorSpecs; + } + + // Should be used only internally (private). But it is public, because it is accessed from the tests. + public ConnectorInstance getConfiguredConnectorInstance(PrismObject resource, + Class operationCapabilityClass, boolean forceFresh, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException { + ConnectorSpec connectorSpec = selectConnectorSpec(resource, operationCapabilityClass); + if (connectorSpec == null) { + return null; + } + return connectorManager.getConfiguredConnectorInstance(connectorSpec, forceFresh, parentResult); + } + + // Used by the tests. Does not change anything. + @SuppressWarnings("SameParameterValue") + ConnectorInstance getConfiguredConnectorInstanceFromCache(PrismObject resource, + Class operationCapabilityClass) throws SchemaException { + ConnectorSpec connectorSpec = selectConnectorSpec(resource, operationCapabilityClass); + return connectorSpec != null ? connectorManager.getConfiguredConnectorInstanceFromCache(connectorSpec) : null; + } + + CapabilitiesType getConnectorCapabilities(ResourceType resource, + RefinedObjectClassDefinition objectClassDefinition, Class operationCapabilityClass) { + if (resource == null) { + return null; + } + + CapabilitiesType connectorCapabilities = null; + for (ConnectorInstanceSpecificationType additionalConnectorType : resource.getAdditionalConnector()) { + if (supportsCapability(additionalConnectorType, operationCapabilityClass)) { + connectorCapabilities = additionalConnectorType.getCapabilities(); + } + } + + if (connectorCapabilities == null) { + connectorCapabilities = resource.getCapabilities(); + } + + CapabilitiesType finalCapabilities = applyObjectClassCapabilities(connectorCapabilities, objectClassDefinition); + LOGGER.trace("Returning final capabilities:\n{} ", finalCapabilities); + return finalCapabilities; + + } + + private CapabilitiesType applyObjectClassCapabilities(CapabilitiesType connectorCapabilities, RefinedObjectClassDefinition objectClassDefinition) { + + if (objectClassDefinition == null) { + return connectorCapabilities; + } + + CapabilitiesType objectClassCapabilities = objectClassDefinition.getCapabilities(); + if (objectClassCapabilities == null) { + LOGGER.trace("No capabilities for {} specified, skipping merge.", objectClassDefinition); + return connectorCapabilities; + } + + CapabilityCollectionType configured = objectClassCapabilities.getConfigured(); + if (configured == null) { + LOGGER.trace("Empty capabilities in {} specified, skipping merge", objectClassDefinition); + return connectorCapabilities; + } + + CapabilitiesType finalCapabilities = new CapabilitiesType(); + if (connectorCapabilities.getNative() != null) { + finalCapabilities.setNative(connectorCapabilities.getNative()); + } + + if (!hasConfiguredCapabilities(connectorCapabilities)) { + LOGGER.trace("No configured capabilities found for connector, replacing with capabilities defined for {}", objectClassDefinition); + finalCapabilities.setConfigured(configured); + return finalCapabilities; + } + + for (Object capability : connectorCapabilities.getConfigured().getAny()) { + + if (!CapabilityUtil.containsCapabilityWithSameElementName(configured.getAny(), capability)) { + configured.getAny().add(capability); + } + } + + finalCapabilities.setConfigured(configured); + return finalCapabilities; + } + + private boolean hasConfiguredCapabilities(CapabilitiesType supportedCapabilities) { + CapabilityCollectionType configured = supportedCapabilities.getConfigured(); + return configured != null && !configured.getAny().isEmpty(); + } + + @SuppressWarnings("SameParameterValue") + private ConnectorSpec selectConnectorSpec(PrismObject resource, Map> capabilityMap, Class capabilityClass) throws SchemaException { + if (capabilityMap == null) { + return selectConnectorSpec(resource, capabilityClass); + } + for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { + if (supportsCapability(additionalConnectorType, capabilityMap.get(additionalConnectorType.getName()), capabilityClass)) { + return getConnectorSpec(resource, additionalConnectorType); + } + } + return getDefaultConnectorSpec(resource); + } + + private ConnectorSpec selectConnectorSpec(PrismObject resource, Class operationCapabilityClass) throws SchemaException { + for (ConnectorInstanceSpecificationType additionalConnectorType: resource.asObjectable().getAdditionalConnector()) { + if (supportsCapability(additionalConnectorType, operationCapabilityClass)) { + return getConnectorSpec(resource, additionalConnectorType); + } + } + return getDefaultConnectorSpec(resource); + } + + private boolean supportsCapability(ConnectorInstanceSpecificationType additionalConnectorType, Class capabilityClass) { + T cap = CapabilityUtil.getEffectiveCapability(additionalConnectorType.getCapabilities(), capabilityClass); + return CapabilityUtil.isCapabilityEnabled(cap); + } + + private boolean supportsCapability(ConnectorInstanceSpecificationType additionalConnectorType, Collection nativeCapabilities, Class capabilityClass) { + CapabilitiesType specifiedCapabilitiesType = additionalConnectorType.getCapabilities(); + if (specifiedCapabilitiesType != null) { + CapabilityCollectionType configuredCapCollectionType = specifiedCapabilitiesType.getConfigured(); + if (configuredCapCollectionType != null) { + T configuredCap = CapabilityUtil.getCapability(configuredCapCollectionType.getAny(), capabilityClass); + if (configuredCap != null && !CapabilityUtil.isCapabilityEnabled(configuredCap)) { + return false; + } + } + + } + T cap = CapabilityUtil.getCapability(nativeCapabilities, capabilityClass); + return CapabilityUtil.isCapabilityEnabled(cap); + } + + private ConnectorSpec getDefaultConnectorSpec(PrismObject resource) { + PrismContainer connectorConfiguration = resource.findContainer(ResourceType.F_CONNECTOR_CONFIGURATION); + return new ConnectorSpec(resource, null, ResourceTypeUtil.getConnectorOid(resource), connectorConfiguration); + } + + private ConnectorSpec getConnectorSpec(PrismObject resource, ConnectorInstanceSpecificationType additionalConnectorType) throws SchemaException { + if (additionalConnectorType.getConnectorRef() == null) { + throw new SchemaException("No connector reference in additional connector in "+resource); + } + String connectorOid = additionalConnectorType.getConnectorRef().getOid(); + if (StringUtils.isBlank(connectorOid)) { + throw new SchemaException("No connector OID in additional connector in "+resource); + } + //noinspection unchecked + PrismContainer connectorConfiguration = additionalConnectorType.asPrismContainerValue().findContainer(ConnectorInstanceSpecificationType.F_CONNECTOR_CONFIGURATION); + String connectorName = additionalConnectorType.getName(); + if (StringUtils.isBlank(connectorName)) { + throw new SchemaException("No connector name in additional connector in "+resource); + } + return new ConnectorSpec(resource, connectorName, connectorOid, connectorConfiguration); + } + + public PrismContext getPrismContext() { + return prismContext; + } +} diff --git a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/sync/ChangeProcessingCoordinator.java b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/sync/ChangeProcessingCoordinator.java index e08a759aabe..5b915ebabfa 100644 --- a/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/sync/ChangeProcessingCoordinator.java +++ b/provisioning/provisioning-impl/src/main/java/com/evolveum/midpoint/provisioning/impl/sync/ChangeProcessingCoordinator.java @@ -121,7 +121,7 @@ private void createWorkerTasks(int threadsCount) { } private class WorkerHandler implements LightweightTaskHandler { - private OperationResult workerSpecificResult; + private final OperationResult workerSpecificResult; private WorkerHandler(OperationResult workerSpecificResult) { this.workerSpecificResult = workerSpecificResult; diff --git a/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnIdCapabilitiesAndSchemaParser.java b/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnIdCapabilitiesAndSchemaParser.java index 1fe743c1fa5..65e520050e2 100644 --- a/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnIdCapabilitiesAndSchemaParser.java +++ b/provisioning/ucf-impl-connid/src/main/java/com/evolveum/midpoint/provisioning/ucf/impl/connid/ConnIdCapabilitiesAndSchemaParser.java @@ -247,7 +247,7 @@ private org.identityconnectors.framework.common.objects.Schema fetchConnIdSchema // Connector operation cannot create result for itself, so we need to create result for it OperationResult connIdSchemaResult = parentResult.createSubresult(ConnectorFacade.class.getName() + ".schema"); connIdSchemaResult.addContext("connector", connIdConnectorFacade.getClass()); - org.identityconnectors.framework.common.objects.Schema connIdSchema = null; + org.identityconnectors.framework.common.objects.Schema connIdSchema; try { LOGGER.debug("Fetching schema from {}", connectorHumanReadableName); diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/global/GlobalObjectCache.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/global/GlobalObjectCache.java index 7b76cd949cf..cd6a76aaa7a 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/global/GlobalObjectCache.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/global/GlobalObjectCache.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.repo.cache.global; +import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.schema.cache.CacheType; import com.evolveum.midpoint.util.caching.CacheConfiguration; import com.evolveum.midpoint.util.logging.Trace; @@ -104,7 +105,9 @@ public void remove(@NotNull Class type, String oid) { public void put(GlobalCacheObjectValue cacheObject) { if (cache != null) { - cacheObject.getObject().checkImmutable(); + PrismObject prismObject = cacheObject.getObject(); + prismObject.checkImmutable(); + LOGGER.trace("Putting {} into cache (v{})", prismObject, prismObject.getVersion()); cache.put(cacheObject.getObjectOid(), cacheObject); } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentAsserter.java index 787c9efed7d..0bce99de202 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentAsserter.java @@ -6,6 +6,7 @@ */ package com.evolveum.midpoint.test.asserter; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; @@ -16,6 +17,7 @@ import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.test.IntegrationTestTools; import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType; /** @@ -72,6 +74,15 @@ public AssignmentAsserter assertRole(String expectedOid) { return this; } + public AssignmentAsserter assertResource(String expectedOid) { + ConstructionType construction = assignment.getConstruction(); + assertThat(construction).as("construction in " + desc()).isNotNull(); + assertThat(construction.getResourceRef()).as("resourceRef in construction in " + desc()).isNotNull(); + assertThat(construction.getResourceRef().getOid()).as("resource OID in construction in " + desc()) + .isEqualTo(expectedOid); + return this; + } + public AssignmentAsserter assertSubtype(String expected) { List subtypes = assignment.getSubtype(); if (subtypes.isEmpty()) { diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java index 0f5c631629a..75c91a5c488 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/AssignmentFinder.java @@ -30,6 +30,7 @@ public class AssignmentFinder assignmentsAsserter) { this.assignmentsAsserter = assignmentsAsserter; @@ -50,12 +51,16 @@ public AssignmentFinder targetType(QName targetType) { return this; } + public AssignmentFinder resourceOid(String resourceOid) { + this.resourceOid = resourceOid; + return this; + } + public AssignmentAsserter> find() throws ObjectNotFoundException, SchemaException { AssignmentType found = null; PrismObject foundTarget = null; for (AssignmentType assignment: assignmentsAsserter.getAssignments()) { PrismObject assignmentTarget = null; -// PrismObject assignmentTarget = assignmentsAsserter.getTarget(assignment.getOid()); if (matches(assignment, assignmentTarget)) { if (found == null) { found = assignment; @@ -125,6 +130,12 @@ private boolean matches(AssignmentType assignment, PrismObject targetObject) } } + if (resourceOid != null) { + if (assignment.getConstruction() == null || assignment.getConstruction().getResourceRef() == null + || !resourceOid.equals(assignment.getConstruction().getResourceRef().getOid())) { + return false; + } + } // TODO: more criteria return true; } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ResourceAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ResourceAsserter.java index 257403dda09..87805cbfe6a 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ResourceAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/ResourceAsserter.java @@ -10,6 +10,8 @@ import static com.evolveum.midpoint.prism.Containerable.asPrismContainerValue; +import static org.testng.AssertJUnit.assertNull; + import org.w3c.dom.Element; import com.evolveum.midpoint.prism.PrismContainer; @@ -90,6 +92,12 @@ public ResourceAsserter assertHasSchema() { return this; } + public ResourceAsserter assertHasNoSchema() { + Element schemaElement = ResourceTypeUtil.getResourceXsdSchema(getObject()); + assertNull("Schema present in " + desc(), schemaElement); + return this; + } + @Override public ResourceAsserter display() { super.display(); diff --git a/tools/xjc-plugin/src/main/java/com/evolveum/midpoint/schema/xjc/PrefixMapper.java b/tools/xjc-plugin/src/main/java/com/evolveum/midpoint/schema/xjc/PrefixMapper.java index ba9986a2029..4bf47eec321 100644 --- a/tools/xjc-plugin/src/main/java/com/evolveum/midpoint/schema/xjc/PrefixMapper.java +++ b/tools/xjc-plugin/src/main/java/com/evolveum/midpoint/schema/xjc/PrefixMapper.java @@ -28,6 +28,8 @@ public enum PrefixMapper { S("http://midpoint.evolveum.com/xml/ns/public/model/situation-1.xsd", "SITUATION"), + SC("http://midpoint.evolveum.com/xml/ns/public/model/scripting-3", "SCRIPTING"), + ICF_S("http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/resource-schema-3", "ICF_SCHEMA"), ICF_C("http://midpoint.evolveum.com/xml/ns/public/connector/icf-1/connector-schema-3", "ICF_CONFIGURATION"), From e6d5e66791c3c511cdb20f8c93630f272d10f8d4 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 6 May 2020 15:15:59 +0200 Subject: [PATCH 26/41] Resolve merge issues --- .../model/intest/scripting/AbstractBasicScriptingTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/AbstractBasicScriptingTest.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/AbstractBasicScriptingTest.java index 12eb0bcbf55..69917c4b0b8 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/AbstractBasicScriptingTest.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/scripting/AbstractBasicScriptingTest.java @@ -48,7 +48,6 @@ import com.evolveum.midpoint.common.LoggingConfigurationManager; import com.evolveum.midpoint.model.api.PipelineItem; -import com.evolveum.midpoint.model.api.ScriptExecutionException; import com.evolveum.midpoint.model.impl.scripting.ExecutionContext; import com.evolveum.midpoint.model.impl.scripting.PipelineData; import com.evolveum.midpoint.model.impl.scripting.ScriptingExpressionEvaluator; From 7a19ca70a5807613e92353beadaaa91b93cd1ce4 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 6 May 2020 19:58:09 +0200 Subject: [PATCH 27/41] POM: removed spring*version properties, using ones from boot parent POM --- pom.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 47087db4f54..15b61d199ee 100644 --- a/pom.xml +++ b/pom.xml @@ -165,9 +165,6 @@ 3.141.59 1.7.25 1.2.3 - - 5.1.9.RELEASE - 5.1.6.RELEASE 2.0.0.M30 1.2.1.Final 3.5.0 @@ -953,7 +950,7 @@ org.springframework spring-core - ${spring.version} + ${spring-framework.version} From 0e59c1f5b79ea780becde62786b20c3aa6c0b185 Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Thu, 7 May 2020 10:46:40 +0200 Subject: [PATCH 28/41] wrapper factory cleanup - removing duplicates, rename AssignmentContainerWrapperFactory -> NoEmptyValueContainerWrapperFactory - No EmptyValueContainerWrapperFactory is used for all multivalue containers - we don't want to create empty ADD value for multivalue container by default. --- .../ApprovalSchemaWrapperFactoryImpl.java | 50 ----------- .../wrapper/AssignmentWrapperFactoryImpl.java | 53 ------------ .../wrapper/CaseEventWrapperFactoryImpl.java | 66 --------------- .../CaseWorkItemWrapperFactoryImpl.java | 22 +---- .../wrapper/MetadataWrapperFactory.java | 10 ++- ...EmptyValueContainerWrapperFactoryImpl.java | 59 +++++++++++++ ...rofilingClassLoggerWrapperFactoryImpl.java | 4 +- .../ResourceAttributeWrapperFactoryImpl.java | 6 +- .../wrapper/ShadowWrapperFactoryImpl.java | 3 - .../wrapper/TriggerTypeWrapperFactory.java | 82 ------------------- .../WorkItemOutputWrapperFactoryImpl.java | 65 --------------- 11 files changed, 73 insertions(+), 347 deletions(-) delete mode 100644 gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ApprovalSchemaWrapperFactoryImpl.java delete mode 100644 gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentWrapperFactoryImpl.java delete mode 100644 gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseEventWrapperFactoryImpl.java create mode 100644 gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java delete mode 100644 gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TriggerTypeWrapperFactory.java delete mode 100644 gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/WorkItemOutputWrapperFactoryImpl.java diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ApprovalSchemaWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ApprovalSchemaWrapperFactoryImpl.java deleted file mode 100644 index 16f2aabe765..00000000000 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ApprovalSchemaWrapperFactoryImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.gui.impl.factory.wrapper; - -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ApprovalSchemaType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Created by Kate Honchar - */ -@Component -public class ApprovalSchemaWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { - - @Override - public boolean match(ItemDefinition def) { - return ApprovalSchemaType.COMPLEX_TYPE .equals(def.getTypeName()); - } - - @Override - public void register() { - getRegistry().addToRegistry(this); - } - - @Override - public int getOrder() { - return 1000; - } - - @Override - protected PrismContainerValue createNewValue(PrismContainer item) { - throw new UnsupportedOperationException("New approval schema value should not be created while creating wrappers."); - } - - - @Override - protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { - return false; - } - -} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentWrapperFactoryImpl.java deleted file mode 100644 index 54cfea1b448..00000000000 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentWrapperFactoryImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.gui.impl.factory.wrapper; - -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerDefinition; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; - -/** - * @author katka - * - */ -@Component -public class AssignmentWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { - - @Override - public boolean match(ItemDefinition def) { - return def instanceof PrismContainerDefinition && def.isMultiValue(); - } - - @Override - public void register() { - getRegistry().addToRegistry(this); - } - - @Override - public int getOrder() { - return 1000; - } - - @Override - protected PrismContainerValue createNewValue(PrismContainer item) { - throw new UnsupportedOperationException("New assignment value should not be created while creating wrappers."); - } - - - @Override - protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { - return false; - } - -} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseEventWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseEventWrapperFactoryImpl.java deleted file mode 100644 index 9ab42688b5c..00000000000 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseEventWrapperFactoryImpl.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.gui.impl.factory.wrapper; - -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.web.component.prism.ValueStatus; -import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseEventType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemCompletionEventType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemDelegationEventType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemEventType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Created by honchar - */ -@Component -public class CaseEventWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { - - @Override - public boolean match(ItemDefinition def) { - return CaseEventType.COMPLEX_TYPE.equals(def.getTypeName()) || WorkItemEventType.COMPLEX_TYPE.equals(def.getTypeName()) || - WorkItemCompletionEventType.COMPLEX_TYPE.equals(def.getTypeName()) || WorkItemDelegationEventType.COMPLEX_TYPE.equals(def.getTypeName()); - } - - @Override - public void register() { - getRegistry().addToRegistry(this); - } - - @Override - public int getOrder() { - return 1000; - } - - @Override - protected PrismContainerValue createNewValue(PrismContainer item) { - throw new UnsupportedOperationException("New case event value should not be created while creating wrappers."); - } - - - @Override - protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { - return false; - } - - @Override - public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper parent, - PrismContainerValue value, ValueStatus status, WrapperContext context) throws SchemaException { - context.setCreateIfEmpty(false); - return super.createValueWrapper(parent, value, status, context); - } - - -} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java index 0b945135a33..75f5c8abac1 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java @@ -26,7 +26,7 @@ * Created by honchar */ @Component -public class CaseWorkItemWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { +public class CaseWorkItemWrapperFactoryImpl extends NoEmptyValueContainerWrapperFactoryImpl { @Override public boolean match(ItemDefinition def) { @@ -40,19 +40,9 @@ public void register() { @Override public int getOrder() { - return 1000; + return super.getOrder() - 10; } - @Override - protected PrismContainerValue createNewValue(PrismContainer item) { - throw new UnsupportedOperationException("New case work item value should not be created while creating wrappers."); - } - - - @Override - protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { - return false; - } @Override protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, PrismContainer item, @@ -62,12 +52,4 @@ protected PrismContainerWrapper createWrapper(PrismContainerVa return containerWrapper; } - @Override - public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper parent, - PrismContainerValue value, ValueStatus status, WrapperContext context) throws SchemaException { - context.setCreateIfEmpty(false); - return super.createValueWrapper(parent, value, status, context); - } - - } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/MetadataWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/MetadataWrapperFactory.java index ce782be368b..4e9375fc380 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/MetadataWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/MetadataWrapperFactory.java @@ -12,6 +12,9 @@ import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; +import com.evolveum.midpoint.prism.PrismContainer; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -33,7 +36,7 @@ public class MetadataWrapperFactory extends PrismContainerWrapperFactoryImpl def) { - return QNameUtil.match(MetadataType.COMPLEX_TYPE, def.getTypeName()); + return QNameUtil.match(MetadataType.COMPLEX_TYPE, def.getTypeName()) || QNameUtil.match(TriggerType.COMPLEX_TYPE, def.getTypeName()); } @PostConstruct @@ -63,4 +66,9 @@ protected void addItemWrapper(ItemDefinition def, PrismContainerValueWrapper< protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { return true; } + + @Override + protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { + return false; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java new file mode 100644 index 00000000000..53aa812e3c1 --- /dev/null +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2019 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.gui.impl.factory.wrapper; + +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemOutputType; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.ApprovalSchemaType; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; + +/** + * @author katka + * + */ +@Component +public class NoEmptyValueContainerWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { + + @Override + public boolean match(ItemDefinition def) { + return def instanceof PrismContainerDefinition && def.isMultiValue() + || AbstractWorkItemOutputType.COMPLEX_TYPE.equals(def.getTypeName()) + || ApprovalSchemaType.COMPLEX_TYPE .equals(def.getTypeName()); + } + //CaseEventType.COMPLEX_TYPE.equals(def.getTypeName()) || WorkItemEventType.COMPLEX_TYPE.equals(def.getTypeName()) || + // WorkItemCompletionEventType.COMPLEX_TYPE.equals(def.getTypeName()) || WorkItemDelegationEventType.COMPLEX_TYPE.equals(def.getTypeName()); + + + @Override + public void register() { + getRegistry().addToRegistry(this); + } + + @Override + public int getOrder() { + return 1000; + } + + @Override + protected PrismContainerValue createNewValue(PrismContainer item) { + throw new UnsupportedOperationException("New value for multi-value container should not be created while creating wrappers."); + } + + + @Override + protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { + return false; + } + +} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java index 2fa3fb1d84f..e5e336ae6e7 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java @@ -34,8 +34,6 @@ @Component public class ProfilingClassLoggerWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { - @Autowired private GuiComponentRegistry registry; - public static final QName PROFILING_LOGGER_PATH = new QName("profilingClassLogger"); public static final String LOGGER_PROFILING = "PROFILING"; @@ -61,7 +59,7 @@ protected boolean canCreateValueWrapper(PrismContainerValue createWrapper(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { PrismContainer clone = childContainer.clone(); - registry.registerWrapperPanel(PROFILING_LOGGER_PATH, ProfilingClassLoggerPanel.class); + getRegistry().registerWrapperPanel(PROFILING_LOGGER_PATH, ProfilingClassLoggerPanel.class); return new ProfilingClassLoggerContainerWrapperImpl<>(parent, clone, status); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java index 03acbdd60b6..361b3495897 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java @@ -34,8 +34,6 @@ @Component public class ResourceAttributeWrapperFactoryImpl extends ItemWrapperFactoryImpl, PrismPropertyValue, ResourceAttribute, PrismPropertyValueWrapper> { - @Autowired private GuiComponentRegistry registry; - @Override public boolean match(ItemDefinition def) { return def instanceof ResourceAttributeDefinition; @@ -56,7 +54,7 @@ public PrismPropertyValueWrapper createValueWrapper(ResourceAttributeWrapper< @PostConstruct @Override public void register() { - registry.addToRegistry(this); + getRegistry().addToRegistry(this); } @Override @@ -74,7 +72,7 @@ protected PrismPropertyValue createNewValue(ResourceAttribute item) throws @Override protected ResourceAttributeWrapper createWrapper(PrismContainerValueWrapper parent, ResourceAttribute childContainer, ItemStatus status, WrapperContext ctx) { - registry.registerWrapperPanel(new QName("ResourceAttributeDefinition"), ResourceAttributeDefinitionPanel.class); + getRegistry().registerWrapperPanel(new QName("ResourceAttributeDefinition"), ResourceAttributeDefinitionPanel.class); ResourceAttributeWrapper propertyWrapper = new ResourceAttributeWrapperImpl<>(parent, childContainer, status); return propertyWrapper; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java index 198379e41ba..893790edc98 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java @@ -26,9 +26,6 @@ @Component public class ShadowWrapperFactoryImpl extends PrismObjectWrapperFactoryImpl { - @Autowired private GuiComponentRegistry registry; - @Autowired private ModelService modelService; - @Override public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStatus status) { return new ShadowWrapperImpl(object, status); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TriggerTypeWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TriggerTypeWrapperFactory.java deleted file mode 100644 index d9732678ee4..00000000000 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TriggerTypeWrapperFactory.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.gui.impl.factory.wrapper; - -import java.util.List; - -import javax.annotation.PostConstruct; - -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.web.component.prism.ValueStatus; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; - -import org.springframework.stereotype.Component; - -import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.SchemaException; - -/** - * @author Kate Honchar - * - */ -//TODO review, dirrefence with MetadataWrapperFactory? -@Component -public class TriggerTypeWrapperFactory extends PrismContainerWrapperFactoryImpl { - - @Override - public boolean match(ItemDefinition def) { - return QNameUtil.match(TriggerType.COMPLEX_TYPE, def.getTypeName()); - } - - @PostConstruct - @Override - public void register() { - getRegistry().addToRegistry(this); - } - - @Override - public int getOrder() { - return 10; - } - - protected void addItemWrapper(ItemDefinition def, PrismContainerValueWrapper containerValueWrapper, - WrapperContext context, List> wrappers) throws SchemaException { - ItemWrapperFactory factory = getRegistry().findWrapperFactory(def); - context.setCreateOperational(true); - ItemWrapper wrapper = factory.createWrapper(containerValueWrapper, def, context); - wrapper.setReadOnly(true); - wrappers.add(wrapper); - context.setCreateOperational(false); - } - - @Override - protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { - return true; - } - - @Override - protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { - return false; - } - - - @Override - public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper parent, - PrismContainerValue value, ValueStatus status, WrapperContext context) throws SchemaException { - context.setCreateIfEmpty(false); - return super.createValueWrapper(parent, value, status, context); - } - -} diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/WorkItemOutputWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/WorkItemOutputWrapperFactoryImpl.java deleted file mode 100644 index d6bda710e9b..00000000000 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/WorkItemOutputWrapperFactoryImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.gui.impl.factory.wrapper; - -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.web.component.prism.ValueStatus; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -/** - * Created by honchar - */ -@Component -public class WorkItemOutputWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { - - @Autowired - private GuiComponentRegistry registry; - - @Override - public boolean match(ItemDefinition def) { - return AbstractWorkItemOutputType.COMPLEX_TYPE.equals(def.getTypeName()); - } - - @Override - public void register() { - registry.addToRegistry(this); - } - - @Override - public int getOrder() { - return 1000; - } - - @Override - protected PrismContainerValue createNewValue(PrismContainer item) { - throw new UnsupportedOperationException("New work item output value should not be created while creating wrappers."); - } - - - @Override - protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { - return false; - } - - @Override - public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper parent, - PrismContainerValue value, ValueStatus status, WrapperContext context) throws SchemaException { - context.setCreateIfEmpty(false); - return super.createValueWrapper(parent, value, status, context); - } - - -} From a74a664a229ac5e73929fbcc51ca0866fbdfa3ab Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Thu, 7 May 2020 12:32:46 +0200 Subject: [PATCH 29/41] making registerWrapperPanel explicit --- .../CaseWorkItemWrapperFactoryImpl.java | 9 ++----- .../wrapper/ExpressionWrapperFactory.java | 24 +++++++++++++++---- .../wrapper/ItemWrapperFactoryImpl.java | 5 +++- .../LoggingAppenderWrapperFactoryImpl.java | 2 +- ...oggingConfigurationWrapperFactoryImpl.java | 1 - .../LoggingPackageWrapperFactoryImpl.java | 1 - .../PrismContainerWrapperFactoryImpl.java | 7 +++++- .../PrismObjectWrapperFactoryImpl.java | 6 ++--- .../PrismPropertyWrapperFactoryImpl.java | 22 +++++++++++++---- .../wrapper/PrismReferenceWrapperFactory.java | 7 +++++- ...rofilingClassLoggerWrapperFactoryImpl.java | 7 +++++- .../ProtectedStringWrapperFactory.java | 6 ++++- .../ResourceAttributeWrapperFactoryImpl.java | 7 +++++- .../ShadowAssociationWrapperFactoryImpl.java | 1 - .../ShadowAttributesWrapperFactoryImpl.java | 6 ++--- .../wrapper/TaskHandlerWrapperFactory.java | 1 - 16 files changed, 77 insertions(+), 35 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java index 75f5c8abac1..5e031f97bd5 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java @@ -43,13 +43,8 @@ public int getOrder() { return super.getOrder() - 10; } - @Override - protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, PrismContainer item, - ItemStatus status, WrapperContext ctx) { - getRegistry().registerWrapperPanel(item.getDefinition().getTypeName(), WorkItemDetailsPanel.class); - CaseWorkItemTypeWrapper containerWrapper = new CaseWorkItemTypeWrapper(parent, item, status); - return containerWrapper; + public void registerWrapperPanel(PrismContainerWrapper wrapper) { + getRegistry().registerWrapperPanel(wrapper.getTypeName(), WorkItemDetailsPanel.class); } - } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java index df7af05e20f..5feddd6d3b0 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java @@ -47,13 +47,27 @@ public int getOrder() { protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext ctx) { - ExpressionWrapper propertyWrapper = new ExpressionWrapper(parent, item, status); - if (propertyWrapper.isConstructionExpression() || propertyWrapper.isAttributeExpression() || propertyWrapper.isAssociationExpression()) { - getRegistry().registerWrapperPanel(propertyWrapper.getTypeName(), ExpressionPropertyPanel.class); - } else { + ExpressionWrapper expressionWrapper = new ExpressionWrapper(parent, item, status); + if (!expressionWrapper.isConstructionExpression() && !expressionWrapper.isAttributeExpression() && !expressionWrapper.isAssociationExpression()) { return super.createWrapper(parent, item, status, ctx); } - return propertyWrapper; + + return expressionWrapper; } + @Override + public void registerWrapperPanel(PrismPropertyWrapper wrapper) { + if (!(wrapper instanceof ExpressionWrapper)) { + super.registerWrapperPanel(wrapper); + return; + } + ExpressionWrapper expressionWrapper = (ExpressionWrapper) wrapper; + if (expressionWrapper.isConstructionExpression() || expressionWrapper.isAttributeExpression() || expressionWrapper.isAssociationExpression()) { + getRegistry().registerWrapperPanel(expressionWrapper.getTypeName(), ExpressionPropertyPanel.class); + return; + } + + super.registerWrapperPanel(wrapper); + + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java index 9b7d482ddef..70bb496ce32 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java @@ -76,7 +76,9 @@ public IW createWrapper(PrismContainerValueWrapper parent, ItemDefinition childItem = (I) parent.getNewValue().findOrCreateItem(name); } - return createCompleteWrapper(parent, childItem, status, context); + IW wrapper = createCompleteWrapper(parent, childItem, status, context); + registerWrapperPanel(wrapper); + return wrapper; } private ItemStatus getStatus(I childItem) { @@ -88,6 +90,7 @@ private ItemStatus getStatus(I childItem) { } + public abstract void registerWrapperPanel(IW wrapper); public IW createWrapper(Item childItem, ItemStatus status, WrapperContext context) throws SchemaException { return createCompleteWrapper(null, (I) childItem, status, context); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java index 8d394ef6b12..57ac8b54abd 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java @@ -48,7 +48,7 @@ public int getOrder() { @Override protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext ctx) { - getRegistry().registerWrapperPanel(item.getDefinition().getTypeName(), PrismPropertyPanel.class); + PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); propertyWrapper.setPredefinedValues(getPredefinedValues(parent)); return propertyWrapper; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java index bf4266cf2f5..aa3cc81c3ca 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java @@ -96,7 +96,6 @@ protected PrismContainerValue createNewValue(PrismContainer item) { @Override protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { - getRegistry().registerWrapperPanel(childContainer.getDefinition().getTypeName(), PrismContainerPanel.class); return new PrismContainerWrapperImpl<>(parent, childContainer, status); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java index 95aaf31382b..ab35d661255 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java @@ -52,7 +52,6 @@ public int getOrder() { @Override protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext ctx) { - getRegistry().registerWrapperPanel(item.getDefinition().getTypeName(), PrismPropertyPanel.class); PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); propertyWrapper.setPredefinedValues(getPredefinedValues()); return propertyWrapper; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java index 5c282c80fc2..a842af9ccf6 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java @@ -120,10 +120,15 @@ protected PrismContainerValue createNewValue(PrismContainer item) { @Override protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { - getRegistry().registerWrapperPanel(childContainer.getDefinition().getTypeName(), PrismContainerPanel.class); + return new PrismContainerWrapperImpl<>(parent, childContainer, status); } + @Override + public void registerWrapperPanel(PrismContainerWrapper wrapper) { + getRegistry().registerWrapperPanel(wrapper.getTypeName(), PrismContainerPanel.class); + } + @Override public PrismContainerValueWrapper createContainerValueWrapper(PrismContainerWrapper objectWrapper, PrismContainerValue objectValue, ValueStatus status, WrapperContext context) { return new PrismContainerValueWrapperImpl(objectWrapper, objectValue, status); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java index 2d35d64a41a..dd3e6be8d2e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java @@ -54,7 +54,6 @@ public class PrismObjectWrapperFactoryImpl extends PrismCo private static final QName VIRTUAL_CONTAINER_COMPLEX_TYPE = new QName("VirtualContainerType"); private static final QName VIRTUAL_CONTAINER = new QName("virtualContainer"); - @Autowired private GuiComponentRegistry registry; @Autowired protected ModelInteractionService modelInteractionService; public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStatus status, WrapperContext context) throws SchemaException { @@ -82,7 +81,6 @@ public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStat PrismContainerValueWrapper valueWrapper = createValueWrapper(objectWrapper, object.getValue(), ItemStatus.ADDED == status ? ValueStatus.ADDED : ValueStatus.NOT_CHANGED, context); objectWrapper.getValues().add(valueWrapper); - registry.registerWrapperPanel(object.getDefinition().getTypeName(), PrismContainerPanel.class); return objectWrapper; } @@ -110,7 +108,7 @@ public void updateWrapper(PrismObjectWrapper wrapper, WrapperContext context) wrapper.getValues().clear(); wrapper.getValues().add(valueWrapper); - registry.registerWrapperPanel(wrapper.getObject().getDefinition().getTypeName(), PrismContainerPanel.class); + registerWrapperPanel(wrapper); } @Override @@ -192,7 +190,7 @@ public boolean match(ItemDefinition def) { @Override @PostConstruct public void register() { - registry.addToRegistry(this); + getRegistry().addToRegistry(this); } @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java index 37bb226cced..8aff083e91a 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java @@ -74,10 +74,16 @@ protected PrismPropertyValue createNewValue(PrismProperty item) throws Sch @Override protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext wrapperContext) { - getRegistry().registerWrapperPanel(item.getDefinition().getTypeName(), PrismPropertyPanel.class); PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); + propertyWrapper.setPredefinedValues(getPredefinedValues(item, wrapperContext)); + return propertyWrapper; + } + + protected LookupTableType getPredefinedValues(PrismProperty item, WrapperContext wrapperContext) { PrismReferenceValue valueEnumerationRef = item.getDefinition().getValueEnumerationRef(); - if (valueEnumerationRef != null) { + if (valueEnumerationRef == null) { + return null; + } //TODO: task and result from context Task task = wrapperContext.getTask(); OperationResult result = wrapperContext.getResult().createSubresult(OPERATION_LOAD_LOOKUP_TABLE); @@ -86,16 +92,17 @@ protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper pa try { PrismObject lookupTable = getModelService().getObject(LookupTableType.class, valueEnumerationRef.getOid(), options, task, result); - propertyWrapper.setPredefinedValues(lookupTable.asObjectable()); result.computeStatusIfUnknown(); + return lookupTable.asObjectable(); } catch (ObjectNotFoundException | SchemaException | SecurityViolationException | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { LOGGER.error("Cannot load lookup table for {} ", item); result.recordFatalError("Cannot load lookupTable for " + item + ", Reason: " + e.getMessage(), e); //TODO throw??? } - } - return propertyWrapper; + + return null; + } @Override @@ -105,6 +112,11 @@ public PrismPropertyValueWrapper createValueWrapper(PrismPropertyWrapper p return valueWrapper; } + @Override + public void registerWrapperPanel(PrismPropertyWrapper wrapper) { + getRegistry().registerWrapperPanel(wrapper.getTypeName(), PrismPropertyPanel.class); + } + @Override protected void setupWrapper(PrismPropertyWrapper wrapper) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java index d43f9d09469..203c9614e36 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java @@ -64,7 +64,7 @@ protected PrismReferenceValue createNewValue(PrismReference item) throws SchemaE protected PrismReferenceWrapper createWrapper(PrismContainerValueWrapper parent, PrismReference item, ItemStatus status, WrapperContext ctx) { PrismReferenceWrapperImpl wrapper = new PrismReferenceWrapperImpl<>(parent, item, status); - getRegistry().registerWrapperPanel(item.getDefinition().getTypeName(), PrismReferencePanel.class); + return wrapper; } @@ -77,6 +77,11 @@ public PrismReferenceValueWrapperImpl createValueWrapper(PrismReferenceWrappe return refValue; } + @Override + public void registerWrapperPanel(PrismReferenceWrapper wrapper) { + getRegistry().registerWrapperPanel(wrapper.getTypeName(), PrismReferencePanel.class); + } + @Override protected void setupWrapper(PrismReferenceWrapper wrapper) { // TODO Auto-generated method stub diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java index e5e336ae6e7..5bb35eb93d7 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java @@ -59,10 +59,15 @@ protected boolean canCreateValueWrapper(PrismContainerValue createWrapper(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { PrismContainer clone = childContainer.clone(); - getRegistry().registerWrapperPanel(PROFILING_LOGGER_PATH, ProfilingClassLoggerPanel.class); return new ProfilingClassLoggerContainerWrapperImpl<>(parent, clone, status); } + @Override + public void registerWrapperPanel(PrismContainerWrapper wrapper) { + getRegistry().registerWrapperPanel(PROFILING_LOGGER_PATH, ProfilingClassLoggerPanel.class); + + } + @Override protected >> List> createValuesWrapper( PrismContainerWrapper itemWrapper, PrismContainer item, WrapperContext context) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java index cc7c6f1e242..408b6adfbd2 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java @@ -46,8 +46,12 @@ public int getOrder() { protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext ctx) { ProtectedStringTypeWrapperImpl propertyWrapper = new ProtectedStringTypeWrapperImpl(parent, item, status); - getRegistry().registerWrapperPanel(item.getDefinition().getTypeName(), PasswordPropertyPanel.class); return propertyWrapper; } + @Override + public void registerWrapperPanel(PrismPropertyWrapper wrapper) { + getRegistry().registerWrapperPanel(wrapper.getTypeName(), PasswordPropertyPanel.class); + + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java index 361b3495897..439d5674960 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java @@ -72,8 +72,13 @@ protected PrismPropertyValue createNewValue(ResourceAttribute item) throws @Override protected ResourceAttributeWrapper createWrapper(PrismContainerValueWrapper parent, ResourceAttribute childContainer, ItemStatus status, WrapperContext ctx) { - getRegistry().registerWrapperPanel(new QName("ResourceAttributeDefinition"), ResourceAttributeDefinitionPanel.class); ResourceAttributeWrapper propertyWrapper = new ResourceAttributeWrapperImpl<>(parent, childContainer, status); return propertyWrapper; } + + @Override + public void registerWrapperPanel(ResourceAttributeWrapper wrapper) { + getRegistry().registerWrapperPanel(new QName("ResourceAttributeDefinition"), ResourceAttributeDefinitionPanel.class); + + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java index b0cf07f5c2e..4c9d4734b96 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java @@ -189,7 +189,6 @@ private ShadowAssociationWrapperImpl createShadowAssociationWrapper(PrismContain return null; } - getRegistry().registerWrapperPanel(associationTransformed.getDefinition().getTypeName(), PrismContainerPanel.class); ShadowAssociationWrapperImpl associationWrapper = new ShadowAssociationWrapperImpl(parent, associationTransformed, status); associationWrapper.setResource(resource.asObjectable()); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java index 6e99077d5d0..237e3fb8983 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java @@ -32,10 +32,10 @@ public int getOrder() { return Integer.MAX_VALUE - 100; } + @Override - protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, - PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { + public void registerWrapperPanel(PrismContainerWrapper wrapper) { getRegistry().registerWrapperPanel(ShadowAttributesType.COMPLEX_TYPE, PrismContainerPanel.class); - return new PrismContainerWrapperImpl<>(parent, childContainer, status); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java index 718b78b28ae..ed52b56fba9 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java @@ -52,7 +52,6 @@ protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper< return super.createWrapper(parent, item, status, ctx); } - getRegistry().registerWrapperPanel(item.getDefinition().getTypeName(), PrismPropertyPanel.class); PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); PrismReferenceValue valueEnumerationRef = item.getDefinition().getValueEnumerationRef(); if (valueEnumerationRef != null) { From fe881b321332b7bcadacadf866da6b953c64905a Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Thu, 7 May 2020 13:06:30 +0200 Subject: [PATCH 30/41] better handling for predefined values in wrapper factories --- .../LoggingAppenderWrapperFactoryImpl.java | 50 +++++++++--------- .../LoggingPackageWrapperFactoryImpl.java | 16 ++---- .../PrismObjectWrapperFactoryImpl.java | 1 + .../wrapper/TaskHandlerWrapperFactory.java | 51 +++++-------------- 4 files changed, 43 insertions(+), 75 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java index 57ac8b54abd..534c90b0010 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java @@ -9,6 +9,8 @@ import java.util.List; import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; +import com.evolveum.midpoint.prism.*; + import org.springframework.stereotype.Component; import com.evolveum.midpoint.gui.api.prism.ItemStatus; @@ -16,9 +18,6 @@ import com.evolveum.midpoint.gui.impl.prism.panel.PrismPropertyPanel; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyWrapperImpl; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismProperty; -import com.evolveum.midpoint.prism.PrismPropertyDefinition; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.AppenderConfigurationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ClassLoggerConfigurationType; @@ -42,42 +41,41 @@ public boolean match(ItemDefinition def) { @Override public int getOrder() { - return Integer.MAX_VALUE-1; + return super.getOrder() - 10; } @Override - protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, - ItemStatus status, WrapperContext ctx) { - - PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); - propertyWrapper.setPredefinedValues(getPredefinedValues(parent)); - return propertyWrapper; - } - - private LookupTableType getPredefinedValues(PrismContainerValueWrapper parent) { - LookupTableType lookupTable = new LookupTableType(); - List list = lookupTable.createRowList(); + protected LookupTableType getPredefinedValues(PrismProperty item, WrapperContext wrapperContext) { + PrismContainerValue parent = item.getParent(); + if (parent == null || parent.getParent() == null) { + return null; + } - if(parent == null || parent.getParent() == null || parent.getParent().getParent() == null) { - return lookupTable; + //TODO change matchMethid to be able to check path istead of def??? + PrismContainerable parentParent = parent.getParent(); + if (!(parentParent instanceof PrismContainer)) { + return null; } + PrismContainerValue parentValue = ((PrismContainer) parentParent).getParent(); - if(!(parent.getParent().getParent().getRealValue() instanceof LoggingConfigurationType)) { + if(!(parentValue.getRealValue() instanceof LoggingConfigurationType)) { throw new IllegalArgumentException("LoggingConfigurationType not found in parent for Appender"); } - LoggingConfigurationType loggingConfig = (LoggingConfigurationType) parent.getParent().getParent().getRealValue(); + LoggingConfigurationType loggingConfig = parentValue.getRealValue(); + LookupTableType lookupTable = new LookupTableType(); + List list = lookupTable.createRowList(); for (AppenderConfigurationType appender : loggingConfig.getAppender()) { - LookupTableRowType row = new LookupTableRowType(); - String name = appender.getName(); - row.setKey(name); - row.setValue(name); - row.setLabel(new PolyStringType(name)); - list.add(row); + LookupTableRowType row = new LookupTableRowType(); + String name = appender.getName(); + row.setKey(name); + row.setValue(name); + row.setLabel(new PolyStringType(name)); + list.add(row); } return lookupTable; - } + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java index ab35d661255..7a50b3a44ed 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java @@ -46,23 +46,15 @@ public boolean match(ItemDefinition def) { @Override public int getOrder() { - return Integer.MAX_VALUE-1; + return super.getOrder() - 10; } - @Override - protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, - ItemStatus status, WrapperContext ctx) { - PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); - propertyWrapper.setPredefinedValues(getPredefinedValues()); - return propertyWrapper; - } - private LookupTableType getPredefinedValues() { + public LookupTableType getPredefinedValues(PrismProperty item, WrapperContext wrapperContext) { LookupTableType lookupTable = new LookupTableType(); List list = lookupTable.createRowList(); - List standardLoggers = EnumUtils.getEnumList(StandardLoggerType.class); - List componentLoggers = EnumUtils.getEnumList(LoggingComponentType.class); + List standardLoggers = EnumUtils.getEnumList(StandardLoggerType.class); for(StandardLoggerType standardLogger : standardLoggers) { LookupTableRowType row = new LookupTableRowType(); row.setKey(standardLogger.getValue()); @@ -74,6 +66,8 @@ private LookupTableType getPredefinedValues() { row.setLabel(label); list.add(row); } + + List componentLoggers = EnumUtils.getEnumList(LoggingComponentType.class); for(LoggingComponentType componentLogger : componentLoggers) { LookupTableRowType row = new LookupTableRowType(); String value = ComponentLoggerType.getPackageByValue(componentLogger); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java index dd3e6be8d2e..4eaf9f6b6ba 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java @@ -80,6 +80,7 @@ public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStat objectWrapper.setExpanded(true); PrismContainerValueWrapper valueWrapper = createValueWrapper(objectWrapper, object.getValue(), ItemStatus.ADDED == status ? ValueStatus.ADDED : ValueStatus.NOT_CHANGED, context); objectWrapper.getValues().add(valueWrapper); + registerWrapperPanel(objectWrapper); return objectWrapper; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java index ed52b56fba9..dfc6d9dd18a 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java @@ -44,46 +44,25 @@ public class TaskHandlerWrapperFactory extends PrismPropertyWrapperFactoryImpl createWrapper(PrismContainerValueWrapper parent, PrismProperty item, - ItemStatus status, WrapperContext ctx) { - + protected LookupTableType getPredefinedValues(PrismProperty item, WrapperContext ctx) { PrismObject prismObject = getParent(ctx); if (prismObject == null || !TaskType.class.equals(prismObject.getCompileTimeClass())) { - return super.createWrapper(parent, item, status, ctx); + return super.getPredefinedValues(item, ctx); } - PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); - PrismReferenceValue valueEnumerationRef = item.getDefinition().getValueEnumerationRef(); - if (valueEnumerationRef != null) { - Task task = ctx.getTask(); - OperationResult result = ctx.getResult().createSubresult(OPERATION_DETERMINE_LOOKUP_TABLE); - Collection> options = WebModelServiceUtils - .createLookupTableRetrieveOptions(schemaHelper); - - try { - PrismObject lookupTable = getModelService().getObject(LookupTableType.class, valueEnumerationRef.getOid(), options, task, result); - propertyWrapper.setPredefinedValues(lookupTable.asObjectable()); - } catch (ObjectNotFoundException | SchemaException | SecurityViolationException | CommunicationException - | ConfigurationException | ExpressionEvaluationException e) { - LOGGER.error("Cannot load lookup table for {} ", item); - //TODO throw??? - } - - return propertyWrapper; + LookupTableType parentLookup = super.getPredefinedValues(item, ctx); + if (parentLookup != null) { + return parentLookup; } - if (parent != null && parent.getParent() != null) { - - TaskType task = (TaskType) prismObject.asObjectable(); - - if (ItemStatus.ADDED == status) { - Collection assignmentTypes = task.getAssignment() - .stream() - .filter(assignmentType -> WebComponentUtil.isArchetypeAssignment(assignmentType)) - .collect(Collectors.toList()); + TaskType task = (TaskType) prismObject.asObjectable(); + Collection assignmentTypes = task.getAssignment() + .stream() + .filter(assignmentType -> WebComponentUtil.isArchetypeAssignment(assignmentType)) + .collect(Collectors.toList()); - Collection handlers; - if (assignmentTypes.isEmpty()) { + Collection handlers; + if (assignmentTypes.isEmpty()) { // TODO all handlers handlers = getTaskManager().getAllHandlerUris(true); } else if (assignmentTypes.size() == 1) { @@ -106,11 +85,7 @@ protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper< lookupTableType.getRow().add(row); }); - propertyWrapper.setPredefinedValues(lookupTableType); - - } - } - return propertyWrapper; + return lookupTableType; } private PrismObject getParent(WrapperContext ctx) { From 25381f46c16e2de21f6a2d4a7e360fea89f41237 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Mon, 11 May 2020 11:02:27 +0200 Subject: [PATCH 31/41] Escape invalid XML chars in traces/audit Sometimes an invalid XML character (like 0x06) is to be stored into XML element content. This occurs especially when dealing with traces or audit records. This commit fixes this: For traces it always replaces such chars with a descriptive form. For audit records this behavior is configurable with the default being turned off. Resolves MID-6262. --- .../midpoint/prism/SerializationOptions.java | 34 +- .../impl/lex/dom/DomLexicalProcessor.java | 18 +- .../prism/impl/lex/dom/DomLexicalWriter.java | 15 +- .../schema/DeltaConversionOptions.java | 24 +- .../midpoint/schema/DeltaConvertor.java | 8 +- .../util/SystemConfigurationAuditUtil.java | 27 + .../xml/ns/public/common/common-core-3.xsd | 14 + .../com/evolveum/midpoint/util/DOMUtil.java | 26 + .../evolveum/midpoint/util/DOMUtilTest.java | 14 + .../model/impl/lens/ChangeExecutor.java | 4 +- .../model/impl/lens/LensElementContext.java | 1700 ++++++++--------- .../impl/lens/LensObjectDeltaOperation.java | 11 +- .../model/intest/misc/TestTracing.java | 104 + .../resources/tracing/resource-illegal.xml | 109 ++ .../test/resources/tracing/role-illegal.xml | 29 + .../tracing/system-configuration.xml | 16 + .../src/test/resources/tracing/user-joe.xml | 11 + .../model-intest/testng-integration-full.xml | 1 + .../test/AbstractModelIntegrationTest.java | 27 + .../report/impl/MidPointQueryExecutor.java | 406 ++-- .../midpoint/audit/api/AuditEventRecord.java | 9 +- .../midpoint/audit/api/AuditService.java | 6 + .../repo/sql/SqlAuditServiceImpl.java | 34 +- ...stemConfigurationChangeDispatcherImpl.java | 16 +- .../sql/data/audit/RAuditEventRecord.java | 7 +- .../sql/data/audit/RObjectDeltaOperation.java | 21 +- .../evolveum/midpoint/test/TestResource.java | 4 + .../test/asserter/DummyObjectAsserter.java | 2 +- .../midpoint/init/AuditServiceProxy.java | 9 +- .../task/quartzimpl/tracing/TracerImpl.java | 5 +- 30 files changed, 1604 insertions(+), 1107 deletions(-) create mode 100644 infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SystemConfigurationAuditUtil.java create mode 100644 model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/misc/TestTracing.java create mode 100644 model/model-intest/src/test/resources/tracing/resource-illegal.xml create mode 100644 model/model-intest/src/test/resources/tracing/role-illegal.xml create mode 100644 model/model-intest/src/test/resources/tracing/system-configuration.xml create mode 100644 model/model-intest/src/test/resources/tracing/user-joe.xml diff --git a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java index 3842bed905c..d6d67635e68 100644 --- a/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java +++ b/infra/prism-api/src/main/java/com/evolveum/midpoint/prism/SerializationOptions.java @@ -7,8 +7,10 @@ package com.evolveum.midpoint.prism; +import com.evolveum.midpoint.util.annotation.Experimental; + /** - * @author Pavol Mederly + * */ public class SerializationOptions implements Cloneable { @@ -29,6 +31,14 @@ public class SerializationOptions implements Cloneable { */ private boolean serializeForExport; + /** + * Works around characters that cannot be serialized in XML by replacing them with acceptable form. + * Because the result is not machine processable it should be used with caution; for example only + * for logging, tracing, or maybe auditing purposes. + */ + @Experimental + private boolean escapeInvalidCharacters; + // private NameQualificationStrategy itemTypeQualificationStrategy; // private NameQualificationStrategy itemPathQualificationStrategy; // private NameQualificationStrategy genericQualificationStrategy; @@ -117,6 +127,27 @@ public static boolean isSerializeForExport(SerializationOptions options) { return options != null && options.isSerializeForExport(); } + public boolean isEscapeInvalidCharacters() { + return escapeInvalidCharacters; + } + + public void setEscapeInvalidCharacters(boolean escapeInvalidCharacters) { + this.escapeInvalidCharacters = escapeInvalidCharacters; + } + + public SerializationOptions escapeInvalidCharacters(boolean value) { + setEscapeInvalidCharacters(value); + return this; + } + + public static SerializationOptions createEscapeInvalidCharacters() { + return new SerializationOptions().escapeInvalidCharacters(true); + } + + public static boolean isEscapeInvalidCharacters(SerializationOptions options) { + return options != null && options.isEscapeInvalidCharacters(); + } + public void setSkipIndexOnly(boolean skipIndexOnly) { this.skipIndexOnly = skipIndexOnly; } @@ -203,6 +234,7 @@ public String toString() { ", skipIndexOnly=" + skipIndexOnly + ", itemNameQualificationStrategy=" + itemNameQualificationStrategy + ", serializeForExport=" + serializeForExport + + ", escapeInvalidCharacters=" + escapeInvalidCharacters + '}'; } } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java index eb84797b6ea..8dc80a55c50 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java @@ -422,7 +422,7 @@ public boolean canRead(@NotNull String dataString) { @NotNull @Override public String write(@NotNull XNode xnode, @NotNull QName rootElementName, SerializationContext serializationContext) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry); + DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, serializationContext); RootXNodeImpl xroot = LexicalUtils.createRootXNode((XNodeImpl) xnode, rootElementName); Element element = serializer.serialize(xroot); return DOMUtil.serializeDOMToString(element); @@ -431,7 +431,7 @@ public String write(@NotNull XNode xnode, @NotNull QName rootElementName, Serial @NotNull @Override public String write(@NotNull RootXNode xnode, SerializationContext serializationContext) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry); + DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, serializationContext); Element element = serializer.serialize((RootXNodeImpl) xnode); return DOMUtil.serializeDOMToString(element); } @@ -446,29 +446,23 @@ public String write(@NotNull List roots, @Nullable QName aggregat @NotNull public Element writeXRootListToElement(@NotNull List roots, QName aggregateElementName) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry); + DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); return serializer.serialize(roots, aggregateElementName); } - public Element serializeUnderElement(XNodeImpl xnode, QName rootElementName, Element parentElement) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry); - RootXNodeImpl xroot = LexicalUtils.createRootXNode(xnode, rootElementName); - return serializer.serializeUnderElement(xroot, parentElement); - } - public Element serializeXMapToElement(MapXNodeImpl xmap, QName elementName) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry); + DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); return serializer.serializeToElement(xmap, elementName); } private Element serializeXPrimitiveToElement(PrimitiveXNodeImpl xprim, QName elementName) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry); + DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); return serializer.serializeXPrimitiveToElement(xprim, elementName); } @NotNull public Element writeXRootToElement(@NotNull RootXNodeImpl xroot) throws SchemaException { - DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry); + DomLexicalWriter serializer = new DomLexicalWriter(schemaRegistry, null); return serializer.serialize(xroot); } diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java index 07d63894abc..204534e804a 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalWriter.java @@ -6,6 +6,8 @@ */ package com.evolveum.midpoint.prism.impl.lex.dom; +import com.evolveum.midpoint.prism.SerializationContext; +import com.evolveum.midpoint.prism.SerializationOptions; import com.evolveum.midpoint.prism.impl.PrismContextImpl; import com.evolveum.midpoint.prism.impl.marshaller.ItemPathHolder; import com.evolveum.midpoint.prism.schema.SchemaRegistry; @@ -45,11 +47,13 @@ public class DomLexicalWriter { private Document doc; private boolean serializeCompositeObjects = false; - private SchemaRegistry schemaRegistry; + private final SchemaRegistry schemaRegistry; + private final SerializationOptions serializationOptions; - DomLexicalWriter(SchemaRegistry schemaRegistry) { + DomLexicalWriter(SchemaRegistry schemaRegistry, SerializationContext context) { super(); this.schemaRegistry = schemaRegistry; + this.serializationOptions = context != null ? context.getOptions() : null; } public boolean isSerializeCompositeObjects() { @@ -318,11 +322,12 @@ private void serializePrimitiveElementOrAttribute(PrimitiveXNodeImpl xprim, E } else { // not ItemType nor QName String value = xprim.getGuessedFormattedValue(); - + String fixedValue = SerializationOptions.isEscapeInvalidCharacters(serializationOptions) ? + DOMUtil.escapeInvalidXmlCharsIfPresent(value) : value; if (asAttribute) { - DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), value); + DOMUtil.setAttributeValue(parentElement, elementOrAttributeName.getLocalPart(), fixedValue); } else { - DOMUtil.setElementTextContent(element, value); + DOMUtil.setElementTextContent(element, fixedValue); } } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConversionOptions.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConversionOptions.java index f864620c63a..67c9e026c7c 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConversionOptions.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConversionOptions.java @@ -7,13 +7,23 @@ package com.evolveum.midpoint.schema; +import com.evolveum.midpoint.util.annotation.Experimental; + /** - * @author Pavol Mederly + * Options used when serializing deltas to "bean" form (ObjectDeltaType). */ public class DeltaConversionOptions { private boolean serializeReferenceNames; + /** + * Works around characters that cannot be serialized in XML by replacing them with appropriate + * form. The result will not be machine processable, so it should be used with caution: for example + * only for logging, tracing, or maybe auditing purposes. + */ + @Experimental + private boolean escapeInvalidCharacters; + public boolean isSerializeReferenceNames() { return serializeReferenceNames; } @@ -31,4 +41,16 @@ public static DeltaConversionOptions createSerializeReferenceNames() { options.setSerializeReferenceNames(true); return options; } + + public boolean isEscapeInvalidCharacters() { + return escapeInvalidCharacters; + } + + public void setEscapeInvalidCharacters(boolean escapeInvalidCharacters) { + this.escapeInvalidCharacters = escapeInvalidCharacters; + } + + public static boolean isEscapeInvalidCharacters(DeltaConversionOptions options) { + return options != null && options.isEscapeInvalidCharacters(); + } } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java index 8d7eb58c7a2..94e5bc2dd78 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/DeltaConvertor.java @@ -229,9 +229,11 @@ public static String toObjectDeltaTypeXml(ObjectDelta delt public static String toObjectDeltaTypeXml(ObjectDelta delta, DeltaConversionOptions options) throws SchemaException, JAXBException { Validate.notNull(delta.getPrismContext(), "ObjectDelta without prismContext cannot be converted to XML"); ObjectDeltaType objectDeltaType = toObjectDeltaType(delta, options); - SerializationOptions serializationOptions = new SerializationOptions(); - serializationOptions.setSerializeReferenceNames(DeltaConversionOptions.isSerializeReferenceNames(options)); - return delta.getPrismContext().xmlSerializer().options(serializationOptions).serializeRealValue(objectDeltaType, SchemaConstants.T_OBJECT_DELTA); + SerializationOptions serializationOptions = new SerializationOptions() + .serializeReferenceNames(DeltaConversionOptions.isSerializeReferenceNames(options)) + .escapeInvalidCharacters(DeltaConversionOptions.isEscapeInvalidCharacters(options)); + return delta.getPrismContext().xmlSerializer().options(serializationOptions) + .serializeRealValue(objectDeltaType, SchemaConstants.T_OBJECT_DELTA); } diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SystemConfigurationAuditUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SystemConfigurationAuditUtil.java new file mode 100644 index 00000000000..f1ef8ab2b1c --- /dev/null +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/SystemConfigurationAuditUtil.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.schema.util; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationAuditType; + +import org.apache.commons.lang3.ObjectUtils; + +/** + * Utility methods for audit-related system configuration options. + */ +public class SystemConfigurationAuditUtil { + + public static boolean isEscapingInvalidCharacters(SystemConfigurationAuditType configuration) { + final boolean defaultValue = false; + if (configuration == null || configuration.getEventRecording() == null) { + return defaultValue; + } else { + return ObjectUtils.defaultIfNull(configuration.getEventRecording().isEscapeIllegalCharacters(), defaultValue); + } + } +} diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd index 676434c18b7..add76aae543 100755 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-core-3.xsd @@ -16376,6 +16376,20 @@ + + + + Should illegal characters (like majority of 0x00-0x1F in XML 1.0) be escaped? + If they occur and are not escaped, audit operation will fail. If they are stored + in escaped form, audit operation will succeed but some parts of the audit records + will not be correctly machine processable in the future. So the default value is "false". + + + 4.2 + SystemConfigurationAuditEventRecordingType.escapeIllegalCharacters + + + diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java index 9ca6dc3eb9b..95fe3c1d770 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java @@ -1417,6 +1417,32 @@ public static void checkValidXmlChars(String stringValue) { } } + public static String escapeInvalidXmlCharsIfPresent(String value) { + if (value == null) { + return null; + } + int codepointCount = value.codePointCount(0, value.length()); + for (int i = 0; i < codepointCount; i++) { + if (!XMLChar.isValid(value.codePointAt(i))) { + return escapeInvalidXmlChars(value, codepointCount); + } + } + return value; + } + + private static String escapeInvalidXmlChars(String value, int codepointCount) { + StringBuilder sb = new StringBuilder(value.length()); + for (int i = 0; i < codepointCount; i++) { + int cp = value.codePointAt(i); + if (XMLChar.isValid(cp)) { + sb.appendCodePoint(cp); + } else { + sb.append("[INVALID CODE POINT: ").append(cp).append(']'); + } + } + return sb.toString(); + } + // todo move to some Util class private static String makeSafelyPrintable(String text, int maxSize) { StringBuilder sb = new StringBuilder(); diff --git a/infra/util/src/test/java/com/evolveum/midpoint/util/DOMUtilTest.java b/infra/util/src/test/java/com/evolveum/midpoint/util/DOMUtilTest.java index a5c33b8fd05..04b0fe3e428 100644 --- a/infra/util/src/test/java/com/evolveum/midpoint/util/DOMUtilTest.java +++ b/infra/util/src/test/java/com/evolveum/midpoint/util/DOMUtilTest.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.util; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.*; import java.util.Map; @@ -338,4 +339,17 @@ public void testSupportStringWithSurrogates() { assertTrue("Not support string with surrogates in xml", support); } + @Test + public void testEscapeInvalidXmlChars() { + String value = "A\u0006B\u0000C"; + try { + DOMUtil.checkValidXmlChars(value); + fail("unexpected success"); + } catch (IllegalStateException e) { + displayExpectedException(e); + } + + String fixed = DOMUtil.escapeInvalidXmlCharsIfPresent(value); + assertThat(fixed).as("fixed string").isEqualTo("A[INVALID CODE POINT: 6]B[INVALID CODE POINT: 0]C"); + } } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java index e2543a7c8fc..41cad83f7d9 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/ChangeExecutor.java @@ -956,12 +956,12 @@ private PrismObject executeDelta objectContext.addToExecutedDeltas(objectDeltaOp); if (result.isTracingNormal(ModelExecuteDeltaTraceType.class)) { TraceType trace = new ModelExecuteDeltaTraceType(prismContext) - .delta(objectDeltaOp.clone().toLensObjectDeltaOperationType()); // todo kill operation result? + .delta(objectDeltaOp.clone().toLensObjectDeltaOperationType()); // todo kill operation result? result.addTrace(trace); } } else { if (result.isTracingNormal(ModelExecuteDeltaTraceType.class)) { - LensObjectDeltaOperation objectDeltaOp = new LensObjectDeltaOperation<>(objectDelta); // todo + LensObjectDeltaOperation objectDeltaOp = new LensObjectDeltaOperation<>(objectDelta); // todo TraceType trace = new ModelExecuteDeltaTraceType(prismContext) .delta(objectDeltaOp.toLensObjectDeltaOperationType()); result.addTrace(trace); diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java index 2a33379b47b..09aeac6c462 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensElementContext.java @@ -1,850 +1,850 @@ -/* - * Copyright (c) 2010-2018 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.model.impl.lens; - -import java.util.*; -import java.util.function.Consumer; - -import com.evolveum.midpoint.model.api.context.SynchronizationIntent; -import com.evolveum.midpoint.prism.ConsistencyCheckScope; -import com.evolveum.midpoint.prism.Objectable; -import com.evolveum.midpoint.prism.delta.*; -import com.evolveum.midpoint.prism.delta.builder.S_ItemEntry; -import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy; -import com.evolveum.midpoint.prism.util.ObjectDeltaObject; -import com.evolveum.midpoint.schema.DeltaConvertor; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.schema.util.ObjectTypeUtil; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; -import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; -import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.Validate; - -import com.evolveum.midpoint.common.crypto.CryptoUtil; -import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; -import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRuleTrigger; -import com.evolveum.midpoint.model.api.context.ModelElementContext; -import com.evolveum.midpoint.prism.PrismContext; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismObjectDefinition; -import com.evolveum.midpoint.schema.util.ShadowUtil; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import org.jetbrains.annotations.NotNull; - -/** - * @author semancik - * - */ -public abstract class LensElementContext implements ModelElementContext, Cloneable { - - private static final long serialVersionUID = 1649567559396392861L; - - private static final Trace LOGGER = TraceManager.getTrace(LensElementContext.class); - - private PrismObject objectOld; - private transient PrismObject objectCurrent; - private PrismObject objectNew; - private ObjectDelta primaryDelta; - @NotNull private final List> executedDeltas = new ArrayList<>(); - @NotNull private final Class objectTypeClass; - private String oid = null; - private int iteration; - private String iterationToken; - - transient private SecurityPolicyType securityPolicy; - - /** - * These are policy state modifications that should be applied. - * Currently we apply them in ChangeExecutor.executeChanges only. - * - * In the future we plan to be able to apply some state modifications even - * if the clockwork is exited in non-standard way (e.g. in primary state or with an exception). - * But we must be sure what policy state to store, because some constraints might be triggered - * because of expectation of future state (like conflicting assignment is added etc.) - * --- - * Although placed in LensElementContext, support for this data is currently implemented only for focus, not for projections. - */ - @NotNull private final List> pendingObjectPolicyStateModifications = new ArrayList<>(); - - /** - * Policy state modifications for assignments. - * - * Although we put here also deltas for assignments that are to be deleted, we do not execute these - * (because we implement execution only for the standard exit-path from the clockwork). - */ - @NotNull private final Map>> pendingAssignmentPolicyStateModifications = new HashMap<>(); - - private SynchronizationIntent synchronizationIntent; - - private transient boolean isFresh = false; - - private LensContext lensContext; - - private transient PrismObjectDefinition objectDefinition = null; - - private final Collection policyRules = new ArrayList<>(); - private final Collection policySituations = new ArrayList<>(); - - public LensElementContext(@NotNull Class objectTypeClass, LensContext lensContext) { - super(); - Validate.notNull(objectTypeClass, "Object class is null"); - Validate.notNull(lensContext, "Lens context is null"); - this.lensContext = lensContext; - this.objectTypeClass = objectTypeClass; - } - - public int getIteration() { - return iteration; - } - - public void setIteration(int iteration) { - this.iteration = iteration; - } - - public String getIterationToken() { - return iterationToken; - } - - public void setIterationToken(String iterationToken) { - this.iterationToken = iterationToken; - } - - public SynchronizationIntent getSynchronizationIntent() { - return synchronizationIntent; - } - - public void setSynchronizationIntent(SynchronizationIntent synchronizationIntent) { - this.synchronizationIntent = synchronizationIntent; - } - - public LensContext getLensContext() { - return lensContext; - } - - protected PrismContext getNotNullPrismContext() { - return getLensContext().getNotNullPrismContext(); - } - - @Override - public Class getObjectTypeClass() { - return objectTypeClass; - } - - public boolean represents(Class type) { - return type.isAssignableFrom(objectTypeClass); - } - - public PrismContext getPrismContext() { - return lensContext.getPrismContext(); - } - - @Override - public PrismObject getObjectOld() { - return objectOld; - } - - public void setObjectOld(PrismObject objectOld) { - this.objectOld = objectOld; - } - - @Override - public PrismObject getObjectCurrent() { - return objectCurrent; - } - - public void setObjectCurrent(PrismObject objectCurrent) { - this.objectCurrent = objectCurrent; - } - - public PrismObject getObjectAny() { - if (objectNew != null) { - return objectNew; - } - if (objectCurrent != null) { - return objectCurrent; - } - return objectOld; - } - - /** - * Sets current and possibly also old object. This method is used with - * freshly loaded object. The object is set as current object. - * If the old object was not initialized yet (and if it should be initialized) - * then the object is also set as old object. - */ - public void setLoadedObject(PrismObject object) { - setObjectCurrent(object); - if (objectOld == null && !isAdd()) { - setObjectOld(object.clone()); - } - } - - @Override - public PrismObject getObjectNew() { - return objectNew; - } - - public void setObjectNew(PrismObject objectNew) { - this.objectNew = objectNew; - } - - @Override - public ObjectDelta getPrimaryDelta() { - return primaryDelta; - } - - public boolean hasPrimaryDelta() { - return primaryDelta != null && !primaryDelta.isEmpty(); - } - - /** - * As getPrimaryDelta() but caters for the possibility that an object already exists. - * So, if the primary delta is ADD and object already exists, it should be changed somehow, - * e.g. to MODIFY delta or to null. - * - * Actually, the question is what to do with the attribute values if changed to MODIFY. - * (a) Should they become REPLACE item deltas? (b) ADD ones? - * (c) Or should we compute a difference from objectCurrent to objectToAdd, hoping that - * secondary deltas will re-add everything that might be unknowingly removed by this step? - * (d) Or should we simply ignore ADD delta altogether, hoping that it was executed - * so it need not be repeated? - * - * And, should not we report AlreadyExistingException instead? - * - * It seems that (c) i.e. reverting back to objectToAdd is not a good idea at all. For example, this - * may erase linkRefs for good. - * - * For the time being let us proceed with (d), i.e. ignoring such a delta. - * - * TODO is this OK???? [med] - * - * @return - */ - public ObjectDelta getFixedPrimaryDelta() { - if (primaryDelta == null || !primaryDelta.isAdd() || objectCurrent == null) { - return primaryDelta; // nothing to do - } - // Object does exist. Let's ignore the delta - see description above. - return null; - } - - public void setPrimaryDelta(ObjectDelta primaryDelta) { - this.primaryDelta = primaryDelta; - } - - public void addPrimaryDelta(ObjectDelta delta) throws SchemaException { - if (primaryDelta == null) { - primaryDelta = delta; - } else { - primaryDelta.merge(delta); - } - } - - public void swallowToPrimaryDelta(ItemDelta itemDelta) throws SchemaException { - modifyOrCreatePrimaryDelta( - delta -> delta.swallow(itemDelta), - () -> { - ObjectDelta newPrimaryDelta = getPrismContext().deltaFactory().object().create(getObjectTypeClass(), ChangeType.MODIFY); - newPrimaryDelta.setOid(oid); - newPrimaryDelta.addModification(itemDelta); - return newPrimaryDelta; - }); - } - - @FunctionalInterface - private interface DeltaModifier { - void modify(ObjectDelta delta) throws SchemaException; - } - - @FunctionalInterface - private interface DeltaCreator { - ObjectDelta create() throws SchemaException; - } - - private void modifyOrCreatePrimaryDelta(DeltaModifier modifier, DeltaCreator creator) throws SchemaException { - if (primaryDelta == null) { - primaryDelta = creator.create(); - } else if (!primaryDelta.isImmutable()) { - modifier.modify(primaryDelta); - } else { - primaryDelta = primaryDelta.clone(); - modifier.modify(primaryDelta); - primaryDelta.freeze(); - } - } - - public abstract void swallowToSecondaryDelta(ItemDelta itemDelta) throws SchemaException; - - // TODO deduplicate with swallowToSecondaryDelta in LensFocusContext - public ObjectDelta swallowToDelta(ObjectDelta originalDelta, ItemDelta propDelta) throws SchemaException { - if (originalDelta == null) { - originalDelta = getPrismContext().deltaFactory().object().create(getObjectTypeClass(), ChangeType.MODIFY); - originalDelta.setOid(getOid()); - } else if (originalDelta.containsModification(propDelta, EquivalenceStrategy.LITERAL_IGNORE_METADATA)) { // todo why literal? - return originalDelta; - } - originalDelta.swallow(propDelta); - return originalDelta; - } - - /** - * Returns collection of all deltas that this element context contains. - * This is a nice method to use if we want to inspect all deltas (e.g. look for a changed item) - * but we want to avoid the overhead of merging all the deltas together. - */ - public abstract Collection> getAllDeltas(); - - @NotNull - public List> getPendingObjectPolicyStateModifications() { - return pendingObjectPolicyStateModifications; - } - - public void clearPendingObjectPolicyStateModifications() { - pendingObjectPolicyStateModifications.clear(); - } - - public void addToPendingObjectPolicyStateModifications(ItemDelta modification) { - pendingObjectPolicyStateModifications.add(modification); - } - - @NotNull - public Map>> getPendingAssignmentPolicyStateModifications() { - return pendingAssignmentPolicyStateModifications; - } - - public void clearPendingAssignmentPolicyStateModifications() { - pendingAssignmentPolicyStateModifications.clear(); - } - - public void addToPendingAssignmentPolicyStateModifications(@NotNull AssignmentType assignment, @NotNull PlusMinusZero mode, @NotNull ItemDelta modification) { - AssignmentSpec spec = new AssignmentSpec(assignment, mode); - pendingAssignmentPolicyStateModifications.computeIfAbsent(spec, k -> new ArrayList<>()).add(modification); - } - - public boolean isModify() { - // TODO I'm not sure why isModify checks both primary and secondary deltas for focus context, while - // isAdd and isDelete care only for the primary delta. - return ObjectDelta.isModify(getPrimaryDelta()) || ObjectDelta.isModify(getSecondaryDelta()); - } - - @NotNull - public SimpleOperationName getOperation() { - if (isAdd()) { - return SimpleOperationName.ADD; - } else if (isDelete()) { - return SimpleOperationName.DELETE; - } else { - return SimpleOperationName.MODIFY; - } - } - - @NotNull - @Override - public List> getExecutedDeltas() { - return executedDeltas; - } - - List> getExecutedDeltas(Boolean audited) { - if (audited == null) { - return executedDeltas; - } - List> deltas = new ArrayList<>(); - for (LensObjectDeltaOperation delta: executedDeltas) { - if (delta.isAudited() == audited) { - deltas.add(delta); - } - } - return deltas; - } - - public void markExecutedDeltasAudited() { - for(LensObjectDeltaOperation executedDelta: executedDeltas) { - executedDelta.setAudited(true); - } - } - - public void addToExecutedDeltas(LensObjectDeltaOperation executedDelta) { - executedDeltas.add(executedDelta.clone()); // must be cloned because e.g. for ADD deltas the object gets modified afterwards - } - - /** - * Returns user delta, both primary and secondary (merged together). - * The returned object is (kind of) immutable. Changing it may do strange things (but most likely the changes will be lost). - */ - public ObjectDelta getDelta() throws SchemaException { - return ObjectDeltaCollectionsUtil.union(primaryDelta, getSecondaryDelta()); - } - - public ObjectDelta getFixedDelta() throws SchemaException { - return ObjectDeltaCollectionsUtil.union(getFixedPrimaryDelta(), getSecondaryDelta()); - } - - public boolean wasAddExecuted() { - - for (LensObjectDeltaOperation executedDeltaOperation : getExecutedDeltas()){ - ObjectDelta executedDelta = executedDeltaOperation.getObjectDelta(); - if (!executedDelta.isAdd()){ - continue; - } else if (executedDelta.getObjectToAdd() != null && executedDelta.getObjectTypeClass().equals(getObjectTypeClass())){ - return true; - } - } - - return false; - } - - abstract public ObjectDeltaObject getObjectDeltaObject() throws SchemaException; - - @Override - public String getOid() { - if (oid == null) { - oid = determineOid(); - } - return oid; - } - - public String determineOid() { - if (getObjectOld() != null && getObjectOld().getOid() != null) { - return getObjectOld().getOid(); - } - if (getObjectCurrent() != null && getObjectCurrent().getOid() != null) { - return getObjectCurrent().getOid(); - } - if (getObjectNew() != null && getObjectNew().getOid() != null) { - return getObjectNew().getOid(); - } - if (getPrimaryDelta() != null && getPrimaryDelta().getOid() != null) { - return getPrimaryDelta().getOid(); - } - if (getSecondaryDelta() != null && getSecondaryDelta().getOid() != null) { - return getSecondaryDelta().getOid(); - } - return null; - } - - /** - * Sets oid to the field but also to the deltas (if applicable). - */ - public void setOid(String oid) { - this.oid = oid; - if (primaryDelta != null && !primaryDelta.isImmutable()) { - primaryDelta.setOid(oid); - } - // TODO What if primary delta is immutable ADD delta and objectNew was taken from it? - // It would be immutable as well in that case. We will see. - if (objectNew != null) { - objectNew.setOid(oid); - } - } - - public PrismObjectDefinition getObjectDefinition() { - if (objectDefinition == null) { - if (objectOld != null) { - objectDefinition = objectOld.getDefinition(); - } else if (objectCurrent != null) { - objectDefinition = objectCurrent.getDefinition(); - } else if (objectNew != null) { - objectDefinition = objectNew.getDefinition(); - } else { - objectDefinition = getNotNullPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(getObjectTypeClass()); - } - } - return objectDefinition; - } - - public boolean isFresh() { - return isFresh; - } - - public void setFresh(boolean isFresh) { - this.isFresh = isFresh; - } - - @NotNull - public Collection getPolicyRules() { - return policyRules; - } - - public void addPolicyRule(EvaluatedPolicyRule policyRule) { - this.policyRules.add(policyRule); - } - - public void clearPolicyRules() { - policyRules.clear(); - } - - public void triggerRule(@NotNull EvaluatedPolicyRule rule, Collection> triggers) { - LensUtil.triggerRule(rule, triggers, policySituations); - } - - @NotNull - public Collection getPolicySituations() { - return policySituations; - } - - /** - * Returns security policy applicable to the object. This means security policy - * applicable directory to focus or projection. It will NOT return global - * security policy. - */ - public SecurityPolicyType getSecurityPolicy() { - return securityPolicy; - } - - public void setSecurityPolicy(SecurityPolicyType securityPolicy) { - this.securityPolicy = securityPolicy; - } - - public CredentialsPolicyType getCredentialsPolicy() { - return securityPolicy != null ? securityPolicy.getCredentials() : null; - } - - public void recompute() throws SchemaException, ConfigurationException { - PrismObject base = getObjectCurrentOrOld(); - ObjectDelta delta = getDelta(); - if (delta == null) { - // No change - objectNew = base; - return; - } - objectNew = delta.computeChangedObject(base); - } - - public void checkConsistence() { - checkConsistence(null); - } - - public void checkConsistence(String contextDesc) { - if (getObjectOld() != null) { - checkConsistence(getObjectOld(), "old "+getElementDesc() , contextDesc); - } - if (getObjectCurrent() != null) { - checkConsistence(getObjectCurrent(), "current "+getElementDesc() , contextDesc); - } - if (primaryDelta != null) { - checkConsistence(primaryDelta, false, getElementDesc()+" primary delta in "+this + (contextDesc == null ? "" : " in " +contextDesc)); - } - if (getObjectNew() != null) { - checkConsistence(getObjectNew(), "new "+getElementDesc(), contextDesc); - } - } - - protected void checkConsistence(ObjectDelta delta, boolean requireOid, String contextDesc) { - try { - delta.checkConsistence(requireOid, true, true, ConsistencyCheckScope.THOROUGH); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException(e.getMessage()+"; in "+contextDesc, e); - } catch (IllegalStateException e) { - throw new IllegalStateException(e.getMessage()+"; in "+contextDesc, e); - } - if (delta.isAdd()) { - checkConsistence(delta.getObjectToAdd(), "add object", contextDesc); - } - } - - protected boolean isRequireSecondaryDeltaOid() { - return primaryDelta == null; - } - - protected void checkConsistence(PrismObject object, String elementDesc, String contextDesc) { - String desc = elementDesc+" in "+this + (contextDesc == null ? "" : " in " +contextDesc); - try { - object.checkConsistence(true, ConsistencyCheckScope.THOROUGH); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException(e.getMessage()+"; in "+desc, e); - } catch (IllegalStateException e) { - throw new IllegalStateException(e.getMessage()+"; in "+desc, e); - } - if (object.getDefinition() == null) { - throw new IllegalStateException("No "+getElementDesc()+" definition "+desc); - } - O objectType = object.asObjectable(); - if (objectType instanceof ShadowType) { - ShadowUtil.checkConsistence((PrismObject) object, desc); - } - } - - /** - * Cleans up the contexts by removing some of the working state. - */ - public abstract void cleanup(); - - public void normalize() { - if (objectNew != null) { - objectNew.normalize(); - } - if (objectOld != null) { - objectOld.normalize(); - } - if (objectCurrent != null) { - objectCurrent.normalize(); - } - if (primaryDelta != null && !primaryDelta.isImmutable()) { - primaryDelta.normalize(); - } - } - - public void adopt(PrismContext prismContext) throws SchemaException { - if (objectNew != null) { - prismContext.adopt(objectNew); - } - if (objectOld != null) { - prismContext.adopt(objectOld); - } - if (objectCurrent != null) { - prismContext.adopt(objectCurrent); - } - if (primaryDelta != null) { - prismContext.adopt(primaryDelta); - } - // TODO: object definition? - } - - public abstract LensElementContext clone(LensContext lensContext); - - void copyValues(LensElementContext clone, LensContext lensContext) { - //noinspection unchecked - clone.lensContext = lensContext; - // This is de-facto immutable - clone.objectDefinition = this.objectDefinition; - clone.objectNew = cloneObject(this.objectNew); - clone.objectOld = cloneObject(this.objectOld); - clone.objectCurrent = cloneObject(this.objectCurrent); - clone.oid = this.oid; - clone.primaryDelta = cloneDelta(this.primaryDelta); - clone.isFresh = this.isFresh; - clone.iteration = this.iteration; - clone.iterationToken = this.iterationToken; - clone.securityPolicy = this.securityPolicy; - } - - protected ObjectDelta cloneDelta(ObjectDelta thisDelta) { - if (thisDelta == null) { - return null; - } - return thisDelta.clone(); - } - - private PrismObject cloneObject(PrismObject thisObject) { - if (thisObject == null) { - return null; - } - return thisObject.clone(); - } - - void storeIntoLensElementContextType(LensElementContextType lensElementContextType, LensContext.ExportType exportType) throws SchemaException { - if (objectOld != null && exportType != LensContext.ExportType.MINIMAL) { - if (exportType == LensContext.ExportType.REDUCED) { - lensElementContextType.setObjectOldRef(ObjectTypeUtil.createObjectRef(objectOld, getPrismContext())); - } else { - lensElementContextType.setObjectOld(objectOld.asObjectable().clone()); - } - } - if (objectCurrent != null && exportType == LensContext.ExportType.TRACE) { - lensElementContextType.setObjectCurrent(objectCurrent.asObjectable().clone()); - } - if (objectNew != null && exportType != LensContext.ExportType.MINIMAL) { - if (exportType == LensContext.ExportType.REDUCED) { - lensElementContextType.setObjectNewRef(ObjectTypeUtil.createObjectRef(objectNew, getPrismContext())); - } else { - lensElementContextType.setObjectNew(objectNew.asObjectable().clone()); - } - } - if (exportType != LensContext.ExportType.MINIMAL) { - lensElementContextType.setPrimaryDelta(primaryDelta != null ? DeltaConvertor.toObjectDeltaType(primaryDelta) : null); - for (LensObjectDeltaOperation executedDelta : executedDeltas) { - lensElementContextType.getExecutedDeltas() - .add(LensContext.simplifyExecutedDelta(executedDelta).toLensObjectDeltaOperationType()); - } - lensElementContextType.setObjectTypeClass(objectTypeClass != null ? objectTypeClass.getName() : null); - lensElementContextType.setOid(oid); - lensElementContextType.setIteration(iteration); - lensElementContextType.setIterationToken(iterationToken); - lensElementContextType.setSynchronizationIntent( - synchronizationIntent != null ? synchronizationIntent.toSynchronizationIntentType() : null); - } - lensElementContextType.setFresh(isFresh); - } - - public void retrieveFromLensElementContextType(LensElementContextType lensElementContextType, Task task, OperationResult result) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, ExpressionEvaluationException { - - ObjectType objectTypeOld = lensElementContextType.getObjectOld(); - this.objectOld = objectTypeOld != null ? (PrismObject) objectTypeOld.asPrismObject() : null; - fixProvisioningTypeInObject(this.objectOld, task, result); - - ObjectType objectTypeNew = lensElementContextType.getObjectNew(); - this.objectNew = objectTypeNew != null ? (PrismObject) objectTypeNew.asPrismObject() : null; - fixProvisioningTypeInObject(this.objectNew, task, result); - - ObjectType object = objectTypeNew != null ? objectTypeNew : objectTypeOld; - - ObjectDeltaType primaryDeltaType = lensElementContextType.getPrimaryDelta(); - this.primaryDelta = primaryDeltaType != null ? (ObjectDelta) DeltaConvertor.createObjectDelta(primaryDeltaType, lensContext.getPrismContext()) : null; - fixProvisioningTypeInDelta(this.primaryDelta, object, task, result); - - for (LensObjectDeltaOperationType eDeltaOperationType : lensElementContextType.getExecutedDeltas()) { - LensObjectDeltaOperation objectDeltaOperation = LensObjectDeltaOperation.fromLensObjectDeltaOperationType(eDeltaOperationType, lensContext.getPrismContext()); - if (objectDeltaOperation.getObjectDelta() != null) { - fixProvisioningTypeInDelta(objectDeltaOperation.getObjectDelta(), object, task, result); - } - this.executedDeltas.add(objectDeltaOperation); - } - - this.oid = lensElementContextType.getOid(); - - this.iteration = lensElementContextType.getIteration() != null ? lensElementContextType.getIteration() : 0; - this.iterationToken = lensElementContextType.getIterationToken(); - this.synchronizationIntent = SynchronizationIntent.fromSynchronizationIntentType(lensElementContextType.getSynchronizationIntent()); - - // note: objectTypeClass is already converted (used in the constructor) - } - - protected void fixProvisioningTypeInDelta(ObjectDelta delta, Objectable object, Task task, OperationResult result) { - if (delta != null && delta.getObjectTypeClass() != null && (ShadowType.class.isAssignableFrom(delta.getObjectTypeClass()) || ResourceType.class.isAssignableFrom(delta.getObjectTypeClass()))) { - try { - lensContext.getProvisioningService().applyDefinition(delta, object, task, result); - } catch (Exception e) { - LOGGER.warn("Error applying provisioning definitions to delta {}: {}", delta, e.getMessage()); - // In case of error just go on. Maybe we do not have correct definition here. But at least we can - // display the GUI pages and maybe we can also salvage the operation. - result.recordWarning(e); - } - } - } - - private void fixProvisioningTypeInObject(PrismObject object, Task task, OperationResult result) { - if (object != null && object.getCompileTimeClass() != null && (ShadowType.class.isAssignableFrom(object.getCompileTimeClass()) || ResourceType.class.isAssignableFrom(object.getCompileTimeClass()))) { - try { - lensContext.getProvisioningService().applyDefinition(object, task, result); - } catch (Exception e) { - LOGGER.warn("Error applying provisioning definitions to object {}: {}", object, e.getMessage()); - // In case of error just go on. Maybe we do not have correct definition here. But at least we can - // display the GUI pages and maybe we can also salvage the operation. - result.recordWarning(e); - } - } - } - - public void checkEncrypted() { - if (objectNew != null) { - CryptoUtil.checkEncrypted(objectNew); - } - if (objectOld != null) { - CryptoUtil.checkEncrypted(objectOld); - } - if (objectCurrent != null) { - CryptoUtil.checkEncrypted(objectCurrent); - } - if (primaryDelta != null) { - CryptoUtil.checkEncrypted(primaryDelta); - } - } - - public boolean operationMatches(ChangeTypeType operation) { - switch (operation) { - case ADD: - return getOperation() == SimpleOperationName.ADD; - case MODIFY: - return getOperation() == SimpleOperationName.MODIFY; - case DELETE: - return getOperation() == SimpleOperationName.DELETE; - } - throw new IllegalArgumentException("Unknown operation "+operation); - } - - protected abstract String getElementDefaultDesc(); - - protected String getElementDesc() { - PrismObject object = getObjectNew(); - if (object == null) { - object = getObjectOld(); - } - if (object == null) { - object = getObjectCurrent(); - } - if (object == null) { - return getElementDefaultDesc(); - } - return object.toDebugType(); - } - - protected String getDebugDumpTitle() { - return StringUtils.capitalize(getElementDesc()); - } - - protected String getDebugDumpTitle(String suffix) { - return getDebugDumpTitle()+" "+suffix; - } - - public abstract String getHumanReadableName(); - - public String getObjectReadVersion() { - // Do NOT use version from object current. - // Current object may be re-read, but the computation - // might be based on older data (objectOld). -// if (getObjectCurrent() != null) { -// return getObjectCurrent().getVersion(); -// } - if (getObjectOld() != null) { - return getObjectOld().getVersion(); - } - return null; - } - - public PrismObject getObjectCurrentOrOld() { - return objectCurrent != null ? objectCurrent : objectOld; - } - - @Override - public boolean isOfType(Class aClass) { - if (aClass.isAssignableFrom(objectTypeClass)) { - return true; - } - PrismObject object = getObjectAny(); - return object != null && aClass.isAssignableFrom(object.asObjectable().getClass()); - } - - public abstract void deleteSecondaryDeltas(); - - public S_ItemEntry deltaBuilder() throws SchemaException { - return getPrismContext().deltaFor(getObjectTypeClass()); - } - - public void forEachObject(Consumer> consumer) { - if (objectCurrent != null) { - consumer.accept(objectCurrent); - } - if (objectOld != null) { - consumer.accept(objectOld); - } - if (objectNew != null) { - consumer.accept(objectNew); - } - } - - public void forEachDelta(Consumer> consumer) { - if (primaryDelta != null) { - consumer.accept(primaryDelta); - } - } - - public void finishBuild() { - if (primaryDelta != null) { - primaryDelta.normalize(); - primaryDelta.freeze(); - } - } -} +/* + * Copyright (c) 2010-2018 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.model.impl.lens; + +import java.util.*; +import java.util.function.Consumer; + +import com.evolveum.midpoint.model.api.context.SynchronizationIntent; +import com.evolveum.midpoint.prism.ConsistencyCheckScope; +import com.evolveum.midpoint.prism.Objectable; +import com.evolveum.midpoint.prism.delta.*; +import com.evolveum.midpoint.prism.delta.builder.S_ItemEntry; +import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy; +import com.evolveum.midpoint.prism.util.ObjectDeltaObject; +import com.evolveum.midpoint.schema.DeltaConvertor; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; +import com.evolveum.midpoint.util.exception.CommunicationException; +import com.evolveum.midpoint.util.exception.ConfigurationException; +import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.prism.xml.ns._public.types_3.ChangeTypeType; +import com.evolveum.prism.xml.ns._public.types_3.ObjectDeltaType; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.Validate; + +import com.evolveum.midpoint.common.crypto.CryptoUtil; +import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; +import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRuleTrigger; +import com.evolveum.midpoint.model.api.context.ModelElementContext; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.PrismObjectDefinition; +import com.evolveum.midpoint.schema.util.ShadowUtil; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import org.jetbrains.annotations.NotNull; + +/** + * @author semancik + * + */ +public abstract class LensElementContext implements ModelElementContext, Cloneable { + + private static final long serialVersionUID = 1649567559396392861L; + + private static final Trace LOGGER = TraceManager.getTrace(LensElementContext.class); + + private PrismObject objectOld; + private transient PrismObject objectCurrent; + private PrismObject objectNew; + private ObjectDelta primaryDelta; + @NotNull private final List> executedDeltas = new ArrayList<>(); + @NotNull private final Class objectTypeClass; + private String oid = null; + private int iteration; + private String iterationToken; + + transient private SecurityPolicyType securityPolicy; + + /** + * These are policy state modifications that should be applied. + * Currently we apply them in ChangeExecutor.executeChanges only. + * + * In the future we plan to be able to apply some state modifications even + * if the clockwork is exited in non-standard way (e.g. in primary state or with an exception). + * But we must be sure what policy state to store, because some constraints might be triggered + * because of expectation of future state (like conflicting assignment is added etc.) + * --- + * Although placed in LensElementContext, support for this data is currently implemented only for focus, not for projections. + */ + @NotNull private final List> pendingObjectPolicyStateModifications = new ArrayList<>(); + + /** + * Policy state modifications for assignments. + * + * Although we put here also deltas for assignments that are to be deleted, we do not execute these + * (because we implement execution only for the standard exit-path from the clockwork). + */ + @NotNull private final Map>> pendingAssignmentPolicyStateModifications = new HashMap<>(); + + private SynchronizationIntent synchronizationIntent; + + private transient boolean isFresh = false; + + private LensContext lensContext; + + private transient PrismObjectDefinition objectDefinition = null; + + private final Collection policyRules = new ArrayList<>(); + private final Collection policySituations = new ArrayList<>(); + + public LensElementContext(@NotNull Class objectTypeClass, LensContext lensContext) { + super(); + Validate.notNull(objectTypeClass, "Object class is null"); + Validate.notNull(lensContext, "Lens context is null"); + this.lensContext = lensContext; + this.objectTypeClass = objectTypeClass; + } + + public int getIteration() { + return iteration; + } + + public void setIteration(int iteration) { + this.iteration = iteration; + } + + public String getIterationToken() { + return iterationToken; + } + + public void setIterationToken(String iterationToken) { + this.iterationToken = iterationToken; + } + + public SynchronizationIntent getSynchronizationIntent() { + return synchronizationIntent; + } + + public void setSynchronizationIntent(SynchronizationIntent synchronizationIntent) { + this.synchronizationIntent = synchronizationIntent; + } + + public LensContext getLensContext() { + return lensContext; + } + + protected PrismContext getNotNullPrismContext() { + return getLensContext().getNotNullPrismContext(); + } + + @Override + public Class getObjectTypeClass() { + return objectTypeClass; + } + + public boolean represents(Class type) { + return type.isAssignableFrom(objectTypeClass); + } + + public PrismContext getPrismContext() { + return lensContext.getPrismContext(); + } + + @Override + public PrismObject getObjectOld() { + return objectOld; + } + + public void setObjectOld(PrismObject objectOld) { + this.objectOld = objectOld; + } + + @Override + public PrismObject getObjectCurrent() { + return objectCurrent; + } + + public void setObjectCurrent(PrismObject objectCurrent) { + this.objectCurrent = objectCurrent; + } + + public PrismObject getObjectAny() { + if (objectNew != null) { + return objectNew; + } + if (objectCurrent != null) { + return objectCurrent; + } + return objectOld; + } + + /** + * Sets current and possibly also old object. This method is used with + * freshly loaded object. The object is set as current object. + * If the old object was not initialized yet (and if it should be initialized) + * then the object is also set as old object. + */ + public void setLoadedObject(PrismObject object) { + setObjectCurrent(object); + if (objectOld == null && !isAdd()) { + setObjectOld(object.clone()); + } + } + + @Override + public PrismObject getObjectNew() { + return objectNew; + } + + public void setObjectNew(PrismObject objectNew) { + this.objectNew = objectNew; + } + + @Override + public ObjectDelta getPrimaryDelta() { + return primaryDelta; + } + + public boolean hasPrimaryDelta() { + return primaryDelta != null && !primaryDelta.isEmpty(); + } + + /** + * As getPrimaryDelta() but caters for the possibility that an object already exists. + * So, if the primary delta is ADD and object already exists, it should be changed somehow, + * e.g. to MODIFY delta or to null. + * + * Actually, the question is what to do with the attribute values if changed to MODIFY. + * (a) Should they become REPLACE item deltas? (b) ADD ones? + * (c) Or should we compute a difference from objectCurrent to objectToAdd, hoping that + * secondary deltas will re-add everything that might be unknowingly removed by this step? + * (d) Or should we simply ignore ADD delta altogether, hoping that it was executed + * so it need not be repeated? + * + * And, should not we report AlreadyExistingException instead? + * + * It seems that (c) i.e. reverting back to objectToAdd is not a good idea at all. For example, this + * may erase linkRefs for good. + * + * For the time being let us proceed with (d), i.e. ignoring such a delta. + * + * TODO is this OK???? [med] + * + * @return + */ + public ObjectDelta getFixedPrimaryDelta() { + if (primaryDelta == null || !primaryDelta.isAdd() || objectCurrent == null) { + return primaryDelta; // nothing to do + } + // Object does exist. Let's ignore the delta - see description above. + return null; + } + + public void setPrimaryDelta(ObjectDelta primaryDelta) { + this.primaryDelta = primaryDelta; + } + + public void addPrimaryDelta(ObjectDelta delta) throws SchemaException { + if (primaryDelta == null) { + primaryDelta = delta; + } else { + primaryDelta.merge(delta); + } + } + + public void swallowToPrimaryDelta(ItemDelta itemDelta) throws SchemaException { + modifyOrCreatePrimaryDelta( + delta -> delta.swallow(itemDelta), + () -> { + ObjectDelta newPrimaryDelta = getPrismContext().deltaFactory().object().create(getObjectTypeClass(), ChangeType.MODIFY); + newPrimaryDelta.setOid(oid); + newPrimaryDelta.addModification(itemDelta); + return newPrimaryDelta; + }); + } + + @FunctionalInterface + private interface DeltaModifier { + void modify(ObjectDelta delta) throws SchemaException; + } + + @FunctionalInterface + private interface DeltaCreator { + ObjectDelta create() throws SchemaException; + } + + private void modifyOrCreatePrimaryDelta(DeltaModifier modifier, DeltaCreator creator) throws SchemaException { + if (primaryDelta == null) { + primaryDelta = creator.create(); + } else if (!primaryDelta.isImmutable()) { + modifier.modify(primaryDelta); + } else { + primaryDelta = primaryDelta.clone(); + modifier.modify(primaryDelta); + primaryDelta.freeze(); + } + } + + public abstract void swallowToSecondaryDelta(ItemDelta itemDelta) throws SchemaException; + + // TODO deduplicate with swallowToSecondaryDelta in LensFocusContext + public ObjectDelta swallowToDelta(ObjectDelta originalDelta, ItemDelta propDelta) throws SchemaException { + if (originalDelta == null) { + originalDelta = getPrismContext().deltaFactory().object().create(getObjectTypeClass(), ChangeType.MODIFY); + originalDelta.setOid(getOid()); + } else if (originalDelta.containsModification(propDelta, EquivalenceStrategy.LITERAL_IGNORE_METADATA)) { // todo why literal? + return originalDelta; + } + originalDelta.swallow(propDelta); + return originalDelta; + } + + /** + * Returns collection of all deltas that this element context contains. + * This is a nice method to use if we want to inspect all deltas (e.g. look for a changed item) + * but we want to avoid the overhead of merging all the deltas together. + */ + public abstract Collection> getAllDeltas(); + + @NotNull + public List> getPendingObjectPolicyStateModifications() { + return pendingObjectPolicyStateModifications; + } + + public void clearPendingObjectPolicyStateModifications() { + pendingObjectPolicyStateModifications.clear(); + } + + public void addToPendingObjectPolicyStateModifications(ItemDelta modification) { + pendingObjectPolicyStateModifications.add(modification); + } + + @NotNull + public Map>> getPendingAssignmentPolicyStateModifications() { + return pendingAssignmentPolicyStateModifications; + } + + public void clearPendingAssignmentPolicyStateModifications() { + pendingAssignmentPolicyStateModifications.clear(); + } + + public void addToPendingAssignmentPolicyStateModifications(@NotNull AssignmentType assignment, @NotNull PlusMinusZero mode, @NotNull ItemDelta modification) { + AssignmentSpec spec = new AssignmentSpec(assignment, mode); + pendingAssignmentPolicyStateModifications.computeIfAbsent(spec, k -> new ArrayList<>()).add(modification); + } + + public boolean isModify() { + // TODO I'm not sure why isModify checks both primary and secondary deltas for focus context, while + // isAdd and isDelete care only for the primary delta. + return ObjectDelta.isModify(getPrimaryDelta()) || ObjectDelta.isModify(getSecondaryDelta()); + } + + @NotNull + public SimpleOperationName getOperation() { + if (isAdd()) { + return SimpleOperationName.ADD; + } else if (isDelete()) { + return SimpleOperationName.DELETE; + } else { + return SimpleOperationName.MODIFY; + } + } + + @NotNull + @Override + public List> getExecutedDeltas() { + return executedDeltas; + } + + List> getExecutedDeltas(Boolean audited) { + if (audited == null) { + return executedDeltas; + } + List> deltas = new ArrayList<>(); + for (LensObjectDeltaOperation delta: executedDeltas) { + if (delta.isAudited() == audited) { + deltas.add(delta); + } + } + return deltas; + } + + public void markExecutedDeltasAudited() { + for(LensObjectDeltaOperation executedDelta: executedDeltas) { + executedDelta.setAudited(true); + } + } + + public void addToExecutedDeltas(LensObjectDeltaOperation executedDelta) { + executedDeltas.add(executedDelta.clone()); // must be cloned because e.g. for ADD deltas the object gets modified afterwards + } + + /** + * Returns user delta, both primary and secondary (merged together). + * The returned object is (kind of) immutable. Changing it may do strange things (but most likely the changes will be lost). + */ + public ObjectDelta getDelta() throws SchemaException { + return ObjectDeltaCollectionsUtil.union(primaryDelta, getSecondaryDelta()); + } + + public ObjectDelta getFixedDelta() throws SchemaException { + return ObjectDeltaCollectionsUtil.union(getFixedPrimaryDelta(), getSecondaryDelta()); + } + + public boolean wasAddExecuted() { + + for (LensObjectDeltaOperation executedDeltaOperation : getExecutedDeltas()){ + ObjectDelta executedDelta = executedDeltaOperation.getObjectDelta(); + if (!executedDelta.isAdd()){ + continue; + } else if (executedDelta.getObjectToAdd() != null && executedDelta.getObjectTypeClass().equals(getObjectTypeClass())){ + return true; + } + } + + return false; + } + + abstract public ObjectDeltaObject getObjectDeltaObject() throws SchemaException; + + @Override + public String getOid() { + if (oid == null) { + oid = determineOid(); + } + return oid; + } + + public String determineOid() { + if (getObjectOld() != null && getObjectOld().getOid() != null) { + return getObjectOld().getOid(); + } + if (getObjectCurrent() != null && getObjectCurrent().getOid() != null) { + return getObjectCurrent().getOid(); + } + if (getObjectNew() != null && getObjectNew().getOid() != null) { + return getObjectNew().getOid(); + } + if (getPrimaryDelta() != null && getPrimaryDelta().getOid() != null) { + return getPrimaryDelta().getOid(); + } + if (getSecondaryDelta() != null && getSecondaryDelta().getOid() != null) { + return getSecondaryDelta().getOid(); + } + return null; + } + + /** + * Sets oid to the field but also to the deltas (if applicable). + */ + public void setOid(String oid) { + this.oid = oid; + if (primaryDelta != null && !primaryDelta.isImmutable()) { + primaryDelta.setOid(oid); + } + // TODO What if primary delta is immutable ADD delta and objectNew was taken from it? + // It would be immutable as well in that case. We will see. + if (objectNew != null) { + objectNew.setOid(oid); + } + } + + public PrismObjectDefinition getObjectDefinition() { + if (objectDefinition == null) { + if (objectOld != null) { + objectDefinition = objectOld.getDefinition(); + } else if (objectCurrent != null) { + objectDefinition = objectCurrent.getDefinition(); + } else if (objectNew != null) { + objectDefinition = objectNew.getDefinition(); + } else { + objectDefinition = getNotNullPrismContext().getSchemaRegistry().findObjectDefinitionByCompileTimeClass(getObjectTypeClass()); + } + } + return objectDefinition; + } + + public boolean isFresh() { + return isFresh; + } + + public void setFresh(boolean isFresh) { + this.isFresh = isFresh; + } + + @NotNull + public Collection getPolicyRules() { + return policyRules; + } + + public void addPolicyRule(EvaluatedPolicyRule policyRule) { + this.policyRules.add(policyRule); + } + + public void clearPolicyRules() { + policyRules.clear(); + } + + public void triggerRule(@NotNull EvaluatedPolicyRule rule, Collection> triggers) { + LensUtil.triggerRule(rule, triggers, policySituations); + } + + @NotNull + public Collection getPolicySituations() { + return policySituations; + } + + /** + * Returns security policy applicable to the object. This means security policy + * applicable directory to focus or projection. It will NOT return global + * security policy. + */ + public SecurityPolicyType getSecurityPolicy() { + return securityPolicy; + } + + public void setSecurityPolicy(SecurityPolicyType securityPolicy) { + this.securityPolicy = securityPolicy; + } + + public CredentialsPolicyType getCredentialsPolicy() { + return securityPolicy != null ? securityPolicy.getCredentials() : null; + } + + public void recompute() throws SchemaException, ConfigurationException { + PrismObject base = getObjectCurrentOrOld(); + ObjectDelta delta = getDelta(); + if (delta == null) { + // No change + objectNew = base; + return; + } + objectNew = delta.computeChangedObject(base); + } + + public void checkConsistence() { + checkConsistence(null); + } + + public void checkConsistence(String contextDesc) { + if (getObjectOld() != null) { + checkConsistence(getObjectOld(), "old "+getElementDesc() , contextDesc); + } + if (getObjectCurrent() != null) { + checkConsistence(getObjectCurrent(), "current "+getElementDesc() , contextDesc); + } + if (primaryDelta != null) { + checkConsistence(primaryDelta, false, getElementDesc()+" primary delta in "+this + (contextDesc == null ? "" : " in " +contextDesc)); + } + if (getObjectNew() != null) { + checkConsistence(getObjectNew(), "new "+getElementDesc(), contextDesc); + } + } + + protected void checkConsistence(ObjectDelta delta, boolean requireOid, String contextDesc) { + try { + delta.checkConsistence(requireOid, true, true, ConsistencyCheckScope.THOROUGH); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(e.getMessage()+"; in "+contextDesc, e); + } catch (IllegalStateException e) { + throw new IllegalStateException(e.getMessage()+"; in "+contextDesc, e); + } + if (delta.isAdd()) { + checkConsistence(delta.getObjectToAdd(), "add object", contextDesc); + } + } + + protected boolean isRequireSecondaryDeltaOid() { + return primaryDelta == null; + } + + protected void checkConsistence(PrismObject object, String elementDesc, String contextDesc) { + String desc = elementDesc+" in "+this + (contextDesc == null ? "" : " in " +contextDesc); + try { + object.checkConsistence(true, ConsistencyCheckScope.THOROUGH); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException(e.getMessage()+"; in "+desc, e); + } catch (IllegalStateException e) { + throw new IllegalStateException(e.getMessage()+"; in "+desc, e); + } + if (object.getDefinition() == null) { + throw new IllegalStateException("No "+getElementDesc()+" definition "+desc); + } + O objectType = object.asObjectable(); + if (objectType instanceof ShadowType) { + ShadowUtil.checkConsistence((PrismObject) object, desc); + } + } + + /** + * Cleans up the contexts by removing some of the working state. + */ + public abstract void cleanup(); + + public void normalize() { + if (objectNew != null) { + objectNew.normalize(); + } + if (objectOld != null) { + objectOld.normalize(); + } + if (objectCurrent != null) { + objectCurrent.normalize(); + } + if (primaryDelta != null && !primaryDelta.isImmutable()) { + primaryDelta.normalize(); + } + } + + public void adopt(PrismContext prismContext) throws SchemaException { + if (objectNew != null) { + prismContext.adopt(objectNew); + } + if (objectOld != null) { + prismContext.adopt(objectOld); + } + if (objectCurrent != null) { + prismContext.adopt(objectCurrent); + } + if (primaryDelta != null) { + prismContext.adopt(primaryDelta); + } + // TODO: object definition? + } + + public abstract LensElementContext clone(LensContext lensContext); + + void copyValues(LensElementContext clone, LensContext lensContext) { + //noinspection unchecked + clone.lensContext = lensContext; + // This is de-facto immutable + clone.objectDefinition = this.objectDefinition; + clone.objectNew = cloneObject(this.objectNew); + clone.objectOld = cloneObject(this.objectOld); + clone.objectCurrent = cloneObject(this.objectCurrent); + clone.oid = this.oid; + clone.primaryDelta = cloneDelta(this.primaryDelta); + clone.isFresh = this.isFresh; + clone.iteration = this.iteration; + clone.iterationToken = this.iterationToken; + clone.securityPolicy = this.securityPolicy; + } + + protected ObjectDelta cloneDelta(ObjectDelta thisDelta) { + if (thisDelta == null) { + return null; + } + return thisDelta.clone(); + } + + private PrismObject cloneObject(PrismObject thisObject) { + if (thisObject == null) { + return null; + } + return thisObject.clone(); + } + + void storeIntoLensElementContextType(LensElementContextType lensElementContextType, LensContext.ExportType exportType) throws SchemaException { + if (objectOld != null && exportType != LensContext.ExportType.MINIMAL) { + if (exportType == LensContext.ExportType.REDUCED) { + lensElementContextType.setObjectOldRef(ObjectTypeUtil.createObjectRef(objectOld, getPrismContext())); + } else { + lensElementContextType.setObjectOld(objectOld.asObjectable().clone()); + } + } + if (objectCurrent != null && exportType == LensContext.ExportType.TRACE) { + lensElementContextType.setObjectCurrent(objectCurrent.asObjectable().clone()); + } + if (objectNew != null && exportType != LensContext.ExportType.MINIMAL) { + if (exportType == LensContext.ExportType.REDUCED) { + lensElementContextType.setObjectNewRef(ObjectTypeUtil.createObjectRef(objectNew, getPrismContext())); + } else { + lensElementContextType.setObjectNew(objectNew.asObjectable().clone()); + } + } + if (exportType != LensContext.ExportType.MINIMAL) { + lensElementContextType.setPrimaryDelta(primaryDelta != null ? DeltaConvertor.toObjectDeltaType(primaryDelta) : null); + for (LensObjectDeltaOperation executedDelta : executedDeltas) { + lensElementContextType.getExecutedDeltas() + .add(LensContext.simplifyExecutedDelta(executedDelta).toLensObjectDeltaOperationType()); + } + lensElementContextType.setObjectTypeClass(objectTypeClass.getName()); + lensElementContextType.setOid(oid); + lensElementContextType.setIteration(iteration); + lensElementContextType.setIterationToken(iterationToken); + lensElementContextType.setSynchronizationIntent( + synchronizationIntent != null ? synchronizationIntent.toSynchronizationIntentType() : null); + } + lensElementContextType.setFresh(isFresh); + } + + public void retrieveFromLensElementContextType(LensElementContextType lensElementContextType, Task task, OperationResult result) throws SchemaException, ConfigurationException, ObjectNotFoundException, CommunicationException, ExpressionEvaluationException { + + ObjectType objectTypeOld = lensElementContextType.getObjectOld(); + this.objectOld = objectTypeOld != null ? (PrismObject) objectTypeOld.asPrismObject() : null; + fixProvisioningTypeInObject(this.objectOld, task, result); + + ObjectType objectTypeNew = lensElementContextType.getObjectNew(); + this.objectNew = objectTypeNew != null ? (PrismObject) objectTypeNew.asPrismObject() : null; + fixProvisioningTypeInObject(this.objectNew, task, result); + + ObjectType object = objectTypeNew != null ? objectTypeNew : objectTypeOld; + + ObjectDeltaType primaryDeltaType = lensElementContextType.getPrimaryDelta(); + this.primaryDelta = primaryDeltaType != null ? (ObjectDelta) DeltaConvertor.createObjectDelta(primaryDeltaType, lensContext.getPrismContext()) : null; + fixProvisioningTypeInDelta(this.primaryDelta, object, task, result); + + for (LensObjectDeltaOperationType eDeltaOperationType : lensElementContextType.getExecutedDeltas()) { + LensObjectDeltaOperation objectDeltaOperation = LensObjectDeltaOperation.fromLensObjectDeltaOperationType(eDeltaOperationType, lensContext.getPrismContext()); + if (objectDeltaOperation.getObjectDelta() != null) { + fixProvisioningTypeInDelta(objectDeltaOperation.getObjectDelta(), object, task, result); + } + this.executedDeltas.add(objectDeltaOperation); + } + + this.oid = lensElementContextType.getOid(); + + this.iteration = lensElementContextType.getIteration() != null ? lensElementContextType.getIteration() : 0; + this.iterationToken = lensElementContextType.getIterationToken(); + this.synchronizationIntent = SynchronizationIntent.fromSynchronizationIntentType(lensElementContextType.getSynchronizationIntent()); + + // note: objectTypeClass is already converted (used in the constructor) + } + + protected void fixProvisioningTypeInDelta(ObjectDelta delta, Objectable object, Task task, OperationResult result) { + if (delta != null && delta.getObjectTypeClass() != null && (ShadowType.class.isAssignableFrom(delta.getObjectTypeClass()) || ResourceType.class.isAssignableFrom(delta.getObjectTypeClass()))) { + try { + lensContext.getProvisioningService().applyDefinition(delta, object, task, result); + } catch (Exception e) { + LOGGER.warn("Error applying provisioning definitions to delta {}: {}", delta, e.getMessage()); + // In case of error just go on. Maybe we do not have correct definition here. But at least we can + // display the GUI pages and maybe we can also salvage the operation. + result.recordWarning(e); + } + } + } + + private void fixProvisioningTypeInObject(PrismObject object, Task task, OperationResult result) { + if (object != null && object.getCompileTimeClass() != null && (ShadowType.class.isAssignableFrom(object.getCompileTimeClass()) || ResourceType.class.isAssignableFrom(object.getCompileTimeClass()))) { + try { + lensContext.getProvisioningService().applyDefinition(object, task, result); + } catch (Exception e) { + LOGGER.warn("Error applying provisioning definitions to object {}: {}", object, e.getMessage()); + // In case of error just go on. Maybe we do not have correct definition here. But at least we can + // display the GUI pages and maybe we can also salvage the operation. + result.recordWarning(e); + } + } + } + + public void checkEncrypted() { + if (objectNew != null) { + CryptoUtil.checkEncrypted(objectNew); + } + if (objectOld != null) { + CryptoUtil.checkEncrypted(objectOld); + } + if (objectCurrent != null) { + CryptoUtil.checkEncrypted(objectCurrent); + } + if (primaryDelta != null) { + CryptoUtil.checkEncrypted(primaryDelta); + } + } + + public boolean operationMatches(ChangeTypeType operation) { + switch (operation) { + case ADD: + return getOperation() == SimpleOperationName.ADD; + case MODIFY: + return getOperation() == SimpleOperationName.MODIFY; + case DELETE: + return getOperation() == SimpleOperationName.DELETE; + } + throw new IllegalArgumentException("Unknown operation "+operation); + } + + protected abstract String getElementDefaultDesc(); + + protected String getElementDesc() { + PrismObject object = getObjectNew(); + if (object == null) { + object = getObjectOld(); + } + if (object == null) { + object = getObjectCurrent(); + } + if (object == null) { + return getElementDefaultDesc(); + } + return object.toDebugType(); + } + + protected String getDebugDumpTitle() { + return StringUtils.capitalize(getElementDesc()); + } + + protected String getDebugDumpTitle(String suffix) { + return getDebugDumpTitle()+" "+suffix; + } + + public abstract String getHumanReadableName(); + + public String getObjectReadVersion() { + // Do NOT use version from object current. + // Current object may be re-read, but the computation + // might be based on older data (objectOld). +// if (getObjectCurrent() != null) { +// return getObjectCurrent().getVersion(); +// } + if (getObjectOld() != null) { + return getObjectOld().getVersion(); + } + return null; + } + + public PrismObject getObjectCurrentOrOld() { + return objectCurrent != null ? objectCurrent : objectOld; + } + + @Override + public boolean isOfType(Class aClass) { + if (aClass.isAssignableFrom(objectTypeClass)) { + return true; + } + PrismObject object = getObjectAny(); + return object != null && aClass.isAssignableFrom(object.asObjectable().getClass()); + } + + public abstract void deleteSecondaryDeltas(); + + public S_ItemEntry deltaBuilder() throws SchemaException { + return getPrismContext().deltaFor(getObjectTypeClass()); + } + + public void forEachObject(Consumer> consumer) { + if (objectCurrent != null) { + consumer.accept(objectCurrent); + } + if (objectOld != null) { + consumer.accept(objectOld); + } + if (objectNew != null) { + consumer.accept(objectNew); + } + } + + public void forEachDelta(Consumer> consumer) { + if (primaryDelta != null) { + consumer.accept(primaryDelta); + } + } + + public void finishBuild() { + if (primaryDelta != null) { + primaryDelta.normalize(); + primaryDelta.freeze(); + } + } +} diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensObjectDeltaOperation.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensObjectDeltaOperation.java index 254e6b7968e..bd7c4b7ca6e 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensObjectDeltaOperation.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/lens/LensObjectDeltaOperation.java @@ -18,7 +18,6 @@ import org.jetbrains.annotations.NotNull; import java.io.Serializable; -import java.util.List; /** * @author semancik @@ -58,10 +57,14 @@ protected String getDebugDumpClassName() { return "LensObjectDeltaOperation"; } - @NotNull - public LensObjectDeltaOperationType toLensObjectDeltaOperationType() throws SchemaException { + @NotNull LensObjectDeltaOperationType toLensObjectDeltaOperationType() throws SchemaException { LensObjectDeltaOperationType retval = new LensObjectDeltaOperationType(); - retval.setObjectDeltaOperation(DeltaConvertor.toObjectDeltaOperationType(this, DeltaConversionOptions.createSerializeReferenceNames())); + DeltaConversionOptions options = DeltaConversionOptions.createSerializeReferenceNames(); + // Escaping invalid characters in serialized deltas could be questionable; see MID-6262. + // But because we currently do not use the deltas for other than human readers we can afford + // to replace invalid characters by descriptive text. + options.setEscapeInvalidCharacters(true); + retval.setObjectDeltaOperation(DeltaConvertor.toObjectDeltaOperationType(this, options)); retval.setAudited(audited); return retval; } diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/misc/TestTracing.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/misc/TestTracing.java new file mode 100644 index 00000000000..5d00a30b3c5 --- /dev/null +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/misc/TestTracing.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2010-2018 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.model.intest.misc; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; + +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.ClassMode; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +import com.evolveum.midpoint.model.api.ModelExecuteOptions; +import com.evolveum.midpoint.model.intest.AbstractEmptyModelIntegrationTest; +import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.delta.ObjectDelta; +import com.evolveum.midpoint.schema.SearchResultList; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.traces.TraceParser; +import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.test.DummyTestResource; +import com.evolveum.midpoint.test.TestResource; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; + +@ContextConfiguration(locations = { "classpath:ctx-model-intest-test-main.xml" }) +@DirtiesContext(classMode = ClassMode.AFTER_CLASS) +public class TestTracing extends AbstractEmptyModelIntegrationTest { + + public static final File TEST_DIR = new File("src/test/resources/tracing"); + + private static final File SYSTEM_CONFIGURATION_FILE = new File(TEST_DIR, "system-configuration.xml"); + + private static final TestResource USER_JOE = new TestResource<>(TEST_DIR, "user-joe.xml", "c279c1c8-a160-4442-88b2-72b358e0c745"); + private static final TestResource ROLE_ILLEGAL = new TestResource<>(TEST_DIR, "role-illegal.xml", "13ca97ae-5919-42fb-91fb-cbc88704fd91"); + private static final DummyTestResource RESOURCE_ILLEGAL = new DummyTestResource(TEST_DIR, "resource-illegal.xml", "793bb9f5-edae-4251-bce7-4e99a72ac23f", "illegal"); + + @Override + public void initSystem(Task initTask, OperationResult initResult) + throws Exception { + super.initSystem(initTask, initResult); + + repoAdd(USER_JOE, initResult); + repoAdd(ROLE_ILLEGAL, initResult); + + initDummyResource(RESOURCE_ILLEGAL, initTask, initResult); + assertSuccess(modelService.testResource(RESOURCE_ILLEGAL.oid, initTask)); + } + + @Override + protected File getSystemConfigurationFile() { + return SYSTEM_CONFIGURATION_FILE; + } + + @Test + public void test100IllegalChars() throws Exception { + given(); + Task task = getTestTask(); + OperationResult result = getTestOperationResult(); + + when(); + ModelExecuteOptions options = new ModelExecuteOptions(); + options.setTracingProfile(createModelAndProvisioningLoggingTracingProfile()); + + ObjectDelta delta = deltaFor(UserType.class) + .item(UserType.F_ASSIGNMENT) + .add( + new AssignmentType(prismContext) + .targetRef(ROLE_ILLEGAL.oid, RoleType.COMPLEX_TYPE), + new AssignmentType(prismContext) + .beginConstruction() + .resourceRef(RESOURCE_ILLEGAL.oid, ResourceType.COMPLEX_TYPE) + .end() + ) + .asObjectDelta(USER_JOE.oid); + + executeChanges(delta, options, task, result); + + then(); + assertSuccess(result); + assertUserAfter(USER_JOE.oid) + .assignments() + .assertAssignments(2) + .end() + .links() + .assertLinks(1); + + RESOURCE_ILLEGAL.controller.assertAccountByUsername("joe") + .assertFullName("A\u0007B"); + + SearchResultList> reportOutputs = + repositoryService.searchObjects(ReportOutputType.class, null, null, result); + assertThat(reportOutputs.size()).as("# of report outputs").isEqualTo(1); + String file = reportOutputs.get(0).asObjectable().getFilePath(); + + TraceParser parser = new TraceParser(prismContext); + TracingOutputType parsed = parser.parse(new File(file)); + System.out.println("Tracing output parsed OK: " + parsed); + } +} diff --git a/model/model-intest/src/test/resources/tracing/resource-illegal.xml b/model/model-intest/src/test/resources/tracing/resource-illegal.xml new file mode 100644 index 00000000000..75bac3a7ff0 --- /dev/null +++ b/model/model-intest/src/test/resources/tracing/resource-illegal.xml @@ -0,0 +1,109 @@ + + + + + + Dummy Resource + + + + + connectorType + com.evolveum.icf.dummy.connector.DummyConnector + + + connectorVersion + 2.0 + + + + + + + + illegal + + + + false + false + false + + + + + + + account + default + true + ri:AccountObjectClass + + icfs:name + + strong + + name + + + + + ri:fullname + Full Name + + + + + + + + + + + ri:AccountObjectClass + account + default + + + name + + $account/attributes/icfs:name + + + + + linked + true + + + deleted + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#unlink + + + + unlinked + + http://midpoint.evolveum.com/xml/ns/public/model/action-3#link + + + + unmatched + + + + diff --git a/model/model-intest/src/test/resources/tracing/role-illegal.xml b/model/model-intest/src/test/resources/tracing/role-illegal.xml new file mode 100644 index 00000000000..cbe381cae7e --- /dev/null +++ b/model/model-intest/src/test/resources/tracing/role-illegal.xml @@ -0,0 +1,29 @@ + + + + illegal + + + + strong + + name + + + + + + + + + diff --git a/model/model-intest/src/test/resources/tracing/system-configuration.xml b/model/model-intest/src/test/resources/tracing/system-configuration.xml new file mode 100644 index 00000000000..c9eecb1c115 --- /dev/null +++ b/model/model-intest/src/test/resources/tracing/system-configuration.xml @@ -0,0 +1,16 @@ + + + + SystemConfiguration + + + true + + + diff --git a/model/model-intest/src/test/resources/tracing/user-joe.xml b/model/model-intest/src/test/resources/tracing/user-joe.xml new file mode 100644 index 00000000000..1eefaf9b4c1 --- /dev/null +++ b/model/model-intest/src/test/resources/tracing/user-joe.xml @@ -0,0 +1,11 @@ + + + + joe + diff --git a/model/model-intest/testng-integration-full.xml b/model/model-intest/testng-integration-full.xml index 88d0dd66bbb..bd661ce986c 100644 --- a/model/model-intest/testng-integration-full.xml +++ b/model/model-intest/testng-integration-full.xml @@ -65,6 +65,7 @@ + diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index ab784b6a1cb..3607ba91b4f 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.model.test; import static java.util.Collections.singleton; +import static org.assertj.core.api.Assertions.assertThat; import static org.testng.AssertJUnit.*; import static com.evolveum.midpoint.prism.PrismObject.asObjectableList; @@ -1660,6 +1661,32 @@ protected void assignPolicyRule(Class type, String focu assign(type, focusOid, assignmentType, task, result); } + protected void assign(TestResource assignee, TestResource assigned, QName relation, ModelExecuteOptions options, + Task task, OperationResult result) throws SchemaException, CommunicationException, ObjectAlreadyExistsException, + ExpressionEvaluationException, PolicyViolationException, SecurityViolationException, ConfigurationException, ObjectNotFoundException { + ObjectDelta delta = deltaFor(assignee.getObjectClass()) + .item(UserType.F_ASSIGNMENT) + .add(ObjectTypeUtil.createAssignmentTo(assigned.object, relation)) + .asObjectDelta(assignee.oid); + executeChanges(delta, options, task, result); + } + + protected void unassignIfSingle(TestResource assignee, TestResource assigned, QName relation, ModelExecuteOptions options, + Task task, OperationResult result) throws SchemaException, CommunicationException, ObjectAlreadyExistsException, + ExpressionEvaluationException, PolicyViolationException, SecurityViolationException, ConfigurationException, ObjectNotFoundException { + List assignments = ((AssignmentHolderType) assignee.getObjectable()).getAssignment().stream() + .filter(a -> a.getTargetRef() != null && assigned.oid.equals(a.getTargetRef().getOid()) + && QNameUtil.match(a.getTargetRef().getRelation(), relation)) + .collect(Collectors.toList()); + assertThat(assignments).size().as("# of assignments of " + assigned).isEqualTo(1); + AssignmentType assignment = MiscUtil.extractSingleton(assignments); + ObjectDelta delta = deltaFor(assignee.getObjectClass()) + .item(UserType.F_ASSIGNMENT) + .delete(assignment.clone()) + .asObjectDelta(assignee.oid); + executeChanges(delta, options, task, result); + } + protected void assign(Class type, String focusOid, AssignmentType assignmentType, Task task, OperationResult result) throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException, diff --git a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/MidPointQueryExecutor.java b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/MidPointQueryExecutor.java index 4f5dfc0bf53..7dd8ee72332 100644 --- a/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/MidPointQueryExecutor.java +++ b/model/report-impl/src/main/java/com/evolveum/midpoint/report/impl/MidPointQueryExecutor.java @@ -1,203 +1,203 @@ -/* - * Copyright (c) 2010-2019 Evolveum and contributors - * - * This work is dual-licensed under the Apache License 2.0 - * and European Union Public License. See LICENSE file for details. - */ -package com.evolveum.midpoint.report.impl; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -import com.evolveum.midpoint.prism.*; -import net.sf.jasperreports.engine.JRDataSource; -import net.sf.jasperreports.engine.JRDataset; -import net.sf.jasperreports.engine.JRException; -import net.sf.jasperreports.engine.JRParameter; -import net.sf.jasperreports.engine.JRValueParameter; -import net.sf.jasperreports.engine.JasperReportsContext; -import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; -import net.sf.jasperreports.engine.query.JRAbstractQueryExecuter; - -import org.apache.commons.lang.StringUtils; - -import com.evolveum.midpoint.audit.api.AuditEventRecord; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.schema.expression.TypedValue; -import com.evolveum.midpoint.schema.expression.VariablesMap; -import com.evolveum.midpoint.util.exception.CommunicationException; -import com.evolveum.midpoint.util.exception.ConfigurationException; -import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; -import com.evolveum.midpoint.util.exception.ObjectNotFoundException; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.exception.SecurityViolationException; -import com.evolveum.midpoint.util.exception.SystemException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; - -public abstract class MidPointQueryExecutor extends JRAbstractQueryExecuter { - - private static final Trace LOGGER = TraceManager.getTrace(MidPointLocalQueryExecutor.class); - - private Object query; - private String script; - private Class type; - - public String getScript() { - return script; - } - public Object getQuery() { - return query; - } - public Class getType() { - return type; - } - - protected abstract TypedValue createTypedPropertyValue(T realValue, Class valueClass); - - protected VariablesMap getParameters(){ - JRParameter[] params = dataset.getParameters(); - VariablesMap expressionParameters = new VariablesMap(); - for (JRParameter param : params){ - if (param.isSystemDefined()){ - continue; - } - //LOGGER.trace(((JRBaseParameter)param).getName()); - Object v = getParameterValue(param.getName()); - try{ - expressionParameters.put(param.getName(), createTypedPropertyValue(v, (Class)param.getValueClass())); - } catch (Exception e){ - //just skip properties that are not important for midpoint - } - - LOGGER.trace("p.val: {}", v); - } - return expressionParameters; - } - - protected VariablesMap getPromptingParameters() { - JRParameter[] params = dataset.getParameters(); - VariablesMap expressionParameters = new VariablesMap(); - for (JRParameter param : params) { - if (param.isSystemDefined()) { - continue; - } - if (!param.isForPrompting()) { - continue; - } - //LOGGER.trace(((JRBaseParameter)param).getName()); - Object v = getParameterValue(param.getName()); - try{ - expressionParameters.put(param.getName(), createTypedPropertyValue(v, (Class)param.getValueClass())); - } catch (Exception e){ - //just skip properties that are not important for midpoint - } - - LOGGER.trace("p.val: {}", v); - } - return expressionParameters; - } - - protected abstract Object getParsedQuery(String query, VariablesMap expressionParameters) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException; - - protected String getParsedScript(String script){ - String normalized = script.replace("", ""); - return normalized.replace("", ""); - } - - protected MidPointQueryExecutor(JasperReportsContext jasperReportsContext, JRDataset dataset, - Map parametersMap) { - super(jasperReportsContext, dataset, parametersMap); - } - - protected abstract Collection> searchObjects(Object query, Collection> options) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException; - - protected abstract Collection> evaluateScript(String script, VariablesMap parameters) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException; - - protected abstract Collection searchAuditRecords(String script, VariablesMap parameters) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException; - - protected abstract JRDataSource createDataSourceFromObjects(Collection> results); - - protected abstract JRDataSource createDataSourceFromContainerValues(Collection> results); - - @Override - protected void parseQuery() { - try { - - String s = dataset.getQuery().getText(); - LOGGER.trace("query: " + s); - if (StringUtils.isEmpty(s)) { - query = null; - } else { - if (s.startsWith("> results; - results = (Collection) searchObjects(query, SelectorOptions.createCollection(GetOperationOptions.createRaw())); - return createDataSourceFromObjects(results); - } else { - if (isAuditReport()) { - Collection audtiEventRecords = searchAuditRecords(script, getPromptingParameters()); - Collection auditEventRecordsType = new ArrayList<>(); - for (AuditEventRecord aer : audtiEventRecords){ - AuditEventRecordType aerType = aer.createAuditEventRecordType(true); - auditEventRecordsType.add(aerType); - } - return new JRBeanCollectionDataSource(auditEventRecordsType); - } else { - Collection> results; - results = evaluateScript(script, getParameters()); - return createDataSourceFromContainerValues(results); - } - } - } catch (SchemaException | ObjectNotFoundException | SecurityViolationException - | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { - // TODO Auto-generated catch block - throw new JRException(e); - } - } - - protected abstract boolean isAuditReport(); - - @Override - public void close() { -// throw new UnsupportedOperationException("QueryExecutor.close() not supported"); - //nothing to DO - } - - @Override - public boolean cancelQuery() throws JRException { - throw new UnsupportedOperationException("QueryExecutor.cancelQuery() not supported"); - } - - @Override - protected String getParameterReplacement(String parameterName) { - throw new UnsupportedOperationException("QueryExecutor.getParameterReplacement() not supported"); - } - - - -} +/* + * Copyright (c) 2010-2019 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ +package com.evolveum.midpoint.report.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import com.evolveum.midpoint.prism.*; +import net.sf.jasperreports.engine.JRDataSource; +import net.sf.jasperreports.engine.JRDataset; +import net.sf.jasperreports.engine.JRException; +import net.sf.jasperreports.engine.JRParameter; +import net.sf.jasperreports.engine.JRValueParameter; +import net.sf.jasperreports.engine.JasperReportsContext; +import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource; +import net.sf.jasperreports.engine.query.JRAbstractQueryExecuter; + +import org.apache.commons.lang.StringUtils; + +import com.evolveum.midpoint.audit.api.AuditEventRecord; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.SelectorOptions; +import com.evolveum.midpoint.schema.expression.TypedValue; +import com.evolveum.midpoint.schema.expression.VariablesMap; +import com.evolveum.midpoint.util.exception.CommunicationException; +import com.evolveum.midpoint.util.exception.ConfigurationException; +import com.evolveum.midpoint.util.exception.ExpressionEvaluationException; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.SystemException; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; + +public abstract class MidPointQueryExecutor extends JRAbstractQueryExecuter { + + private static final Trace LOGGER = TraceManager.getTrace(MidPointLocalQueryExecutor.class); + + private Object query; + private String script; + private Class type; + + public String getScript() { + return script; + } + public Object getQuery() { + return query; + } + public Class getType() { + return type; + } + + protected abstract TypedValue createTypedPropertyValue(T realValue, Class valueClass); + + protected VariablesMap getParameters(){ + JRParameter[] params = dataset.getParameters(); + VariablesMap expressionParameters = new VariablesMap(); + for (JRParameter param : params){ + if (param.isSystemDefined()){ + continue; + } + //LOGGER.trace(((JRBaseParameter)param).getName()); + Object v = getParameterValue(param.getName()); + try{ + expressionParameters.put(param.getName(), createTypedPropertyValue(v, (Class)param.getValueClass())); + } catch (Exception e){ + //just skip properties that are not important for midpoint + } + + LOGGER.trace("p.val: {}", v); + } + return expressionParameters; + } + + protected VariablesMap getPromptingParameters() { + JRParameter[] params = dataset.getParameters(); + VariablesMap expressionParameters = new VariablesMap(); + for (JRParameter param : params) { + if (param.isSystemDefined()) { + continue; + } + if (!param.isForPrompting()) { + continue; + } + //LOGGER.trace(((JRBaseParameter)param).getName()); + Object v = getParameterValue(param.getName()); + try{ + expressionParameters.put(param.getName(), createTypedPropertyValue(v, (Class)param.getValueClass())); + } catch (Exception e){ + //just skip properties that are not important for midpoint + } + + LOGGER.trace("p.val: {}", v); + } + return expressionParameters; + } + + protected abstract Object getParsedQuery(String query, VariablesMap expressionParameters) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException; + + protected String getParsedScript(String script){ + String normalized = script.replace("", ""); + return normalized.replace("", ""); + } + + protected MidPointQueryExecutor(JasperReportsContext jasperReportsContext, JRDataset dataset, + Map parametersMap) { + super(jasperReportsContext, dataset, parametersMap); + } + + protected abstract Collection> searchObjects(Object query, Collection> options) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException; + + protected abstract Collection> evaluateScript(String script, VariablesMap parameters) throws SchemaException, ObjectNotFoundException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException; + + protected abstract Collection searchAuditRecords(String script, VariablesMap parameters) throws SchemaException, ExpressionEvaluationException, ObjectNotFoundException, CommunicationException, ConfigurationException, SecurityViolationException; + + protected abstract JRDataSource createDataSourceFromObjects(Collection> results); + + protected abstract JRDataSource createDataSourceFromContainerValues(Collection> results); + + @Override + protected void parseQuery() { + try { + + String s = dataset.getQuery().getText(); + LOGGER.trace("query: " + s); + if (StringUtils.isEmpty(s)) { + query = null; + } else { + if (s.startsWith("> results; + results = (Collection) searchObjects(query, SelectorOptions.createCollection(GetOperationOptions.createRaw())); + return createDataSourceFromObjects(results); + } else { + if (isAuditReport()) { + Collection auditEventRecords = searchAuditRecords(script, getPromptingParameters()); + Collection auditEventRecordsType = new ArrayList<>(); + for (AuditEventRecord aer : auditEventRecords) { + AuditEventRecordType aerType = aer.createAuditEventRecordType(true); + auditEventRecordsType.add(aerType); + } + return new JRBeanCollectionDataSource(auditEventRecordsType); + } else { + Collection> results; + results = evaluateScript(script, getParameters()); + return createDataSourceFromContainerValues(results); + } + } + } catch (SchemaException | ObjectNotFoundException | SecurityViolationException + | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { + // TODO Auto-generated catch block + throw new JRException(e); + } + } + + protected abstract boolean isAuditReport(); + + @Override + public void close() { +// throw new UnsupportedOperationException("QueryExecutor.close() not supported"); + //nothing to DO + } + + @Override + public boolean cancelQuery() throws JRException { + throw new UnsupportedOperationException("QueryExecutor.cancelQuery() not supported"); + } + + @Override + protected String getParameterReplacement(String parameterName) { + throw new UnsupportedOperationException("QueryExecutor.getParameterReplacement() not supported"); + } + + + +} diff --git a/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java b/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java index 414b6f3d3b2..9a6e20c5e17 100644 --- a/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java +++ b/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditEventRecord.java @@ -492,7 +492,7 @@ public void checkConsistence() { // } } - public AuditEventRecordType createAuditEventRecordType(){ + public AuditEventRecordType createAuditEventRecordType() { return createAuditEventRecordType(false); } @@ -522,7 +522,12 @@ public AuditEventRecordType createAuditEventRecordType(boolean tolerateInconsist for (ObjectDeltaOperation delta : deltas) { ObjectDeltaOperationType odo = new ObjectDeltaOperationType(); try { - DeltaConvertor.toObjectDeltaOperationType(delta, odo, DeltaConversionOptions.createSerializeReferenceNames()); + DeltaConversionOptions options = DeltaConversionOptions.createSerializeReferenceNames(); + // This can be tricky because it can create human-readable but machine-unprocessable + // data, see MID-6262. But in current context the results of this method are to be + // used only in GUI and reports, so we are safe here. + options.setEscapeInvalidCharacters(true); + DeltaConvertor.toObjectDeltaOperationType(delta, odo, options); auditRecordType.getDelta().add(odo); } catch (Exception e) { if (tolerateInconsistencies){ diff --git a/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditService.java b/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditService.java index f61854cdc3d..c4d4e12ab66 100644 --- a/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditService.java +++ b/repo/audit-api/src/main/java/com/evolveum/midpoint/audit/api/AuditService.java @@ -12,6 +12,7 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.xml.ns._public.common.common_3.CleanupPolicyType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationAuditType; /** * @author semancik @@ -54,4 +55,9 @@ public interface AuditService { */ boolean supportsRetrieval(); + /** + * Called when audit configuration is established or changed. + */ + default void applyAuditConfiguration(SystemConfigurationAuditType configuration) { + } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java index ce8736a8a57..d72a2bcff59 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SqlAuditServiceImpl.java @@ -17,6 +17,10 @@ import javax.persistence.criteria.CriteriaQuery; import javax.xml.datatype.Duration; +import com.evolveum.midpoint.prism.util.CloneUtil; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationAuditType; + import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; import org.hibernate.FlushMode; @@ -67,11 +71,11 @@ */ public class SqlAuditServiceImpl extends SqlBaseService implements AuditService { - public static final String OP_CLEANUP_AUDIT_MAX_AGE = "cleanupAuditMaxAge"; - public static final String OP_CLEANUP_AUDIT_MAX_RECORDS = "cleanupAuditMaxRecords"; - public static final String OP_LIST_RECORDS = "listRecords"; - public static final String OP_LIST_RECORDS_ATTEMPT = "listRecordsAttempt"; - public static final String OP_LOAD_AUDIT_DELTA = "loadAuditDelta"; + private static final String OP_CLEANUP_AUDIT_MAX_AGE = "cleanupAuditMaxAge"; + private static final String OP_CLEANUP_AUDIT_MAX_RECORDS = "cleanupAuditMaxRecords"; + private static final String OP_LIST_RECORDS = "listRecords"; + private static final String OP_LIST_RECORDS_ATTEMPT = "listRecordsAttempt"; + private static final String OP_LOAD_AUDIT_DELTA = "loadAuditDelta"; @Autowired private BaseHelper baseHelper; @@ -82,7 +86,9 @@ public class SqlAuditServiceImpl extends SqlBaseService implements AuditService private static final String QUERY_MAX_RESULT = "setMaxResults"; private static final String QUERY_FIRST_RESULT = "setFirstResult"; - private Map customColumn = new HashMap<>(); + private final Map customColumn = new HashMap<>(); + + private volatile SystemConfigurationAuditType auditConfiguration; public SqlAuditServiceImpl(SqlRepositoryFactory repositoryFactory) { super(repositoryFactory); @@ -201,10 +207,10 @@ private void reindexEntryAttempt(AuditEventRecord record) { Session session = baseHelper.beginTransaction(); try { - RAuditEventRecord reindexed = RAuditEventRecord.toRepo(record, getPrismContext(), null); - //TODO FIXME temporary hack, merge will eventyually load the object to the session if there isn't one, + RAuditEventRecord reindexed = RAuditEventRecord.toRepo(record, getPrismContext(), null, auditConfiguration); + //TODO FIXME temporary hack, merge will eventually load the object to the session if there isn't one, // but in this case we force loading object because of "objectDeltaOperation". There is some problem probably - // during serializing/deserializing which causes constraint violation on priamry key.. + // during serializing/deserializing which causes constraint violation on primary key.. RAuditEventRecord rRecord = session.load(RAuditEventRecord.class, record.getRepoId()); rRecord.getChangedItems().clear(); rRecord.getChangedItems().addAll(reindexed.getChangedItems()); @@ -464,7 +470,7 @@ private void auditAttempt(AuditEventRecord record) { SingleSqlQuery deltaQuery; try { - deltaQuery = RObjectDeltaOperation.toRepo(id, delta, getPrismContext()); + deltaQuery = RObjectDeltaOperation.toRepo(id, delta, getPrismContext(), auditConfiguration); deltaBatchQuery.addQueryForBatch(deltaQuery); } catch (DtoTranslationException e) { baseHelper.handleGeneralCheckedException(e, session, null); @@ -844,9 +850,7 @@ public long countObjects(String query, Map params) { SelectQueryBuilder queryBuilder = new SelectQueryBuilder(database, basicQuery); setParametersToQuery(queryBuilder, params); - if (LOGGER.isTraceEnabled()) { - LOGGER.trace("List records attempt\n processed query: {}", queryBuilder); - } + LOGGER.trace("List records attempt\n processed query: {}", queryBuilder); try (PreparedStatement stmt = queryBuilder.build().createPreparedStatement(connection)) { ResultSet resultList = stmt.executeQuery(); @@ -876,4 +880,8 @@ public Map getCustomColumn() { return customColumn; } + @Override + public void applyAuditConfiguration(SystemConfigurationAuditType configuration) { + this.auditConfiguration = CloneUtil.clone(configuration); + } } diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeDispatcherImpl.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeDispatcherImpl.java index e217a225d57..e42785c6aae 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeDispatcherImpl.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/SystemConfigurationChangeDispatcherImpl.java @@ -7,6 +7,7 @@ package com.evolveum.midpoint.repo.sql; +import com.evolveum.midpoint.audit.api.AuditService; import com.evolveum.midpoint.common.LoggingConfigurationManager; import com.evolveum.midpoint.common.ProfilingConfigurationManager; import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration; @@ -37,7 +38,7 @@ import java.util.HashSet; /** - * @author mederly + * Dispatches "system configuration changed" events to relevant objects. */ @Component public class SystemConfigurationChangeDispatcherImpl implements SystemConfigurationChangeDispatcher { @@ -45,6 +46,7 @@ public class SystemConfigurationChangeDispatcherImpl implements SystemConfigurat private static final Trace LOGGER = TraceManager.getTrace(SystemConfigurationChangeDispatcherImpl.class); @Autowired private RepositoryService repositoryService; + @Autowired(required = false) private AuditService auditService; @Autowired private PrismContext prismContext; @Autowired private RelationRegistry relationRegistry; @Autowired private MidpointConfiguration midpointConfiguration; @@ -95,6 +97,7 @@ public synchronized void dispatch(boolean ignoreVersion, boolean allowNotFound, applyRemoteHostAddressHeadersConfiguration(configuration); applyPolyStringNormalizerConfiguration(configuration); applyFullTextSearchConfiguration(configuration); + applyAuditConfiguration(configuration); applyRelationsConfiguration(configuration); applyOperationResultHandlingConfiguration(configuration); applyCachingConfiguration(configuration); @@ -164,6 +167,17 @@ private void applyFullTextSearchConfiguration(SystemConfigurationType configurat } } + private void applyAuditConfiguration(SystemConfigurationType configuration) { + try { + if (auditService != null) { + auditService.applyAuditConfiguration(configuration.getAudit()); + } + } catch (Throwable t) { + LoggingUtils.logUnexpectedException(LOGGER, "Couldn't apply audit configuration", t); + lastVersionApplied = null; + } + } + private void applyRelationsConfiguration(SystemConfigurationType configuration) { try { relationRegistry.applyRelationsConfiguration(configuration); diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RAuditEventRecord.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RAuditEventRecord.java index 850c71d3960..69d8d4b10b1 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RAuditEventRecord.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RAuditEventRecord.java @@ -30,6 +30,8 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationAuditType; + import org.apache.commons.lang.Validate; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.ForeignKey; @@ -504,7 +506,8 @@ public int hashCode() { changedItems, resourceOids, propertyValues, referenceValues, result); } - public static RAuditEventRecord toRepo(AuditEventRecord record, PrismContext prismContext, Boolean isTransient) + public static RAuditEventRecord toRepo(AuditEventRecord record, PrismContext prismContext, Boolean isTransient, + SystemConfigurationAuditType auditConfiguration) throws DtoTranslationException { Validate.notNull(record, "Audit event record must not be null."); @@ -588,7 +591,7 @@ public static RAuditEventRecord toRepo(AuditEventRecord record, PrismContext pri } } - RObjectDeltaOperation rDelta = RObjectDeltaOperation.toRepo(repo, delta, prismContext); + RObjectDeltaOperation rDelta = RObjectDeltaOperation.toRepo(repo, delta, prismContext, auditConfiguration); rDelta.setTransient(true); rDelta.setRecord(repo); repo.getDeltas().add(rDelta); diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RObjectDeltaOperation.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RObjectDeltaOperation.java index 17192aa3255..7f338d3dbb4 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RObjectDeltaOperation.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/data/audit/RObjectDeltaOperation.java @@ -8,10 +8,15 @@ package com.evolveum.midpoint.repo.sql.data.audit; import static com.evolveum.midpoint.repo.sql.data.audit.RObjectDeltaOperation.COLUMN_RECORD_ID; +import static com.evolveum.midpoint.schema.util.SystemConfigurationAuditUtil.isEscapingInvalidCharacters; import java.sql.ResultSet; import javax.persistence.*; +import com.evolveum.midpoint.prism.SerializationOptions; + +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationAuditType; + import org.hibernate.annotations.ForeignKey; import org.jetbrains.annotations.NotNull; @@ -261,7 +266,7 @@ public int hashCode() { } public static RObjectDeltaOperation toRepo(RAuditEventRecord record, ObjectDeltaOperation operation, - PrismContext prismContext) throws DtoTranslationException { + PrismContext prismContext, SystemConfigurationAuditType auditConfiguration) throws DtoTranslationException { RObjectDeltaOperation auditDelta = new RObjectDeltaOperation(); auditDelta.setRecord(record); @@ -269,7 +274,9 @@ public static RObjectDeltaOperation toRepo(RAuditEventRecord record, ObjectDelta if (operation.getObjectDelta() != null) { ObjectDelta delta = operation.getObjectDelta(); - String xmlDelta = DeltaConvertor.toObjectDeltaTypeXml(delta, DeltaConversionOptions.createSerializeReferenceNames()); + DeltaConversionOptions options = DeltaConversionOptions.createSerializeReferenceNames(); + options.setEscapeInvalidCharacters(isEscapingInvalidCharacters(auditConfiguration)); + String xmlDelta = DeltaConvertor.toObjectDeltaTypeXml(delta, options); byte[] data = RUtil.getByteArrayFromXml(xmlDelta, true); auditDelta.setDelta(data); @@ -294,7 +301,7 @@ public static RObjectDeltaOperation toRepo(RAuditEventRecord record, ObjectDelta } public static SingleSqlQuery toRepo(Long recordId, ObjectDeltaOperation operation, - PrismContext prismContext) throws DtoTranslationException { + PrismContext prismContext, SystemConfigurationAuditType auditConfiguration) throws DtoTranslationException { InsertQueryBuilder queryBuilder = new InsertQueryBuilder(TABLE_NAME); queryBuilder.addParameter(COLUMN_RECORD_ID, recordId, true); @@ -304,7 +311,9 @@ public static SingleSqlQuery toRepo(Long recordId, ObjectDeltaOperation operatio if (operation.getObjectDelta() != null) { ObjectDelta delta = operation.getObjectDelta(); - String xmlDelta = DeltaConvertor.toObjectDeltaTypeXml(delta, DeltaConversionOptions.createSerializeReferenceNames()); + DeltaConversionOptions options = DeltaConversionOptions.createSerializeReferenceNames(); + options.setEscapeInvalidCharacters(isEscapingInvalidCharacters(auditConfiguration)); + String xmlDelta = DeltaConvertor.toObjectDeltaTypeXml(delta, options); deltaData = RUtil.getByteArrayFromXml(xmlDelta, true); queryBuilder.addParameter(DELTA_COLUMN_NAME, deltaData); queryBuilder.addParameter(DELTA_OID_COLUMN_NAME, delta.getOid()); @@ -323,7 +332,9 @@ public static SingleSqlQuery toRepo(Long recordId, ObjectDeltaOperation operatio } else { queryBuilder.addParameter(STATUS_COLUMN_NAME, RUtil.getRepoEnumValue(jaxb.getStatus(), ROperationResultStatus.class)); try { - String full = prismContext.xmlSerializer().serializeRealValue(jaxb, SchemaConstantsGenerated.C_OPERATION_RESULT); + String full = prismContext.xmlSerializer() + .options(SerializationOptions.createEscapeInvalidCharacters()) + .serializeRealValue(jaxb, SchemaConstantsGenerated.C_OPERATION_RESULT); fullResultData = RUtil.getByteArrayFromXml(full, true); queryBuilder.addParameter(FULL_RESULT_COLUMN_NAME, fullResultData); } catch (Exception ex) { diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java index cc5d9e19bb3..3ffc1c64b52 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/TestResource.java @@ -35,4 +35,8 @@ public String getNameOrig() { public T getObjectable() { return object.asObjectable(); } + + public Class getObjectClass() { + return object.getCompileTimeClass(); + } } diff --git a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java index 2d85303ca2b..6bd9f3e6df8 100644 --- a/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java +++ b/repo/repo-test-util/src/main/java/com/evolveum/midpoint/test/asserter/DummyObjectAsserter.java @@ -106,7 +106,7 @@ protected String desc() { if (dummyResourceName == null) { return descWithDetails(dummyObject) + " on default dummy resource"; } else { - return descWithDetails(dummyObject.toString()) + " on dummy resource '"+dummyResourceName+"'"; + return descWithDetails(dummyObject) + " on dummy resource '"+dummyResourceName+"'"; } } diff --git a/repo/system-init/src/main/java/com/evolveum/midpoint/init/AuditServiceProxy.java b/repo/system-init/src/main/java/com/evolveum/midpoint/init/AuditServiceProxy.java index 24f9eca7026..060c8c9f83f 100644 --- a/repo/system-init/src/main/java/com/evolveum/midpoint/init/AuditServiceProxy.java +++ b/repo/system-init/src/main/java/com/evolveum/midpoint/init/AuditServiceProxy.java @@ -32,6 +32,8 @@ import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.CleanupPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationAuditType; + import org.apache.commons.lang.Validate; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; @@ -67,7 +69,7 @@ public class AuditServiceProxy implements AuditService, AuditServiceRegistry { @Autowired private PrismContext prismContext; @Autowired private SchemaHelper schemaHelper; - private List services = new Vector<>(); + private final List services = new Vector<>(); @Override public void audit(AuditEventRecord record, Task task) { @@ -253,4 +255,9 @@ public boolean supportsRetrieval() { } return false; } + + @Override + public void applyAuditConfiguration(SystemConfigurationAuditType configuration) { + services.forEach(service -> service.applyAuditConfiguration(configuration)); + } } diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java index bd9a08378e6..70235b3480c 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/tracing/TracerImpl.java @@ -124,7 +124,10 @@ public void storeTrace(Task task, OperationResult result, @Nullable OperationRes long start = System.currentTimeMillis(); TracingOutputType tracingOutput = createTracingOutput(task, result, tracingProfile); String xml = prismContext.xmlSerializer() - .options(SerializationOptions.createSerializeReferenceNames()) + .options( + SerializationOptions + .createSerializeReferenceNames() + .escapeInvalidCharacters(true)) .serializeRealValue(tracingOutput); if (zip) { MiscUtil.writeZipFile(file, ZIP_ENTRY_NAME, xml, StandardCharsets.UTF_8); From 4ec6017e31d5bb4b2661137f1ccbb9035cf8de2a Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Tue, 12 May 2020 10:52:40 +0200 Subject: [PATCH 32/41] wrapper factories cleanup --- .../factory/wrapper/ItemWrapperFactory.java | 2 +- .../wrapper/PrismContainerWrapperFactory.java | 7 + .../api/factory/wrapper/WrapperContext.java | 3 + .../midpoint/gui/api/page/PageBase.java | 11 +- .../wrapper/PrismContainerValueWrapper.java | 2 + .../api/registry/GuiComponentRegistry.java | 7 +- .../wrapper/AppendersWrapperFactoryImpl.java | 22 ++- .../AssignmentDetailsWrapperFactoryImpl.java | 5 +- .../CaseWorkItemWrapperFactoryImpl.java | 13 +- .../ClassLoggerWrapperFactoryImpl.java | 2 + ...nectorConfigurationWrapperFactoryImpl.java | 12 +- .../wrapper/ConstructionWrapperFactory.java | 19 ++- .../wrapper/ExpressionWrapperFactory.java | 4 +- .../HeterogenousContainerWrapperFactory.java | 150 +++--------------- .../wrapper/ItemWrapperFactoryImpl.java | 114 ++++++------- .../LoggingAppenderWrapperFactoryImpl.java | 22 +-- ...oggingConfigurationWrapperFactoryImpl.java | 27 +--- .../LoggingPackageWrapperFactoryImpl.java | 8 +- ...EmptyValueContainerWrapperFactoryImpl.java | 12 +- ...> OperationalContainerWrapperFactory.java} | 29 +--- .../PrismContainerWrapperFactoryImpl.java | 28 ++-- .../PrismObjectWrapperFactoryImpl.java | 43 +++-- .../PrismPropertyWrapperFactoryImpl.java | 3 +- .../wrapper/PrismReferenceWrapperFactory.java | 12 +- ...rofilingClassLoggerWrapperFactoryImpl.java | 17 +- .../ProtectedStringWrapperFactory.java | 5 +- .../ResourceAttributeWrapperFactoryImpl.java | 18 +-- .../ShadowAssociationWrapperFactoryImpl.java | 29 ++-- .../ShadowAttributesWrapperFactoryImpl.java | 8 +- .../wrapper/ShadowWrapperFactoryImpl.java | 3 - .../wrapper/TaskHandlerWrapperFactory.java | 42 ++--- .../impl/prism/wrapper/ExpressionWrapper.java | 39 +++-- .../PrismContainerValueWrapperImpl.java | 4 + .../registry/GuiComponentRegistryImpl.java | 23 ++- .../component/assignment/AssignmentPanel.java | 3 + .../FocusProjectionsTabPanel.java | 4 + .../web/AbstractGuiIntegrationTest.java | 6 +- 37 files changed, 320 insertions(+), 438 deletions(-) rename gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/{MetadataWrapperFactory.java => OperationalContainerWrapperFactory.java} (57%) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/ItemWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/ItemWrapperFactory.java index 706c79e4204..ff639586b3e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/ItemWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/ItemWrapperFactory.java @@ -24,7 +24,7 @@ public interface ItemWrapperFactory parent, ItemDefinition def, WrapperContext context) throws SchemaException; - IW createWrapper(Item childContainer, ItemStatus status, WrapperContext context) throws SchemaException; + IW createWrapper(PrismContainerValueWrapper parent, Item childContainer, ItemStatus status, WrapperContext context) throws SchemaException; VW createValueWrapper(IW parent, PV value, ValueStatus status, WrapperContext context) throws SchemaException; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/PrismContainerWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/PrismContainerWrapperFactory.java index 006b2c3a523..18ea16ba52d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/PrismContainerWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/PrismContainerWrapperFactory.java @@ -6,10 +6,15 @@ */ package com.evolveum.midpoint.gui.api.factory.wrapper; +import com.evolveum.midpoint.gui.api.prism.ItemStatus; +import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.prism.Containerable; +import com.evolveum.midpoint.prism.Item; +import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; +import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.web.component.prism.ValueStatus; /** @@ -21,5 +26,7 @@ public interface PrismContainerWrapperFactory extends I PrismContainerValueWrapper createContainerValueWrapper(PrismContainerWrapper objectWrapper, PrismContainerValue objectValue, ValueStatus status, WrapperContext context); +// ItemWrapper createChildWrapper(ItemDefinition def, PrismContainerValueWrapper containerValueWrapper, WrapperContext context) throws SchemaException; +// ItemWrapper createChildWrapper(PrismContainerValueWrapper parent, I childItem, ItemStatus status, WrapperContext context) throws SchemaException; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/WrapperContext.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/WrapperContext.java index f9265a6838a..5742a98f69e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/WrapperContext.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/factory/wrapper/WrapperContext.java @@ -16,6 +16,8 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.VirtualContainerItemSpecificationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.VirtualContainersSpecificationType; +import org.jetbrains.annotations.NotNull; + import java.util.Collection; import java.util.List; @@ -144,6 +146,7 @@ public List getVirtualItemSpecification() return virtualItemSpecification; } + @NotNull public PrismObject getObject() { return object; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java index e582cfe39dc..378781c8ab0 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/page/PageBase.java @@ -15,6 +15,7 @@ import javax.management.ObjectName; import javax.xml.namespace.QName; +import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory; import com.evolveum.midpoint.gui.impl.prism.panel.ItemPanelSettings; import com.evolveum.midpoint.web.page.admin.PageAdminObjectDetails; @@ -2696,7 +2697,7 @@ public PrismObjectWrapperFactory findObjectWrapperFact public VW createValueWrapper(IW parentWrapper, PV newValue, ValueStatus status, WrapperContext context) throws SchemaException { - ItemWrapperFactory factory = (ItemWrapperFactory) registry.findWrapperFactory(parentWrapper); + ItemWrapperFactory factory = registry.findWrapperFactory(parentWrapper); return factory.createValueWrapper(parentWrapper, newValue, status, context); @@ -2704,19 +2705,19 @@ public IW createItemWrapper(ID def, PrismContainerValueWrapper parent, WrapperContext ctx) throws SchemaException { - ItemWrapperFactory factory = (ItemWrapperFactory) registry.findWrapperFactory(def); + PrismContainerWrapperFactory factory = registry.findContainerWrapperFactory(parent.getDefinition()); ctx.setShowEmpty(true); ctx.setCreateIfEmpty(true); - return factory.createWrapper(parent, def, ctx); + return (IW) factory.createWrapper(parent, def, ctx); } public IW createItemWrapper(I item, ItemStatus status, WrapperContext ctx) throws SchemaException { - ItemWrapperFactory factory = (ItemWrapperFactory) registry.findWrapperFactory(item.getDefinition()); + ItemWrapperFactory factory = registry.findWrapperFactory(item.getDefinition()); ctx.setCreateIfEmpty(true); - return factory.createWrapper(item, status, ctx); + return factory.createWrapper(null, item, status, ctx); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java index 7a804f84b47..715a79e2bc5 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/prism/wrapper/PrismContainerValueWrapper.java @@ -81,4 +81,6 @@ public interface PrismContainerValueWrapper extends Pri void setVirtualContainerItems(List virtualItems); boolean isVirtual(); + PrismContainerDefinition getDefinition(); + } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/registry/GuiComponentRegistry.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/registry/GuiComponentRegistry.java index b4628eac3f4..36f103d1666 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/registry/GuiComponentRegistry.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/api/registry/GuiComponentRegistry.java @@ -12,13 +12,12 @@ import javax.xml.namespace.QName; import com.evolveum.midpoint.gui.api.factory.GuiComponentFactory; +import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory; import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; import com.evolveum.midpoint.gui.api.factory.wrapper.PrismObjectWrapperFactory; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismValueWrapper; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismObjectDefinition; -import com.evolveum.midpoint.prism.PrismValue; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; public interface GuiComponentRegistry extends Serializable { @@ -33,6 +32,8 @@ public interface GuiComponentRegistry extends Serializable { ItemWrapperFactory findWrapperFactory(ItemDefinition def); + PrismContainerWrapperFactory findContainerWrapperFactory(PrismContainerDefinition def); + PrismObjectWrapperFactory getObjectWrapperFactory(PrismObjectDefinition objectDef); void addToRegistry(ItemWrapperFactory factory); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AppendersWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AppendersWrapperFactoryImpl.java index 98c3f32f245..ddcabcbc864 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AppendersWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AppendersWrapperFactoryImpl.java @@ -6,12 +6,12 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; - import java.util.List; import org.springframework.stereotype.Component; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; +import com.evolveum.midpoint.prism.ComplexTypeDefinition; import com.evolveum.midpoint.prism.Containerable; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; @@ -38,10 +38,24 @@ public int getOrder() { @Override protected List getItemDefinitions(PrismContainerWrapper parent, PrismContainerValue value) { - if(value != null && value.getComplexTypeDefinition() != null - && value.getComplexTypeDefinition().getDefinitions() != null) { - return value.getComplexTypeDefinition().getDefinitions(); + List defs = getComplexTypeDefinitions(value); + if (defs != null) { + return defs; } + return parent.getDefinitions(); } + + private List getComplexTypeDefinitions(PrismContainerValue value) { + if (value == null) { + return null; + } + + ComplexTypeDefinition ctd = value.getComplexTypeDefinition(); + if (ctd == null) { + return null; + } + + return ctd.getDefinitions(); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentDetailsWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentDetailsWrapperFactoryImpl.java index e623476e019..247c6a88cda 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentDetailsWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/AssignmentDetailsWrapperFactoryImpl.java @@ -33,10 +33,7 @@ public boolean match(ItemDefinition def) { @Override protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { - if (!isEmptyValue){ - return true; - } - return false; + return !isEmptyValue; } @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java index 5e031f97bd5..fa9ae2f1d4c 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/CaseWorkItemWrapperFactoryImpl.java @@ -6,21 +6,12 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import com.evolveum.midpoint.gui.api.prism.ItemStatus; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.gui.impl.prism.wrapper.CaseWorkItemTypeWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.web.component.prism.ValueStatus; import com.evolveum.midpoint.web.page.admin.workflow.WorkItemDetailsPanel; import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; /** * Created by honchar diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ClassLoggerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ClassLoggerWrapperFactoryImpl.java index bcbc97fe9ab..7f6927599fd 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ClassLoggerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ClassLoggerWrapperFactoryImpl.java @@ -35,4 +35,6 @@ protected boolean canCreateValueWrapper(PrismContainerValue value) { } return !loggerPackage.equals(ProfilingClassLoggerWrapperFactoryImpl.LOGGER_PROFILING); } + + } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConnectorConfigurationWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConnectorConfigurationWrapperFactoryImpl.java index a50c5f301fb..c3b04009c7c 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConnectorConfigurationWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConnectorConfigurationWrapperFactoryImpl.java @@ -7,16 +7,16 @@ package com.evolveum.midpoint.gui.impl.factory.wrapper; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismContainerDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.xml.ns._public.common.common_3.ConnectorConfigurationType; -import org.springframework.stereotype.Component; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; @Component public class ConnectorConfigurationWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { @@ -43,7 +43,7 @@ protected List getItemDefinitions(PrismContainerWrappe relevantDefinitions.add((PrismContainerDefinition) def); } } - Collections.sort(relevantDefinitions, (o1, o2) -> { + relevantDefinitions.sort((o1, o2) -> { int ord1 = o1.getDisplayOrder() != null ? o1.getDisplayOrder() : Integer.MAX_VALUE; int ord2 = o2.getDisplayOrder() != null ? o2.getDisplayOrder() : Integer.MAX_VALUE; return Integer.compare(ord1, ord2); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConstructionWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConstructionWrapperFactory.java index b2537609bd5..d6a5d140890 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConstructionWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ConstructionWrapperFactory.java @@ -6,11 +6,12 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.gui.impl.prism.wrapper.ConstructionValueWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.model.api.ModelService; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; +import com.evolveum.midpoint.gui.impl.prism.wrapper.ConstructionValueWrapper; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismContainerValue; import com.evolveum.midpoint.prism.PrismObject; @@ -23,8 +24,6 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.ConstructionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; @Component public class ConstructionWrapperFactory extends AssignmentDetailsWrapperFactoryImpl { @@ -54,8 +53,15 @@ public PrismContainerValueWrapper createContainerValueWrapper( } } + setupResource(constructionValueWrapper, constructionType, context); + return constructionValueWrapper; + } + private void setupResource(ConstructionValueWrapper constructionValueWrapper, ConstructionType constructionType, WrapperContext context) { ObjectReferenceType resourceRef = constructionType.getResourceRef(); + if (resourceRef == null) { + return; + } PrismObject resource; try { @@ -63,10 +69,9 @@ public PrismContainerValueWrapper createContainerValueWrapper( } catch (ObjectNotFoundException | SchemaException | SecurityViolationException | CommunicationException | ConfigurationException | ExpressionEvaluationException e) { LOGGER.error("Problem occurred during resolving resource, reason: {}", e.getMessage(), e); context.getResult().recordFatalError("A problem occurred during resolving resource, reason: " + e.getMessage(), e); - return constructionValueWrapper; + return; } constructionValueWrapper.setResource(resource); - return constructionValueWrapper; } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java index 5feddd6d3b0..361f0d1096b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ExpressionWrapperFactory.java @@ -44,12 +44,12 @@ public int getOrder() { } @Override - protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, + protected PrismPropertyWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext ctx) { ExpressionWrapper expressionWrapper = new ExpressionWrapper(parent, item, status); if (!expressionWrapper.isConstructionExpression() && !expressionWrapper.isAttributeExpression() && !expressionWrapper.isAssociationExpression()) { - return super.createWrapper(parent, item, status, ctx); + return super.createWrapperInternal(parent, item, status, ctx); } return expressionWrapper; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/HeterogenousContainerWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/HeterogenousContainerWrapperFactory.java index c4875f73234..0cc9f965023 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/HeterogenousContainerWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/HeterogenousContainerWrapperFactory.java @@ -6,140 +6,45 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; - +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.xml.namespace.QName; -import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; -import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; - -import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskPartitionsDefinitionType; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.impl.prism.panel.PrismContainerPanel; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismContainerValueWrapperImpl; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismContainerWrapperImpl; -import com.evolveum.midpoint.prism.Containerable; -import com.evolveum.midpoint.prism.Item; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.prism.PrismContainerDefinition; -import com.evolveum.midpoint.prism.PrismContainerValue; -import com.evolveum.midpoint.prism.path.ItemName; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.util.exception.SchemaException; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.component.prism.ValueStatus; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractPolicyConstraintType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskPartitionsDefinitionType; /** * @author katka */ @Component -public class HeterogenousContainerWrapperFactory implements PrismContainerWrapperFactory { - - private static final Trace LOGGER = TraceManager.getTrace(HeterogenousContainerWrapperFactory.class); - - @Autowired private GuiComponentRegistry registry; - - @Override - public PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, - ItemDefinition def, WrapperContext context) throws SchemaException { - ItemName name = def.getItemName(); - - PrismContainer childItem = parent.getNewValue().findContainer(name); - ItemStatus status = ItemStatus.NOT_CHANGED; - if (childItem == null) { - childItem = parent.getNewValue().findOrCreateContainer(name); - status = ItemStatus.ADDED; - } - - PrismContainerWrapper itemWrapper = new PrismContainerWrapperImpl(parent, childItem, status); - registry.registerWrapperPanel(childItem.getDefinition().getTypeName(), PrismContainerPanel.class); - - List> valueWrappers = createValuesWrapper(itemWrapper, childItem, context); - LOGGER.trace("valueWrappers {}", itemWrapper.getValues()); - itemWrapper.getValues().addAll(valueWrappers); - - return itemWrapper; - } +public class HeterogenousContainerWrapperFactory extends PrismContainerWrapperFactoryImpl { @Override public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper parent, PrismContainerValue value, ValueStatus status, WrapperContext context) throws SchemaException { - PrismContainerValueWrapper containerValueWrapper = new PrismContainerValueWrapperImpl(parent, value, status); - containerValueWrapper.setShowEmpty(context.isShowEmpty()); - containerValueWrapper.setExpanded(shouldBeExpanded(parent, value, context)); + PrismContainerValueWrapper containerValueWrapper = super.createValueWrapper(parent, value, status, context); containerValueWrapper.setHeterogenous(true); - - List> wrappers = new ArrayList<>(); - - for (ItemDefinition def : value.getDefinition().getDefinitions()) { - - Item childItem = value.findItem(def.getItemName()); - - if (childItem == null && def instanceof PrismContainerDefinition) { - LOGGER.trace("Skipping creating wrapper for {}, only property and reference wrappers are created for heterogeneous containers.", def); - continue; - } - - ItemWrapperFactory factory = registry.findWrapperFactory(def); - - ItemWrapper wrapper = factory.createWrapper(containerValueWrapper, def, context); - if (wrapper != null) { - wrappers.add(wrapper); - } - } - - containerValueWrapper.getItems().addAll((Collection) wrappers); return containerValueWrapper; } - protected boolean shouldBeExpanded(PrismContainerWrapper parent, PrismContainerValue value, WrapperContext context) { - if (value.isEmpty()) { - return context.isShowEmpty() || containsEmphasizedItems(parent.getDefinitions()); - } - - return true; + @Override + protected List getItemDefinitions(PrismContainerWrapper parent, PrismContainerValue value) { + return parent.getDefinitions().stream().filter(def -> filterDefinitins(value, def)).collect(Collectors.toList()); } - private boolean containsEmphasizedItems(List definitions) { - for (ItemDefinition def : definitions) { - if (def.isEmphasized()) { - return true; - } - } - - return false; - } - - protected List> createValuesWrapper(PrismContainerWrapper itemWrapper, PrismContainer item, WrapperContext context) throws SchemaException { - List> pvWrappers = new ArrayList<>(); - - if (item.getValues() == null || item.getValues().isEmpty()) { - PrismContainerValueWrapper valueWrapper = createValueWrapper(itemWrapper, item.createNewValue(), ValueStatus.ADDED, context); - pvWrappers.add(valueWrapper); - return pvWrappers; - } - - for (PrismContainerValue pcv : item.getValues()) { - PrismContainerValueWrapper valueWrapper = createValueWrapper(itemWrapper, pcv, ValueStatus.NOT_CHANGED, context); - pvWrappers.add(valueWrapper); - } - - return pvWrappers; - + private boolean filterDefinitins(PrismContainerValue value, ItemDefinition def) { + Item child = value.findItem(def.getItemName()); + return (child != null && !child.isEmpty()) || !(def instanceof PrismContainerDefinition); } /** @@ -160,7 +65,7 @@ public boolean match(ItemDefinition def) { PrismContainerDefinition containerDef = (PrismContainerDefinition) def; - if (containerDef.isMultiValue()) { + if (containerDef.isMultiValue() && isNotPolicyConstraint(containerDef)) { return false; } @@ -172,18 +77,22 @@ public boolean match(ItemDefinition def) { } } - if (containers > 2) { + return containers > 2; + + } + + private boolean isNotPolicyConstraint(PrismContainerDefinition containerDef) { + if (containerDef.getCompileTimeClass() == null) { return true; } - return false; - + return !AbstractPolicyConstraintType.class.isAssignableFrom(containerDef.getCompileTimeClass()); } @Override @PostConstruct public void register() { - registry.addToRegistry(this); + getRegistry().addToRegistry(this); } @Override @@ -191,17 +100,4 @@ public int getOrder() { return 110; } - @Override - public PrismContainerValueWrapper createContainerValueWrapper(PrismContainerWrapper objectWrapper, - PrismContainerValue objectValue, ValueStatus status, WrapperContext context) { - return null; - } - - @Override - public PrismContainerWrapper createWrapper(Item childContainer, ItemStatus status, WrapperContext context) - throws SchemaException { - return null; - } - - } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java index 70bb496ce32..c00f2fcfe72 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java @@ -14,6 +14,7 @@ import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.model.api.ModelService; +import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.task.api.TaskManager; import org.apache.commons.collections.CollectionUtils; @@ -27,7 +28,6 @@ import com.evolveum.midpoint.gui.impl.registry.GuiComponentRegistryImpl; import com.evolveum.midpoint.model.api.ModelInteractionService; import com.evolveum.midpoint.prism.*; -import com.evolveum.midpoint.prism.path.ItemName; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.util.exception.SchemaException; @@ -57,17 +57,10 @@ public abstract class ItemWrapperFactoryImpl parent, ItemDefinition def, WrapperContext context) throws SchemaException { ItemName name = def.getItemName(); - I childItem; - ItemStatus status; - if (CollectionUtils.isNotEmpty(context.getVirtualItemSpecification())) { - childItem = (I) def.instantiate(); - status = ItemStatus.NOT_CHANGED; - } else { - childItem = (I) parent.getNewValue().findItem(name); - status = getStatus(childItem); - } + I childItem = (I) parent.getNewValue().findItem(name); + ItemStatus status = getStatus(childItem); - if (!skipCreateWrapper(def, status, context, childItem == null || CollectionUtils.isEmpty(childItem.getValues()))) { + if (skipCreateWrapper(def, status, context, childItem == null || CollectionUtils.isEmpty(childItem.getValues()))) { LOGGER.trace("Skipping creating wrapper for non-existent item. It is not supported for {}", def); return null; } @@ -76,12 +69,11 @@ public IW createWrapper(PrismContainerValueWrapper parent, ItemDefinition childItem = (I) parent.getNewValue().findOrCreateItem(name); } - IW wrapper = createCompleteWrapper(parent, childItem, status, context); - registerWrapperPanel(wrapper); + IW wrapper = createWrapper(parent, childItem, status, context); return wrapper; } - private ItemStatus getStatus(I childItem) { + private ItemStatus getStatus(I childItem) { if (childItem == null) { return ItemStatus.ADDED; } @@ -92,13 +84,19 @@ private ItemStatus getStatus(I childItem) { public abstract void registerWrapperPanel(IW wrapper); - public IW createWrapper(Item childItem, ItemStatus status, WrapperContext context) throws SchemaException { - return createCompleteWrapper(null, (I) childItem, status, context); + public IW createWrapper(PrismContainerValueWrapper parent, Item childItem, ItemStatus status, WrapperContext context) throws SchemaException { + ItemDefinition def = childItem.getDefinition(); + if (skipCreateWrapper(def, status, context, childItem == null || CollectionUtils.isEmpty(childItem.getValues()))) { + LOGGER.trace("Skipping creating wrapper for non-existent item. It is not supported for {}", def); + if (parent != null && parent.getNewValue() != null) { + parent.getNewValue().remove(childItem); + } + return null; + } - } + IW itemWrapper = createWrapperInternal(parent, (I) childItem, status, context); - protected IW createCompleteWrapper(PrismContainerValueWrapper parent, I childItem, ItemStatus status, WrapperContext context) throws SchemaException { - IW itemWrapper = createWrapper(parent, childItem, status, context); + registerWrapperPanel(itemWrapper); List valueWrappers = createValuesWrapper(itemWrapper, (I) childItem, context); itemWrapper.getValues().addAll(valueWrappers); @@ -113,74 +111,49 @@ protected IW createCompleteWrapper(PrismContainerValueWrapper parent, I child setupWrapper(itemWrapper); return itemWrapper; - } - - protected abstract void setupWrapper(IW wrapper); - - protected > List createValuesWrapper(IW itemWrapper, I item, WrapperContext context) throws SchemaException { - List pvWrappers = new ArrayList<>(); - - //TODO : prismContainer.isEmpty() interates and check is all prismcontainervalues are empty.. isn't it confusing? - if (item.isEmpty() && item.getValues().isEmpty()) { - if (shouldCreateEmptyValue(item, context)) { - PV prismValue = createNewValue(item); - VW valueWrapper = createValueWrapper(itemWrapper, prismValue, ValueStatus.ADDED, context); - pvWrappers.add(valueWrapper); - } - return pvWrappers; - } - - for (PV pcv : (List)item.getValues()) { - if(canCreateValueWrapper(pcv)){ - VW valueWrapper = createValueWrapper(itemWrapper, pcv, ValueStatus.NOT_CHANGED, context); - pvWrappers.add(valueWrapper); - } - } - - return pvWrappers; } private boolean skipCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { if (QNameUtil.match(FocusType.F_LINK_REF, def.getItemName()) || QNameUtil.match(FocusType.F_PERSONA_REF, def.getItemName())) { LOGGER.trace("Skip creating wrapper for {}, it is not supported", def); - return false; + return true; } if (ItemProcessing.IGNORE == def.getProcessing()) { LOGGER.trace("Skip creating wrapper for {}, because item processig is set to IGNORE.", def); - return false; + return true; } - if (def.isExperimental() && !WebModelServiceUtils.isEnableExperimentalFeature(modelInteractionService, context.getTask(), context.getResult())) { + if (def.isExperimental() && !WebModelServiceUtils.isEnableExperimentalFeature(getModelInteractionService(), context.getTask(), context.getResult())) { LOGGER.trace("Skipping creating wrapper for {}, because experimental GUI features are turned off.", def); - return false; + return true; } if (ItemStatus.ADDED == status && def.isDeprecated()) { - LOGGER.trace("Skipping creating wrapeer for {}, because item is deprecated and doesn't contain any value.", def); - return false; + LOGGER.trace("Skipping creating wrapper for {}, because item is deprecated and doesn't contain any value.", def); + return true; } if (ItemStatus.ADDED == context.getObjectStatus() && !def.canAdd()) { LOGGER.trace("Skipping creating wrapper for {}, because ADD operation is not supported.", def); - return false; + return true; } if (ItemStatus.NOT_CHANGED == context.getObjectStatus()) { if (!def.canRead()) { LOGGER.trace("Skipping creating wrapper for {}, because read operation is not supported.", def); - return false; + return true; } } - return canCreateWrapper(def, status, context, isEmptyValue); + return !canCreateWrapper(def, status, context, isEmptyValue); } protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { - if (!context.isCreateOperational() && def.isOperational()) { + if (def.isOperational()) { LOGGER.trace("Skipping creating wrapper for {}, because it is operational.", def.getItemName()); return false; } @@ -188,6 +161,33 @@ protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, Wra return true; } + + protected abstract void setupWrapper(IW wrapper); + + protected > List createValuesWrapper(IW itemWrapper, I item, WrapperContext context) throws SchemaException { + List pvWrappers = new ArrayList<>(); + + //TODO : prismContainer.isEmpty() interates and check is all prismcontainervalues are empty.. isn't it confusing? + if (item.isEmpty() && item.getValues().isEmpty()) { + if (shouldCreateEmptyValue(item, context)) { + PV prismValue = createNewValue(item); + VW valueWrapper = createValueWrapper(itemWrapper, prismValue, ValueStatus.ADDED, context); + pvWrappers.add(valueWrapper); + } + return pvWrappers; + } + + for (PV pcv : (List)item.getValues()) { + if(canCreateValueWrapper(pcv)){ + VW valueWrapper = createValueWrapper(itemWrapper, pcv, ValueStatus.NOT_CHANGED, context); + pvWrappers.add(valueWrapper); + } + } + + return pvWrappers; + + } + private boolean determineReadOnly(IW itemWrapper, WrapperContext context) { Boolean readOnly = context.getReadOnly(); @@ -239,7 +239,7 @@ protected boolean canCreateValueWrapper(PV pcv) { protected abstract PV createNewValue(I item) throws SchemaException; - protected abstract IW createWrapper(PrismContainerValueWrapper parent, I childContainer, ItemStatus status, WrapperContext wrapperContext); + protected abstract IW createWrapperInternal(PrismContainerValueWrapper parent, I childContainer, ItemStatus status, WrapperContext wrapperContext); protected boolean shouldCreateEmptyValue(I item, WrapperContext context) { if (item.getDefinition().isEmphasized()) { @@ -274,4 +274,8 @@ public ModelService getModelService() { public TaskManager getTaskManager() { return taskManager; } + + public ModelInteractionService getModelInteractionService() { + return modelInteractionService; + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java index 534c90b0010..1c1755538f6 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingAppenderWrapperFactoryImpl.java @@ -8,22 +8,12 @@ import java.util.List; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.prism.*; - import org.springframework.stereotype.Component; -import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.panel.PrismPropertyPanel; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyWrapperImpl; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; +import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AppenderConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ClassLoggerConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LoggingConfigurationType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; /** @@ -59,7 +49,7 @@ protected LookupTableType getPredefinedValues(PrismProperty item, WrapperCont PrismContainerValue parentValue = ((PrismContainer) parentParent).getParent(); - if(!(parentValue.getRealValue() instanceof LoggingConfigurationType)) { + if(parentValue == null || isNotLoggingConfiguration(parentValue)) { throw new IllegalArgumentException("LoggingConfigurationType not found in parent for Appender"); } @@ -78,4 +68,8 @@ protected LookupTableType getPredefinedValues(PrismProperty item, WrapperCont return lookupTable; } + + private boolean isNotLoggingConfiguration(PrismContainerValue parentValue) { + return !(parentValue.getRealValue() instanceof LoggingConfigurationType); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java index aa3cc81c3ca..ee6ca0e74d3 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java @@ -60,31 +60,20 @@ public int getOrder() { @Override - public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper parent, PrismContainerValue value, ValueStatus status, WrapperContext context) - throws SchemaException { - PrismContainerValueWrapper containerValueWrapper = createContainerValueWrapper(parent, value, status, context); - containerValueWrapper.setExpanded(!value.isEmpty()); - - - List> wrappers = new ArrayList<>(); - for (ItemDefinition def : parent.getDefinitions()) { - if (QNameUtil.match(def.getTypeName(), ClassLoggerConfigurationType.COMPLEX_TYPE)) { - wrappers.add(createClassLoggingWrapper(containerValueWrapper, def, context)); - wrappers.add(createProfilingWrapper(containerValueWrapper, def, context)); + public void addItemWrapper(ItemDefinition def, PrismContainerValueWrapper containerValueWrapper, WrapperContext context, List> wrappers) throws SchemaException { + if (QNameUtil.match(def.getTypeName(), ClassLoggerConfigurationType.COMPLEX_TYPE)) { + wrappers.add(createClassLoggingWrapper(def, containerValueWrapper, context)); + wrappers.add(createProfilingWrapper(def, containerValueWrapper, context)); } else { super.addItemWrapper(def, containerValueWrapper, context, wrappers); } - } - - containerValueWrapper.getItems().addAll((Collection) wrappers); - return containerValueWrapper; } - private ItemWrapper createProfilingWrapper(PrismContainerValueWrapper parent, ItemDefinition def, WrapperContext context) throws SchemaException { + private ItemWrapper createProfilingWrapper(ItemDefinition def, PrismContainerValueWrapper parent, WrapperContext context) throws SchemaException { return profilingClassLoggerFactory.createWrapper(parent, def, context); } - private ItemWrapper createClassLoggingWrapper(PrismContainerValueWrapper parent, ItemDefinition def, WrapperContext context) throws SchemaException { + private ItemWrapper createClassLoggingWrapper(ItemDefinition def, PrismContainerValueWrapper parent, WrapperContext context) throws SchemaException { return classLoggerFactory.createWrapper(parent, def, context); } @@ -94,14 +83,14 @@ protected PrismContainerValue createNewValue(PrismContainer item) { } @Override - protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, PrismContainer childContainer, + protected PrismContainerWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { return new PrismContainerWrapperImpl<>(parent, childContainer, status); } @Override public PrismContainerValueWrapper createContainerValueWrapper(PrismContainerWrapper objectWrapper, PrismContainerValue objectValue, ValueStatus status, WrapperContext context) { - return new PrismContainerValueWrapperImpl(objectWrapper, objectValue, status); + return new PrismContainerValueWrapperImpl<>(objectWrapper, objectValue, status); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java index 7a50b3a44ed..7940438813e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingPackageWrapperFactoryImpl.java @@ -8,18 +8,12 @@ import java.util.List; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; - import org.apache.commons.lang3.EnumUtils; import org.springframework.stereotype.Component; -import com.evolveum.midpoint.gui.api.prism.ItemStatus; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.impl.page.admin.configuration.component.ComponentLoggerType; import com.evolveum.midpoint.gui.impl.page.admin.configuration.component.StandardLoggerType; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.panel.PrismPropertyPanel; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyWrapperImpl; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismProperty; import com.evolveum.midpoint.prism.PrismPropertyDefinition; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java index 53aa812e3c1..d4976ef1356 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/NoEmptyValueContainerWrapperFactoryImpl.java @@ -6,18 +6,13 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractWorkItemOutputType; - import com.evolveum.midpoint.xml.ns._public.common.common_3.ApprovalSchemaType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; - /** * @author katka * @@ -31,9 +26,6 @@ public boolean match(ItemDefinition def) { || AbstractWorkItemOutputType.COMPLEX_TYPE.equals(def.getTypeName()) || ApprovalSchemaType.COMPLEX_TYPE .equals(def.getTypeName()); } - //CaseEventType.COMPLEX_TYPE.equals(def.getTypeName()) || WorkItemEventType.COMPLEX_TYPE.equals(def.getTypeName()) || - // WorkItemCompletionEventType.COMPLEX_TYPE.equals(def.getTypeName()) || WorkItemDelegationEventType.COMPLEX_TYPE.equals(def.getTypeName()); - @Override public void register() { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/MetadataWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/OperationalContainerWrapperFactory.java similarity index 57% rename from gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/MetadataWrapperFactory.java rename to gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/OperationalContainerWrapperFactory.java index 4e9375fc380..53800abae53 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/MetadataWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/OperationalContainerWrapperFactory.java @@ -6,33 +6,24 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import java.util.List; - import javax.annotation.PostConstruct; -import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.prism.PrismContainer; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.prism.ItemDefinition; +import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.util.QNameUtil; -import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; /** * @author katka * */ @Component -public class MetadataWrapperFactory extends PrismContainerWrapperFactoryImpl{ +public class OperationalContainerWrapperFactory extends PrismContainerWrapperFactoryImpl{ @Override public boolean match(ItemDefinition def) { @@ -50,18 +41,6 @@ public int getOrder() { return 10; } - protected void addItemWrapper(ItemDefinition def, PrismContainerValueWrapper containerValueWrapper, - WrapperContext context, List> wrappers) throws SchemaException { - - ItemWrapperFactory factory = getRegistry().findWrapperFactory(def); - - context.setCreateOperational(true); - ItemWrapper wrapper = factory.createWrapper(containerValueWrapper, def, context); - wrapper.setReadOnly(true); - wrappers.add(wrapper); - context.setCreateOperational(false); - } - @Override protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { return true; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java index a842af9ccf6..21e5440ae32 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismContainerWrapperFactoryImpl.java @@ -11,17 +11,16 @@ import java.util.List; import javax.annotation.PostConstruct; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory; import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; - -import org.springframework.stereotype.Component; - import com.evolveum.midpoint.gui.api.prism.ItemStatus; import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; import com.evolveum.midpoint.gui.impl.prism.panel.PrismContainerPanel; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismContainerValueWrapperImpl; import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismContainerWrapperImpl; import com.evolveum.midpoint.prism.*; @@ -87,12 +86,18 @@ protected void addItemWrapper(ItemDefinition def, PrismContainerValueWrapper< WrapperContext context, List> wrappers) throws SchemaException { ItemWrapper wrapper = createChildWrapper(def, containerValueWrapper, context); + if (wrapper != null) { wrappers.add(wrapper); } } protected ItemWrapper createChildWrapper(ItemDefinition def, PrismContainerValueWrapper containerValueWrapper, WrapperContext context) throws SchemaException { + ItemWrapperFactory factory = getChildWrapperFactory(def); + return factory.createWrapper(containerValueWrapper, def, context); + } + + private ItemWrapperFactory getChildWrapperFactory(ItemDefinition def) throws SchemaException { ItemWrapperFactory factory = getRegistry().findWrapperFactory(def); if (factory == null) { LOGGER.error("Cannot find factory for {}", def); @@ -100,17 +105,10 @@ protected void addItemWrapper(ItemDefinition def, PrismContainerValueWrapper< } LOGGER.trace("Found factory {} for {}", factory, def); + return factory; + } - ItemWrapper wrapper = factory.createWrapper(containerValueWrapper, def, context); - - if (wrapper == null) { - LOGGER.trace("Null wrapper created for {}. Skipping.", def); - return null; - } - wrapper.setShowEmpty(context.isShowEmpty(), false); - return wrapper; - } @Override protected PrismContainerValue createNewValue(PrismContainer item) { @@ -118,7 +116,7 @@ protected PrismContainerValue createNewValue(PrismContainer item) { } @Override - protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, PrismContainer childContainer, + protected PrismContainerWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { return new PrismContainerWrapperImpl<>(parent, childContainer, status); @@ -131,7 +129,7 @@ public void registerWrapperPanel(PrismContainerWrapper wrapper) { @Override public PrismContainerValueWrapper createContainerValueWrapper(PrismContainerWrapper objectWrapper, PrismContainerValue objectValue, ValueStatus status, WrapperContext context) { - return new PrismContainerValueWrapperImpl(objectWrapper, objectValue, status); + return new PrismContainerValueWrapperImpl<>(objectWrapper, objectValue, status); } protected boolean shouldBeExpanded(PrismContainerWrapper parent, PrismContainerValue value, WrapperContext context) { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java index 4eaf9f6b6ba..b79a0a41153 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java @@ -11,26 +11,18 @@ import javax.annotation.PostConstruct; import javax.xml.namespace.QName; -import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; -import com.evolveum.midpoint.gui.api.factory.wrapper.PrismObjectWrapperFactory; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; - -import com.evolveum.midpoint.gui.impl.prism.panel.PrismContainerPanel; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismObjectValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismObjectValueWrapperImpl; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismObjectWrapperImpl; - import org.apache.commons.collections4.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; +import com.evolveum.midpoint.gui.api.factory.wrapper.PrismObjectWrapperFactory; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismObjectWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; +import com.evolveum.midpoint.gui.api.prism.wrapper.*; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; +import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismObjectValueWrapperImpl; +import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismObjectWrapperImpl; import com.evolveum.midpoint.model.api.ModelInteractionService; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.schema.result.OperationResult; @@ -39,7 +31,10 @@ import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.web.component.prism.ValueStatus; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; +import com.evolveum.midpoint.xml.ns._public.common.common_3.AuthorizationPhaseType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.DisplayType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.VirtualContainersSpecificationType; /** * @author katka @@ -49,8 +44,6 @@ public class PrismObjectWrapperFactoryImpl extends PrismCo private static final Trace LOGGER = TraceManager.getTrace(PrismObjectWrapperFactoryImpl.class); - private static final String DOT_CLASS = PrismObjectWrapperFactoryImpl.class.getName() + "."; - private static final QName VIRTUAL_CONTAINER_COMPLEX_TYPE = new QName("VirtualContainerType"); private static final QName VIRTUAL_CONTAINER = new QName("virtualContainer"); @@ -74,7 +67,7 @@ public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStat PrismObjectWrapper objectWrapper = createObjectWrapper(object, status); if (context.getReadOnly() != null) { - objectWrapper.setReadOnly(context.getReadOnly().booleanValue()); + objectWrapper.setReadOnly(context.getReadOnly()); } context.setShowEmpty(ItemStatus.ADDED == status); objectWrapper.setExpanded(true); @@ -114,11 +107,11 @@ public void updateWrapper(PrismObjectWrapper wrapper, WrapperContext context) @Override public PrismObjectValueWrapper createContainerValueWrapper(PrismContainerWrapper objectWrapper, PrismContainerValue objectValue, ValueStatus status, WrapperContext context) { - return new PrismObjectValueWrapperImpl((PrismObjectWrapper) objectWrapper, (PrismObjectValue) objectValue, status); + return new PrismObjectValueWrapperImpl<>((PrismObjectWrapper) objectWrapper, (PrismObjectValue) objectValue, status); } public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStatus status) { - return new PrismObjectWrapperImpl(object, status); + return new PrismObjectWrapperImpl<>(object, status); } @Override @@ -139,7 +132,7 @@ public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper mCtd.setHelp(WebComponentUtil.getOrigStringFromPoly(display.getHelp())); mCtd.setRuntimeSchema(true); - MutablePrismContainerDefinition def = getPrismContext().definitionFactory().createContainerDefinition(VIRTUAL_CONTAINER, mCtd); + MutablePrismContainerDefinition def = getPrismContext().definitionFactory().createContainerDefinition(VIRTUAL_CONTAINER, mCtd); def.setMaxOccurs(1); def.setDisplayName(WebComponentUtil.getOrigStringFromPoly(display.getLabel())); def.setDynamic(true); @@ -152,12 +145,16 @@ public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper WrapperContext ctx = context.clone(); ctx.setVirtualItemSpecification(virtualContainer.getItem()); - ItemWrapper iw = factory.createWrapper(objectValueWrapper, def, ctx); - iw.setVisibleOverwrite(virtualContainer.getVisibility()); + PrismContainer virtualPrismContainer = def.instantiate(); + ItemStatus virtualContainerStatus = context.getObjectStatus() != null ? context.getObjectStatus() : ItemStatus.NOT_CHANGED; + + + ItemWrapper iw = factory.createWrapper(objectValueWrapper, virtualPrismContainer, virtualContainerStatus, ctx); if (iw == null) { continue; } + iw.setVisibleOverwrite(virtualContainer.getVisibility()); ((List) objectValueWrapper.getItems()).add(iw); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java index 8aff083e91a..bd7c1941310 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java @@ -72,7 +72,7 @@ protected PrismPropertyValue createNewValue(PrismProperty item) throws Sch } @Override - protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, + protected PrismPropertyWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext wrapperContext) { PrismPropertyWrapper propertyWrapper = new PrismPropertyWrapperImpl<>(parent, item, status); propertyWrapper.setPredefinedValues(getPredefinedValues(item, wrapperContext)); @@ -84,7 +84,6 @@ protected LookupTableType getPredefinedValues(PrismProperty item, WrapperCont if (valueEnumerationRef == null) { return null; } - //TODO: task and result from context Task task = wrapperContext.getTask(); OperationResult result = wrapperContext.getResult().createSubresult(OPERATION_LOAD_LOOKUP_TABLE); Collection> options = WebModelServiceUtils diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java index 203c9614e36..210a094f679 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismReferenceWrapperFactory.java @@ -54,27 +54,25 @@ public int getOrder() { } @Override - protected PrismReferenceValue createNewValue(PrismReference item) throws SchemaException { + protected PrismReferenceValue createNewValue(PrismReference item) { PrismReferenceValue prv = getPrismContext().itemFactory().createReferenceValue(); item.getValues().add(prv); return prv; } @Override - protected PrismReferenceWrapper createWrapper(PrismContainerValueWrapper parent, PrismReference item, + protected PrismReferenceWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismReference item, ItemStatus status, WrapperContext ctx) { - PrismReferenceWrapperImpl wrapper = new PrismReferenceWrapperImpl<>(parent, item, status); - return wrapper; + return new PrismReferenceWrapperImpl<>(parent, item, status); } @Override public PrismReferenceValueWrapperImpl createValueWrapper(PrismReferenceWrapper parent, PrismReferenceValue value, ValueStatus status, - WrapperContext context) throws SchemaException { + WrapperContext context) { - PrismReferenceValueWrapperImpl refValue = new PrismReferenceValueWrapperImpl<>(parent, value, status); - return refValue; + return new PrismReferenceValueWrapperImpl<>(parent, value, status); } @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java index 5bb35eb93d7..7b38f3ebe3c 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java @@ -10,17 +10,15 @@ import java.util.List; import javax.xml.namespace.QName; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; +import com.evolveum.midpoint.gui.impl.prism.panel.ProfilingClassLoggerPanel; import com.evolveum.midpoint.gui.impl.prism.wrapper.ProfilingClassLoggerContainerValueWrapperImpl; import com.evolveum.midpoint.gui.impl.prism.wrapper.ProfilingClassLoggerContainerWrapperImpl; -import com.evolveum.midpoint.gui.impl.prism.panel.ProfilingClassLoggerPanel; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.prism.PrismContainerValue; @@ -43,6 +41,8 @@ public boolean match(ItemDefinition def) { return false; } + + @Override protected boolean canCreateValueWrapper(PrismContainerValue value) { if(value == null || value.getRealValue() == null) { @@ -56,7 +56,7 @@ protected boolean canCreateValueWrapper(PrismContainerValue createWrapper(PrismContainerValueWrapper parent, + protected PrismContainerWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { PrismContainer clone = childContainer.clone(); return new ProfilingClassLoggerContainerWrapperImpl<>(parent, clone, status); @@ -96,9 +96,12 @@ public PrismContainerValueWrapper createContainerV PrismContainerValue objectValue, ValueStatus status, WrapperContext context) { ClassLoggerConfigurationType logger = objectValue.getRealValue(); - logger.setPackage(LOGGER_PROFILING); + if (logger != null) { + logger.setPackage(LOGGER_PROFILING); + } return new ProfilingClassLoggerContainerValueWrapperImpl(objectWrapper, objectValue, status); } + } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java index 408b6adfbd2..bd38990d2b6 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProtectedStringWrapperFactory.java @@ -43,10 +43,9 @@ public int getOrder() { } @Override - protected PrismPropertyWrapper createWrapper(PrismContainerValueWrapper parent, PrismProperty item, + protected PrismPropertyWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismProperty item, ItemStatus status, WrapperContext ctx) { - ProtectedStringTypeWrapperImpl propertyWrapper = new ProtectedStringTypeWrapperImpl(parent, item, status); - return propertyWrapper; + return new ProtectedStringTypeWrapperImpl(parent, item, status); } @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java index 439d5674960..1911adabfe0 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ResourceAttributeWrapperFactoryImpl.java @@ -9,16 +9,14 @@ import javax.annotation.PostConstruct; import javax.xml.namespace.QName; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.panel.ResourceAttributeDefinitionPanel; import com.evolveum.midpoint.gui.api.prism.wrapper.ResourceAttributeWrapper; +import com.evolveum.midpoint.gui.impl.prism.panel.ResourceAttributeDefinitionPanel; +import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyValueWrapper; import com.evolveum.midpoint.gui.impl.prism.wrapper.ResourceAttributeWrapperImpl; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismPropertyValue; @@ -46,9 +44,8 @@ public int getOrder() { @Override public PrismPropertyValueWrapper createValueWrapper(ResourceAttributeWrapper parent, - PrismPropertyValue value, ValueStatus status, WrapperContext context) throws SchemaException { - PrismPropertyValueWrapper valueWrapper = new PrismPropertyValueWrapper<>(parent, value, status); - return valueWrapper; + PrismPropertyValue value, ValueStatus status, WrapperContext context) { + return new PrismPropertyValueWrapper<>(parent, value, status); } @PostConstruct @@ -70,10 +67,9 @@ protected PrismPropertyValue createNewValue(ResourceAttribute item) throws } @Override - protected ResourceAttributeWrapper createWrapper(PrismContainerValueWrapper parent, + protected ResourceAttributeWrapper createWrapperInternal(PrismContainerValueWrapper parent, ResourceAttribute childContainer, ItemStatus status, WrapperContext ctx) { - ResourceAttributeWrapper propertyWrapper = new ResourceAttributeWrapperImpl<>(parent, childContainer, status); - return propertyWrapper; + return new ResourceAttributeWrapperImpl<>(parent, childContainer, status); } @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java index 4c9d4734b96..34a4c872b0d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java @@ -25,7 +25,6 @@ import com.evolveum.midpoint.gui.api.prism.ItemStatus; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; import com.evolveum.midpoint.gui.api.util.WebComponentUtil; -import com.evolveum.midpoint.gui.impl.prism.panel.PrismContainerPanel; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismReferenceWrapper; import com.evolveum.midpoint.gui.impl.prism.wrapper.ShadowAssociationWrapperImpl; @@ -64,39 +63,39 @@ public int getOrder() { } @Override - protected PrismContainerWrapper createWrapper(PrismContainerValueWrapper parent, PrismContainer childContainer, + protected PrismContainerWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { OperationResult parentResult = ctx.getResult(); - if (isNotShadow(parent, parentResult)) { - return super.createWrapper(parent, childContainer, status, ctx); + if (isNotShadow(ctx, parentResult)) { + return super.createWrapperInternal(parent, childContainer, status, ctx); } if (isNotAssociation(childContainer)) { parentResult.recordStatus(OperationResultStatus.NOT_APPLICABLE, "Association for " + childContainer.getComplexTypeDefinition().getTypeClass() + " is not supported"); LOGGER.debug("Association for {} is not supported", childContainer.getComplexTypeDefinition().getTypeClass()); - return super.createWrapper(parent, childContainer, status, ctx); + return super.createWrapperInternal(parent, childContainer, status, ctx); } - ShadowType shadow = (ShadowType) parent.getRealValue(); + ShadowType shadow = (ShadowType) ctx.getObject().asObjectable(); PrismObject resource = loadResource(shadow, ctx); if (resource == null) { - return super.createWrapper(parent, childContainer, status, ctx); + return super.createWrapperInternal(parent, childContainer, status, ctx); } Collection refinedAssociationDefinitions = loadRefinedAssociationDefinitions(resource, shadow, parentResult); if (refinedAssociationDefinitions == null) { - return super.createWrapper(parent, childContainer, status, ctx); + return super.createWrapperInternal(parent, childContainer, status, ctx); } ShadowAssociationWrapperImpl associationWrapper = createShadowAssociationWrapper(parent, childContainer, resource, refinedAssociationDefinitions, shadow, status, parentResult); if (associationWrapper == null) { - return super.createWrapper(parent, childContainer, status, ctx); + return super.createWrapperInternal(parent, childContainer, status, ctx); } return associationWrapper; @@ -112,6 +111,11 @@ private PrismReferenceDefinition createShadowAssocationDef(RefinedAssociationDef } + @Override + protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, WrapperContext context, boolean isEmptyValue) { + return super.canCreateWrapper(def, status, context, isEmptyValue); + } + private PrismObject loadResource(ShadowType shadow, WrapperContext ctx) { String resourceOid = shadow.getResourceRef().getOid(); if (resourceOid == null) { @@ -160,8 +164,9 @@ private Collection loadRefinedAssociationDefinitio return refinedAssociationDefinitions; } - private boolean isNotShadow(PrismContainerValueWrapper parent, OperationResult parentResult) { - ObjectType objectType = (ObjectType) parent.getRealValue(); + private boolean isNotShadow(WrapperContext ctx, OperationResult parentResult) { + PrismObject object = ctx.getObject(); + ObjectType objectType = (ObjectType) object.asObjectable(); if (!(objectType instanceof ShadowType)) { parentResult.recordFatalError("Something very strange happened. Association container in the" + objectType.getClass().getSimpleName() + "?"); return true; @@ -212,7 +217,7 @@ protected >> Lis for (RefinedAssociationDefinition def : associationWrapper.getRefinedAssociationDefinitions()) { PrismReference shadowAss = fillInShadowReference(def, item); - PrismReferenceWrapper shadowReference = (PrismReferenceWrapper) referenceWrapperFactory.createCompleteWrapper(shadowValueWrapper, shadowAss, shadowAss.isEmpty() ? ItemStatus.ADDED : ItemStatus.NOT_CHANGED, context); + PrismReferenceWrapper shadowReference = (PrismReferenceWrapper) referenceWrapperFactory.createWrapper(shadowValueWrapper, shadowAss, shadowAss.isEmpty() ? ItemStatus.ADDED : ItemStatus.NOT_CHANGED, context); shadowReference.setFilter(WebComponentUtil.createAssociationShadowRefFilter(def, getPrismContext(), associationWrapper.getResource().getOid())); shadowReferences.add(shadowReference); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java index 237e3fb8983..bd393441ffe 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAttributesWrapperFactoryImpl.java @@ -6,18 +6,14 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import com.evolveum.midpoint.gui.api.prism.ItemStatus; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.impl.prism.panel.PrismContainerPanel; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismContainerWrapperImpl; import com.evolveum.midpoint.prism.Containerable; import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismContainer; import com.evolveum.midpoint.schema.processor.ResourceAttributeContainerDefinition; import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowAttributesType; -import org.springframework.stereotype.Component; @Component public class ShadowAttributesWrapperFactoryImpl extends PrismContainerWrapperFactoryImpl { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java index 893790edc98..332ed02748d 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowWrapperFactoryImpl.java @@ -6,14 +6,11 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.evolveum.midpoint.gui.api.prism.ItemStatus; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismObjectWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; import com.evolveum.midpoint.gui.impl.prism.wrapper.ShadowWrapperImpl; -import com.evolveum.midpoint.model.api.ModelService; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismObjectDefinition; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java index dfc6d9dd18a..91b38bfd209 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java @@ -6,47 +6,31 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import com.evolveum.midpoint.gui.api.prism.ItemStatus; -import com.evolveum.midpoint.gui.api.util.WebComponentUtil; -import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; +import java.util.Collection; +import java.util.stream.Collectors; + +import org.apache.commons.lang.StringUtils; +import org.springframework.stereotype.Component; + import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.panel.PrismPropertyPanel; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyWrapperImpl; +import com.evolveum.midpoint.gui.api.util.WebComponentUtil; import com.evolveum.midpoint.prism.ItemDefinition; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismProperty; -import com.evolveum.midpoint.prism.PrismReferenceValue; -import com.evolveum.midpoint.schema.GetOperationOptions; -import com.evolveum.midpoint.schema.SelectorOptions; -import com.evolveum.midpoint.schema.result.OperationResult; -import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.util.exception.*; -import com.evolveum.midpoint.util.logging.Trace; -import com.evolveum.midpoint.util.logging.TraceManager; -import com.evolveum.midpoint.xml.ns._public.common.common_3.*; - +import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableRowType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringTranslationType; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; -import org.apache.commons.lang.StringUtils; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.stream.Collectors; - @Component public class TaskHandlerWrapperFactory extends PrismPropertyWrapperFactoryImpl { - private static final transient Trace LOGGER = TraceManager.getTrace(TaskHandlerWrapperFactory.class); - - private static final String OPERATION_DETERMINE_LOOKUP_TABLE = "determineLookupTable"; - @Override protected LookupTableType getPredefinedValues(PrismProperty item, WrapperContext ctx) { PrismObject prismObject = getParent(ctx); - if (prismObject == null || !TaskType.class.equals(prismObject.getCompileTimeClass())) { + if (!TaskType.class.equals(prismObject.getCompileTimeClass())) { return super.getPredefinedValues(item, ctx); } @@ -58,7 +42,7 @@ protected LookupTableType getPredefinedValues(PrismProperty item, Wrappe TaskType task = (TaskType) prismObject.asObjectable(); Collection assignmentTypes = task.getAssignment() .stream() - .filter(assignmentType -> WebComponentUtil.isArchetypeAssignment(assignmentType)) + .filter(WebComponentUtil::isArchetypeAssignment) .collect(Collectors.toList()); Collection handlers; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ExpressionWrapper.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ExpressionWrapper.java index ddb0d5e8842..e26910bed57 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ExpressionWrapper.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/ExpressionWrapper.java @@ -29,20 +29,6 @@ public class ExpressionWrapper extends PrismPropertyWrapperImpl public ExpressionWrapper(@Nullable PrismContainerValueWrapper parent, PrismProperty property, ItemStatus status) { super(parent, property, status); - - PrismContainerWrapperImpl outboundContainer = (PrismContainerWrapperImpl)parent.getParent(); - if (outboundContainer != null) { - PrismContainerValueWrapperImpl outboundValue = (PrismContainerValueWrapperImpl) outboundContainer.getParent(); - if (outboundValue != null) { - PrismContainerWrapperImpl associationContainer = (PrismContainerWrapperImpl) outboundValue.getParent(); - if (associationContainer != null) { - PrismContainerValueWrapperImpl constructionContainer = (PrismContainerValueWrapperImpl) associationContainer.getParent(); - if (constructionContainer != null && constructionContainer.getRealValue() instanceof ConstructionType) { - construction = (ConstructionType) constructionContainer.getRealValue(); - } - } - } - } } public boolean isConstructionExpression(){ @@ -101,6 +87,31 @@ public boolean isAttributeExpression() { } public ConstructionType getConstruction() { + if (getParent() == null) { + return construction; + } + + if (construction == null) { + PrismContainerWrapperImpl outboundContainer = getParent().getParent(); + if (outboundContainer == null) { + return construction; + } + + PrismContainerValueWrapperImpl outboundValue = (PrismContainerValueWrapperImpl) outboundContainer.getParent(); + if (outboundValue == null) { + return construction; + } + + PrismContainerWrapperImpl associationContainer = (PrismContainerWrapperImpl) outboundValue.getParent(); + if (associationContainer != null) { + PrismContainerValueWrapperImpl constructionContainer = (PrismContainerValueWrapperImpl) associationContainer.getParent(); + if (constructionContainer != null && constructionContainer.getRealValue() instanceof ConstructionType) { + construction = (ConstructionType) constructionContainer.getRealValue(); + } + } + + } + return construction; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java index 832c5416567..643047bfbf9 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/wrapper/PrismContainerValueWrapperImpl.java @@ -483,4 +483,8 @@ public boolean isVisible() { return ((PrismContainerWrapper) parent).isExpanded() || isHeterogenous(); } + + public PrismContainerDefinition getDefinition() { + return getNewValue().getDefinition(); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java index b0842fdbecb..61ef5ce6a57 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/registry/GuiComponentRegistryImpl.java @@ -15,6 +15,10 @@ import javax.xml.namespace.QName; +import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.prism.*; + import org.springframework.stereotype.Component; import com.evolveum.midpoint.gui.api.factory.GuiComponentFactory; @@ -23,9 +27,6 @@ import com.evolveum.midpoint.gui.api.factory.wrapper.ItemWrapperFactory; import com.evolveum.midpoint.gui.api.factory.wrapper.PrismObjectWrapperFactory; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismValueWrapper; -import com.evolveum.midpoint.prism.ItemDefinition; -import com.evolveum.midpoint.prism.PrismObjectDefinition; -import com.evolveum.midpoint.prism.PrismValue; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; @@ -116,6 +117,22 @@ public PrismContainerWrapperFactory findContainerWrapperFactory(PrismContainerDefinition def) { + ItemWrapperFactory factory = findWrapperFactory(def); + if (factory == null) { + return null; + } + + //TODO do we want to throw exception? or just pretend as nothing has happend? + if (!(factory instanceof PrismContainerWrapperFactory)) { + LOGGER.trace("Unexpected facotry found, expected container wrapper factory, byt found: {}", factory); + return null; + } + + return (PrismContainerWrapperFactory) factory; + } + public PrismObjectWrapperFactory getObjectWrapperFactory(PrismObjectDefinition objectDef) { return (PrismObjectWrapperFactory) findWrapperFactory(objectDef); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java index 33f582db9f7..c0f31f61cb6 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/assignment/AssignmentPanel.java @@ -780,6 +780,9 @@ private IModel getKindIntentLabelModelForDisplayNamePanel(PrismContainer PrismContainerValueWrapper constructionValue = null; try { PrismContainerWrapper construction = modelObject.findContainer(AssignmentType.F_CONSTRUCTION); + if (construction == null) { + return null; + } constructionValue = construction.getValue(); } catch (SchemaException e) { LOGGER.error("Unexpected problem during construction wrapper lookup, {}", e.getMessage(), e); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java index 3c94a93ebdf..cefa473cd5e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/component/objectdetails/FocusProjectionsTabPanel.java @@ -16,6 +16,7 @@ import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; @@ -335,6 +336,9 @@ public String getCssClass() @Override protected Component createColumnPanel(String componentId, IModel rowModel) { IW object = rowModel.getObject(); + if (object == null) { + return new WebMarkupContainer(componentId); + } List> values = object.getValues(); List pendingOperations = new ArrayList(); values.forEach(value -> { diff --git a/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/AbstractGuiIntegrationTest.java b/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/AbstractGuiIntegrationTest.java index c4763526184..abbf7f4447d 100644 --- a/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/AbstractGuiIntegrationTest.java +++ b/gui/admin-gui/src/test/java/com/evolveum/midpoint/web/AbstractGuiIntegrationTest.java @@ -225,15 +225,15 @@ public PrismObjectWrapperFactory findObjectWrapperFact @Override public IW createItemWrapper(I item, ItemStatus status, WrapperContext ctx) throws SchemaException { - ItemWrapperFactory factory = (ItemWrapperFactory) registry.findWrapperFactory(item.getDefinition()); + ItemWrapperFactory factory = registry.findWrapperFactory(item.getDefinition()); ctx.setCreateIfEmpty(true); - return factory.createWrapper(item, status, ctx); + return factory.createWrapper(null, item, status, ctx); } @Override public VW createValueWrapper(IW parentWrapper, PV newValue, ValueStatus status, WrapperContext context) throws SchemaException { - ItemWrapperFactory factory = (ItemWrapperFactory) registry.findWrapperFactory(parentWrapper); + ItemWrapperFactory factory = registry.findWrapperFactory(parentWrapper); return factory.createValueWrapper(parentWrapper, newValue, status, context); } From fccae69c9c81ede6983c7aaac78030da36941af4 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Tue, 12 May 2020 22:14:31 +0200 Subject: [PATCH 33/41] MVC based REST: refined config, basic implementation of XML/JSON/YAML Our @Converter (used in executeScript) is not treated yet. --- .../evolveum/midpoint/web/boot/WebConfig.java | 2 + model/rest-impl/pom.xml | 18 ++- .../MidpointAbstractHttpMessageConverter.java | 123 ++++++++++++++++++ .../MidpointJsonHttpMessageConverter.java | 39 ++++++ .../impl/MidpointXmlHttpMessageConverter.java | 39 ++++++ .../MidpointYamlHttpMessageConverter.java | 41 ++++++ .../rest/impl/ModelRestController.java | 11 +- .../midpoint/rest/impl/RestApiIndex.java | 29 ++--- .../midpoint/rest/impl/RestConfig.java | 74 +++++------ 9 files changed, 298 insertions(+), 78 deletions(-) create mode 100644 model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointAbstractHttpMessageConverter.java create mode 100644 model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointJsonHttpMessageConverter.java create mode 100644 model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointXmlHttpMessageConverter.java create mode 100644 model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointYamlHttpMessageConverter.java diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java index b2406601510..93e61fb2630 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebConfig.java @@ -15,6 +15,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -28,6 +29,7 @@ */ @Configuration @Import(com.evolveum.midpoint.rest.impl.RestConfig.class) +@EnableWebMvc public class WebConfig { @Bean diff --git a/model/rest-impl/pom.xml b/model/rest-impl/pom.xml index 96b7af62619..c5f16377194 100644 --- a/model/rest-impl/pom.xml +++ b/model/rest-impl/pom.xml @@ -68,20 +68,18 @@ spring-beans - javax.servlet - javax.servlet-api - - - com.fasterxml.jackson.core - jackson-databind + org.springframework + spring-context + - com.fasterxml.jackson.core - jackson-core + javax.servlet + javax.servlet-api + - org.springframework - spring-context + org.jetbrains + annotations diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointAbstractHttpMessageConverter.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointAbstractHttpMessageConverter.java new file mode 100644 index 00000000000..d26efadbecd --- /dev/null +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointAbstractHttpMessageConverter.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.rest.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.function.Function; +import javax.xml.namespace.QName; + +import org.jetbrains.annotations.NotNull; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; + +import com.evolveum.midpoint.common.LocalizationService; +import com.evolveum.midpoint.prism.*; +import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.util.LocalizableMessage; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.logging.LoggingUtils; +import com.evolveum.midpoint.util.logging.Trace; +import com.evolveum.midpoint.util.logging.TraceManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType; + +public abstract class MidpointAbstractHttpMessageConverter extends AbstractHttpMessageConverter { + + private static final Trace LOGGER = TraceManager.getTrace(MidpointAbstractHttpMessageConverter.class); + + protected final PrismContext prismContext; + private final LocalizationService localizationService; + + protected MidpointAbstractHttpMessageConverter(PrismContext prismContext, + LocalizationService localizationService, MediaType... supportedMediaTypes) { + super(supportedMediaTypes); + this.prismContext = prismContext; + this.localizationService = localizationService; + } + + protected abstract PrismSerializer getSerializer(); + protected abstract PrismParser getParser(InputStream entityStream); + + @Override + protected boolean supports(Class clazz) { + return clazz.getName().startsWith("com.evolveum.midpoint") + || clazz.getName().startsWith("com.evolveum.prism"); + } + + @Override + protected T readInternal(@NotNull Class clazz, @NotNull HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + PrismParser parser = getParser(inputMessage.getBody()); + + T object; + try { + if (PrismObject.class.isAssignableFrom(clazz)) { + object = (T) parser.parse(); + } else { + object = parser.parseRealValue(); // TODO consider prescribing type here (if no converter is specified) + } + +/* +// TODO treat multivalues here - there is just one @Converter usage and one implementation: ExecuteScriptConverter +// com.evolveum.midpoint.model.impl.ModelRestService.executeScript - will we need argument resolver for this + if (object != null && !clazz.isAssignableFrom(object.getClass())) { + Optional converterAnnotation = Arrays.stream(annotations).filter(a -> a instanceof Converter).findFirst(); + if (converterAnnotation.isPresent()) { + Class converterClass = ((Converter) converterAnnotation.get()).value(); + ConverterInterface converter; + try { + converter = converterClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new SystemException("Couldn't instantiate converter class " + converterClass, e); + } + object = (T) converter.convert(object); + } + } +*/ + return object; + } catch (SchemaException e) { + throw new HttpMessageNotReadableException("Failure during read", e, inputMessage); + } + } + + @Override + protected void writeInternal(@NotNull T object, @NotNull HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + // TODO implement in the standard serializer; also change root name + QName fakeQName = new QName(PrismConstants.NS_TYPES, "object"); + String serializedForm; + + PrismSerializer serializer = getSerializer() + .options(SerializationOptions.createSerializeReferenceNames()); + + try { + if (object instanceof ObjectType) { + ObjectType ot = (ObjectType) object; + serializedForm = serializer.serialize(ot.asPrismObject()); + } else if (object instanceof PrismObject) { + serializedForm = serializer.serialize((PrismObject) object); + } else if (object instanceof OperationResult) { + Function resolveKeys = msg -> localizationService.translate(msg, Locale.US); + OperationResultType operationResultType = ((OperationResult) object).createOperationResultType(resolveKeys); + serializedForm = serializer.serializeAnyData(operationResultType, fakeQName); + } else { + serializedForm = serializer.serializeAnyData(object, fakeQName); + } + outputMessage.getBody().write(serializedForm.getBytes(StandardCharsets.UTF_8)); + } catch (SchemaException | RuntimeException e) { + LoggingUtils.logException(LOGGER, "Couldn't marshal element to string: {}", e, object); + } + } +} diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointJsonHttpMessageConverter.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointJsonHttpMessageConverter.java new file mode 100644 index 00000000000..97aab8e4f62 --- /dev/null +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointJsonHttpMessageConverter.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.rest.impl; + +import java.io.InputStream; + +import org.springframework.http.MediaType; + +import com.evolveum.midpoint.common.LocalizationService; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismParser; +import com.evolveum.midpoint.prism.PrismSerializer; + +public class MidpointJsonHttpMessageConverter extends MidpointAbstractHttpMessageConverter { + + public static final MediaType[] MEDIA_TYPES = { + MediaType.APPLICATION_JSON + }; + + protected MidpointJsonHttpMessageConverter( + PrismContext prismContext, LocalizationService localizationService) { + super(prismContext, localizationService, MEDIA_TYPES); + } + + @Override + protected PrismSerializer getSerializer() { + return prismContext.jsonSerializer(); + } + + @Override + protected PrismParser getParser(InputStream entityStream) { + return prismContext.parserFor(entityStream).json(); + } +} diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointXmlHttpMessageConverter.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointXmlHttpMessageConverter.java new file mode 100644 index 00000000000..885ccda9d52 --- /dev/null +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointXmlHttpMessageConverter.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.rest.impl; + +import java.io.InputStream; + +import org.springframework.http.MediaType; + +import com.evolveum.midpoint.common.LocalizationService; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismParser; +import com.evolveum.midpoint.prism.PrismSerializer; + +public class MidpointXmlHttpMessageConverter extends MidpointAbstractHttpMessageConverter { + + public static final MediaType[] MEDIA_TYPES = { + MediaType.APPLICATION_XML, MediaType.TEXT_XML + }; + + protected MidpointXmlHttpMessageConverter( + PrismContext prismContext, LocalizationService localizationService) { + super(prismContext, localizationService, MEDIA_TYPES); + } + + @Override + protected PrismSerializer getSerializer() { + return prismContext.xmlSerializer(); + } + + @Override + protected PrismParser getParser(InputStream entityStream) { + return prismContext.parserFor(entityStream).xml(); + } +} diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointYamlHttpMessageConverter.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointYamlHttpMessageConverter.java new file mode 100644 index 00000000000..9c304b0e0fb --- /dev/null +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/MidpointYamlHttpMessageConverter.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010-2020 Evolveum and contributors + * + * This work is dual-licensed under the Apache License 2.0 + * and European Union Public License. See LICENSE file for details. + */ + +package com.evolveum.midpoint.rest.impl; + +import java.io.InputStream; + +import org.springframework.http.MediaType; + +import com.evolveum.midpoint.common.LocalizationService; +import com.evolveum.midpoint.prism.PrismContext; +import com.evolveum.midpoint.prism.PrismParser; +import com.evolveum.midpoint.prism.PrismSerializer; + +public class MidpointYamlHttpMessageConverter extends MidpointAbstractHttpMessageConverter { + + public static final MediaType[] MEDIA_TYPES = { + MediaType.valueOf("application/yaml"), MediaType.valueOf("text/yaml"), + MediaType.valueOf("application/*-yaml"), MediaType.valueOf("text/*-yaml"), + MediaType.valueOf("application/*.yaml"), MediaType.valueOf("text/*.yaml") + }; + + protected MidpointYamlHttpMessageConverter( + PrismContext prismContext, LocalizationService localizationService) { + super(prismContext, localizationService, MEDIA_TYPES); + } + + @Override + protected PrismSerializer getSerializer() { + return prismContext.yamlSerializer(); + } + + @Override + protected PrismParser getParser(InputStream entityStream) { + return prismContext.parserFor(entityStream).yaml(); + } +} diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java index 3d5f7a477c9..6f08fa831c1 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/ModelRestController.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -28,7 +27,7 @@ import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; @RestController -@RequestMapping("/rest2") +@RequestMapping(value = "/rest2") //, produces = "*/*", consumes = "*/*") public class ModelRestController { private static final Trace LOGGER = TraceManager.getTrace(ModelRestService.class); @@ -37,13 +36,7 @@ public class ModelRestController { @Autowired private ModelCrudService model; @Autowired private SecurityHelper securityHelper; - @GetMapping( - value = "/self", - produces = { - MediaType.APPLICATION_XML_VALUE, - MediaType.APPLICATION_JSON_UTF8_VALUE, - RestServiceUtil.APPLICATION_YAML - }) + @GetMapping("/self") public ResponseEntity getSelf(HttpServletRequest request) { //@Context MessageContext mc){ TODO: do we need it in init request in new era? LOGGER.debug("model rest service for get operation start"); diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java index 31d75da9e95..fe82795c516 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestApiIndex.java @@ -1,5 +1,9 @@ package com.evolveum.midpoint.rest.impl; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -10,10 +14,6 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - /** * Support for simple index page with REST API endpoints (HTML and JSON). */ @@ -21,18 +21,20 @@ @RequestMapping("/rest2") public class RestApiIndex { - private final RequestMappingHandlerMapping handlerMapping; private final List uiRestInfo; public RestApiIndex(RequestMappingHandlerMapping handlerMapping) { - this.handlerMapping = handlerMapping; - uiRestInfo = operationInfoStream() - .filter(info -> info.handler.getBeanType() - // TODO getPackageName on Java 11 - .getPackage().getName().startsWith("com.evolveum.midpoint.rest.")) + uiRestInfo = operationInfoStream(handlerMapping) + .filter(info -> info.handler.getBeanType().getName() + .startsWith("com.evolveum.midpoint.rest.")) .collect(Collectors.toList()); } + private Stream operationInfoStream(RequestMappingHandlerMapping handlerMapping) { + return handlerMapping.getHandlerMethods().entrySet().stream() + .map(entry -> new OperationInfo(entry.getKey(), entry.getValue())); + } + @GetMapping() public List index() { return uiRestInfo.stream() @@ -41,7 +43,7 @@ public List index() { .collect(Collectors.toList()); } - @GetMapping(value = "/x", produces = "text/html") + @GetMapping(produces = "text/html") public String indexHtml() { StringBuilder html = new StringBuilder("" + "REST-ish API" @@ -63,11 +65,6 @@ public String indexHtml() { .toString(); } - private Stream operationInfoStream() { - return handlerMapping.getHandlerMethods().entrySet().stream() - .map(entry -> new OperationInfo(entry.getKey(), entry.getValue())); - } - private static class OperationInfo { final RequestMappingInfo mappingInfo; final HandlerMethod handler; diff --git a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java index abba51940ab..9d3a537013f 100644 --- a/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java +++ b/model/rest-impl/src/main/java/com/evolveum/midpoint/rest/impl/RestConfig.java @@ -6,63 +6,51 @@ */ package com.evolveum.midpoint.rest.impl; -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import com.evolveum.midpoint.prism.PrismObject; -import com.evolveum.midpoint.prism.PrismSerializer; -import com.evolveum.midpoint.prism.SerializationOptions; -import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.common.LocalizationService; +import com.evolveum.midpoint.prism.PrismContext; /** * Spring configuration for MVC-based REST service. TODO: experimental as of early 2020 - * This prepares needed (de)serializers and also drives the package scan for REST controllers. + * This prepares needed XML/JSON/YAML message converters. + * It also drives the package scan for REST controllers under this package. */ +@Configuration @ComponentScan -@EnableWebMvc -public class RestConfig extends WebMvcConfigurationSupport { +public class RestConfig + implements WebMvcConfigurer { - @Bean - public MappingJackson2HttpMessageConverter myConverter(ObjectMapper objectMapper) { - return new MappingJackson2HttpMessageConverter(objectMapper); + // TODO probably @Override addArgumentResolvers for @Converter processing? + + @Override + public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { + for (MediaType mediaType : MidpointYamlHttpMessageConverter.MEDIA_TYPES) { + configurer.mediaType(mediaType.getSubtype(), mediaType); + } + configurer.defaultContentType(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML); } @Bean - public ObjectMapper jsonObjectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - - SimpleModule module = new SimpleModule(); - module.addSerializer(PrismObject.class, prismObjectJsonSerializer()); - objectMapper.registerModule(module); + public MidpointYamlHttpMessageConverter yamlConverter( + PrismContext prismContext, LocalizationService localizationService) { + return new MidpointYamlHttpMessageConverter(prismContext, localizationService); + } - return objectMapper; + @Bean + public MidpointXmlHttpMessageConverter xmlConverter( + PrismContext prismContext, LocalizationService localizationService) { + return new MidpointXmlHttpMessageConverter(prismContext, localizationService); } - @SuppressWarnings("rawtypes") // no , otherwise the line module.addSerializer doesn't compile - private JsonSerializer prismObjectJsonSerializer() { - return new JsonSerializer() { - @Override - public void serialize(PrismObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException { - PrismSerializer prismSerializer = value.getPrismContext().jsonSerializer() - .options(SerializationOptions.createSerializeReferenceNames()); - try { - // TODO: this is just one instance of from MidpointAbstractProvider#writeTo - how much more do we need? - String rawJson = prismSerializer.serialize(value); - gen.writeRaw(rawJson); - } catch (SchemaException e) { - throw new IOException("JSON serialization problems", e); - } - } - }; + @Bean + public MidpointJsonHttpMessageConverter jsonConverter( + PrismContext prismContext, LocalizationService localizationService) { + return new MidpointJsonHttpMessageConverter(prismContext, localizationService); } } From 7f1c9cba1727b59ca6b408f9da462ca2485c7422 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 13 May 2020 09:45:45 +0200 Subject: [PATCH 34/41] schema-pure-jaxb POM: added exclude api-types-3.xsd for Win+JDK8 --- infra/schema-pure-jaxb/pom.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/infra/schema-pure-jaxb/pom.xml b/infra/schema-pure-jaxb/pom.xml index fe63f576934..0b5dcfc6ba6 100644 --- a/infra/schema-pure-jaxb/pom.xml +++ b/infra/schema-pure-jaxb/pom.xml @@ -168,12 +168,13 @@ public.annotation-3\.xsd query-3\.xsd - \\types-3\.xsd - /types-3\.xsd + \\types-3\.xsd + /types-3\.xsd common-.*\.xsd /scripting/.* \\scripting\\.* resource-schema-3\.xsd + api-types-3\.xsd From 4a9f43994c29a8363c7594d78f152ac18de203fe Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 13 May 2020 09:48:04 +0200 Subject: [PATCH 35/41] {Unassign,Assign}Executor.java: added outer class name for JDK8 Funny enough with JDK 11 we can extend nested class from superclass's nested class without using superclass name. In JDK 8 it fails. --- .../midpoint/model/impl/scripting/actions/AssignExecutor.java | 2 +- .../midpoint/model/impl/scripting/actions/UnassignExecutor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java index c88d5c1af1f..11a9f9de440 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/AssignExecutor.java @@ -52,7 +52,7 @@ public class AssignExecutor extends AssignmentOperationsExecutor targetRefs = new ArrayList<>(); private final List constructions = new ArrayList<>(); } diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java index f2baba330a5..e2fe55519dd 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/scripting/actions/UnassignExecutor.java @@ -46,7 +46,7 @@ public class UnassignExecutor extends AssignmentOperationsExecutor dynamicRoleRefs = new ArrayList<>(); private final Collection dynamicResourceRefs = new ArrayList<>(); From a0cee55d5c4295c3711a9fdb55e57e24614a8de7 Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Wed, 13 May 2020 09:57:40 +0200 Subject: [PATCH 36/41] more wrapper factories cleanup. mainly get rid of copy&paste code and using polymorphism instead. --- .../wrapper/ItemWrapperFactoryImpl.java | 20 +++++++------- ...oggingConfigurationWrapperFactoryImpl.java | 8 ++---- .../PrismObjectWrapperFactoryImpl.java | 11 ++++---- .../PrismPropertyWrapperFactoryImpl.java | 21 +++++++-------- ...rofilingClassLoggerWrapperFactoryImpl.java | 27 ++++++------------- .../ShadowAssociationWrapperFactoryImpl.java | 2 +- .../wrapper/TaskHandlerWrapperFactory.java | 1 - 7 files changed, 36 insertions(+), 54 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java index c00f2fcfe72..6a14ace64ef 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java @@ -43,7 +43,7 @@ * @author katka * */ -public abstract class ItemWrapperFactoryImpl implements ItemWrapperFactory { +public abstract class ItemWrapperFactoryImpl implements ItemWrapperFactory { private static final Trace LOGGER = TraceManager.getTrace(ItemWrapperFactoryImpl.class); @@ -53,6 +53,7 @@ public abstract class ItemWrapperFactoryImpl parent, ItemDefinition def, WrapperContext context) throws SchemaException { ItemName name = def.getItemName(); @@ -69,11 +70,10 @@ public IW createWrapper(PrismContainerValueWrapper parent, ItemDefinition childItem = (I) parent.getNewValue().findOrCreateItem(name); } - IW wrapper = createWrapper(parent, childItem, status, context); - return wrapper; + return createWrapper(parent, childItem, status, context); } - private ItemStatus getStatus(I childItem) { + private ItemStatus getStatus(I childItem) { if (childItem == null) { return ItemStatus.ADDED; } @@ -84,9 +84,10 @@ private ItemStatus getStatus(I childItem) { public abstract void registerWrapperPanel(IW wrapper); + @SuppressWarnings("unchecked") public IW createWrapper(PrismContainerValueWrapper parent, Item childItem, ItemStatus status, WrapperContext context) throws SchemaException { ItemDefinition def = childItem.getDefinition(); - if (skipCreateWrapper(def, status, context, childItem == null || CollectionUtils.isEmpty(childItem.getValues()))) { + if (skipCreateWrapper(def, status, context, CollectionUtils.isEmpty(childItem.getValues()))) { LOGGER.trace("Skipping creating wrapper for non-existent item. It is not supported for {}", def); if (parent != null && parent.getNewValue() != null) { parent.getNewValue().remove(childItem); @@ -164,11 +165,12 @@ protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, Wra protected abstract void setupWrapper(IW wrapper); - protected > List createValuesWrapper(IW itemWrapper, I item, WrapperContext context) throws SchemaException { + protected List createValuesWrapper(IW itemWrapper, I item, WrapperContext context) throws SchemaException { List pvWrappers = new ArrayList<>(); + List values = item.getValues(); //TODO : prismContainer.isEmpty() interates and check is all prismcontainervalues are empty.. isn't it confusing? - if (item.isEmpty() && item.getValues().isEmpty()) { + if (item.isEmpty() && values.isEmpty()) { if (shouldCreateEmptyValue(item, context)) { PV prismValue = createNewValue(item); VW valueWrapper = createValueWrapper(itemWrapper, prismValue, ValueStatus.ADDED, context); @@ -177,7 +179,7 @@ protected > List createValuesWrapper(IW itemWra return pvWrappers; } - for (PV pcv : (List)item.getValues()) { + for (PV pcv : values) { if(canCreateValueWrapper(pcv)){ VW valueWrapper = createValueWrapper(itemWrapper, pcv, ValueStatus.NOT_CHANGED, context); pvWrappers.add(valueWrapper); @@ -193,7 +195,7 @@ private boolean determineReadOnly(IW itemWrapper, WrapperContext context) { Boolean readOnly = context.getReadOnly(); if (readOnly != null) { LOGGER.trace("Setting {} as readonly because context said so.", itemWrapper); - return readOnly.booleanValue(); + return readOnly; } ItemStatus objectStatus = context.getObjectStatus(); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java index ee6ca0e74d3..af4f121f79b 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/LoggingConfigurationWrapperFactoryImpl.java @@ -6,21 +6,17 @@ */ package com.evolveum.midpoint.gui.impl.factory.wrapper; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import javax.annotation.PostConstruct; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.ItemStatus; import com.evolveum.midpoint.gui.api.prism.wrapper.ItemWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; -import com.evolveum.midpoint.gui.api.registry.GuiComponentRegistry; -import com.evolveum.midpoint.gui.impl.prism.panel.PrismContainerPanel; import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerWrapper; import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismContainerValueWrapperImpl; import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismContainerWrapperImpl; import com.evolveum.midpoint.prism.*; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java index b79a0a41153..0a66b7a906f 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismObjectWrapperFactoryImpl.java @@ -47,8 +47,6 @@ public class PrismObjectWrapperFactoryImpl extends PrismCo private static final QName VIRTUAL_CONTAINER_COMPLEX_TYPE = new QName("VirtualContainerType"); private static final QName VIRTUAL_CONTAINER = new QName("virtualContainer"); - @Autowired protected ModelInteractionService modelInteractionService; - public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStatus status, WrapperContext context) throws SchemaException { try { @@ -62,7 +60,7 @@ public PrismObjectWrapper createObjectWrapper(PrismObject object, ItemStat } context.setObject(object); - Collection virtualContainers = modelInteractionService.determineVirtualContainers(object, context.getTask(), context.getResult()); + Collection virtualContainers = getModelInteractionService().determineVirtualContainers(object, context.getTask(), context.getResult()); context.setVirtualContainers(virtualContainers); PrismObjectWrapper objectWrapper = createObjectWrapper(object, status); @@ -91,7 +89,7 @@ public void updateWrapper(PrismObjectWrapper wrapper, WrapperContext context) } context.setObject(wrapper.getObject()); - Collection virtualContainers = modelInteractionService.determineVirtualContainers(wrapper.getObject(), context.getTask(), context.getResult()); + Collection virtualContainers = getModelInteractionService().determineVirtualContainers(wrapper.getObject(), context.getTask(), context.getResult()); context.setVirtualContainers(virtualContainers); context.setShowEmpty(ItemStatus.ADDED == wrapper.getStatus()); wrapper.setExpanded(true); @@ -137,7 +135,7 @@ public PrismContainerValueWrapper createValueWrapper(PrismContainerWrapper def.setDisplayName(WebComponentUtil.getOrigStringFromPoly(display.getLabel())); def.setDynamic(true); - ItemWrapperFactory factory = getRegistry().findWrapperFactory(def); + ItemWrapperFactory factory = getRegistry().findWrapperFactory(def); if (factory == null) { LOGGER.warn("Cannot find factory for {}. Skipping wrapper creation.", def); continue; @@ -171,10 +169,11 @@ protected void applySecurityConstraints(PrismObject object, WrapperContext co OperationResult result = context.getResult(); try { - PrismObjectDefinition objectDef = modelInteractionService.getEditObjectDefinition(object, phase, task, result); + PrismObjectDefinition objectDef = getModelInteractionService().getEditObjectDefinition(object, phase, task, result); object.applyDefinition(objectDef, true); } catch (SchemaException | ConfigurationException | ObjectNotFoundException | ExpressionEvaluationException | CommunicationException | SecurityViolationException e) { + LOGGER.error("Exception while applying security constraints: {}", e.getMessage(), e); throw e; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java index bd7c1941310..506b4c6e7a0 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/PrismPropertyWrapperFactoryImpl.java @@ -9,26 +9,23 @@ import java.util.Collection; import javax.annotation.PostConstruct; -import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; -import com.evolveum.midpoint.gui.impl.prism.panel.PrismPropertyPanel; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyValueWrapper; -import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; -import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyWrapperImpl; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext; import com.evolveum.midpoint.gui.api.prism.ItemStatus; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper; +import com.evolveum.midpoint.gui.api.prism.wrapper.PrismPropertyWrapper; import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils; -import com.evolveum.midpoint.model.api.ModelService; +import com.evolveum.midpoint.gui.impl.prism.panel.PrismPropertyPanel; +import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyValueWrapper; +import com.evolveum.midpoint.gui.impl.prism.wrapper.PrismPropertyWrapperImpl; import com.evolveum.midpoint.prism.*; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SchemaHelper; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; -import com.evolveum.midpoint.task.api.TaskManager; import com.evolveum.midpoint.util.exception.*; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; @@ -84,6 +81,7 @@ protected LookupTableType getPredefinedValues(PrismProperty item, WrapperCont if (valueEnumerationRef == null) { return null; } + //TODO: task and result from context Task task = wrapperContext.getTask(); OperationResult result = wrapperContext.getResult().createSubresult(OPERATION_LOAD_LOOKUP_TABLE); Collection> options = WebModelServiceUtils @@ -106,9 +104,8 @@ protected LookupTableType getPredefinedValues(PrismProperty item, WrapperCont @Override public PrismPropertyValueWrapper createValueWrapper(PrismPropertyWrapper parent, PrismPropertyValue value, - ValueStatus status, WrapperContext context) throws SchemaException { - PrismPropertyValueWrapper valueWrapper = new PrismPropertyValueWrapper<>(parent, value, status); - return valueWrapper; + ValueStatus status, WrapperContext context) { + return new PrismPropertyValueWrapper<>(parent, value, status); } @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java index 7b38f3ebe3c..9d77634f433 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java @@ -69,26 +69,15 @@ public void registerWrapperPanel(PrismContainerWrapper>> List> createValuesWrapper( - PrismContainerWrapper itemWrapper, PrismContainer item, WrapperContext context) - throws SchemaException { - List> pvWrappers = new ArrayList<>(); - - for (PrismContainerValue pcv : item.getValues()) { - if(canCreateValueWrapper(pcv)) { - PrismContainerValueWrapper valueWrapper = createValueWrapper(itemWrapper, pcv, ValueStatus.NOT_CHANGED, context); - pvWrappers.add(valueWrapper); - } - } - - if (pvWrappers.isEmpty()) { - PrismContainerValue prismValue = createNewValue(item); - PrismContainerValueWrapper valueWrapper = createValueWrapper(itemWrapper, prismValue, ValueStatus.ADDED, context); - valueWrapper.getRealValue().setPackage(LOGGER_PROFILING); - pvWrappers.add(valueWrapper); - } + protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { + return true; + } - return pvWrappers; + @Override + protected PrismContainerValue createNewValue(PrismContainer item) { + PrismContainerValue profilingLogger = super.createNewValue(item); + profilingLogger.asContainerable().setPackage(LOGGER_PROFILING); + return profilingLogger; } @Override diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java index 34a4c872b0d..6162fe332c4 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ShadowAssociationWrapperFactoryImpl.java @@ -202,7 +202,7 @@ private ShadowAssociationWrapperImpl createShadowAssociationWrapper(PrismContain } @Override - protected >> List> createValuesWrapper(PrismContainerWrapper itemWrapper, PrismContainer item, WrapperContext context) throws SchemaException { + protected List> createValuesWrapper(PrismContainerWrapper itemWrapper, PrismContainer item, WrapperContext context) throws SchemaException { if (!(itemWrapper instanceof ShadowAssociationWrapperImpl)) { return super.createValuesWrapper(itemWrapper, item, context); } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java index 91b38bfd209..809c0fb29b7 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/TaskHandlerWrapperFactory.java @@ -47,7 +47,6 @@ protected LookupTableType getPredefinedValues(PrismProperty item, Wrappe Collection handlers; if (assignmentTypes.isEmpty()) { - // TODO all handlers handlers = getTaskManager().getAllHandlerUris(true); } else if (assignmentTypes.size() == 1) { AssignmentType archetypeAssignment = assignmentTypes.iterator().next(); From 10ac2e749fefcbfc295b7306b1923da657c948e4 Mon Sep 17 00:00:00 2001 From: Richard Richter Date: Wed, 13 May 2020 10:06:32 +0200 Subject: [PATCH 37/41] TestRunAs: added time tolerance to test310BarbossaSetOrganizationRunAs --- .../com/evolveum/midpoint/testing/longtest/TestRunAs.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/testing/longtest/src/test/java/com/evolveum/midpoint/testing/longtest/TestRunAs.java b/testing/longtest/src/test/java/com/evolveum/midpoint/testing/longtest/TestRunAs.java index 8c896f14936..b93bf6f7987 100644 --- a/testing/longtest/src/test/java/com/evolveum/midpoint/testing/longtest/TestRunAs.java +++ b/testing/longtest/src/test/java/com/evolveum/midpoint/testing/longtest/TestRunAs.java @@ -46,7 +46,6 @@ public class TestRunAs extends AbstractLongTest { private static final String RESOURCE_DUMMY_OID = "2f454e92-c9e8-11e7-8f60-17bc95e695f8"; protected static final File USER_ROBOT_FILE = new File(TEST_DIR, "user-robot.xml"); - protected static final String USER_ROBOT_OID = "20b4d7c0-c9e9-11e7-887c-7fe1dc65a3ed"; protected static final String USER_ROBOT_USERNAME = "robot"; protected static final File USER_TEMPLATE_PLAIN_FILE = new File(TEST_DIR, "user-template-plain.xml"); @@ -60,6 +59,9 @@ public class TestRunAs extends AbstractLongTest { private static final int NUM_ORG_MAPPINGS = 10; private static final int WARM_UP_ROUNDS = 30; + // fixed time added to the baseline before any % comparison as random stuff like GC can happen + private static final long BASELINE_RUN_TIME_TOLERANCE = 40; + private long baselineRunTime; private long baselineRepoReadCountIncrement; @@ -207,7 +209,7 @@ public void test210BarbossaSetOrganizationPlain() throws Exception { long readCountIncremenet = getCounterIncrement(InternalCounters.REPOSITORY_READ_COUNT); display("Run time " + (endMillis - starMillis) + "ms, repo read count increment " + readCountIncremenet); - baselineRunTime = endMillis - starMillis; + baselineRunTime = endMillis - starMillis + BASELINE_RUN_TIME_TOLERANCE; baselineRepoReadCountIncrement = readCountIncremenet; PrismObject userAfter = getUser(USER_BARBOSSA_OID); @@ -283,7 +285,7 @@ public void test310BarbossaSetOrganizationRunAs() throws Exception { if (readCountIncrease > 2) { fail("High increase over repo read count baseline: " + readCountIncrease + " (expected: at most 2)"); } - if (percentRuntimeIncrease > 20) { + if (percentRuntimeIncrease > 40) { fail("Too high run time increase over baseline: " + percentRuntimeIncrease + "% " + baselineRunTime + "ms -> " + runTimeMillis + "ms"); } From 71c2ca2b5f5ae31cde07fa3f067c726c642533cd Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Wed, 13 May 2020 10:41:51 +0200 Subject: [PATCH 38/41] buttons visibility for column headers. --- .../data/column/PrismPropertyWrapperColumn.java | 13 ++++++++++++- .../data/column/PrismReferenceWrapperColumn.java | 12 +++++++++++- .../panel/PrismContainerColumnHeaderPanel.java | 10 ++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java index de59eac5a4f..392b0e1f742 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java @@ -49,7 +49,18 @@ public IModel getDataModel(IModel> rowModel) { @Override protected Component createHeader(String componentId, IModel> mainModel) { - return new PrismPropertyHeaderPanel<>(componentId, new PrismPropertyWrapperHeaderModel(mainModel, itemName, pageBase)); + return new PrismPropertyHeaderPanel<>(componentId, new PrismPropertyWrapperHeaderModel(mainModel, itemName, pageBase)) { + + @Override + protected boolean isAddButtonVisible() { + return false; + } + + @Override + protected boolean isButtonEnabled() { + return false; + } + }; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java index 17dcc36d4e5..5a9e74fdb6e 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java @@ -51,7 +51,17 @@ public IModel getDataModel(IModel> rowModel) { @Override protected Component createHeader(String componentId, IModel> mainModel) { - return new PrismReferenceHeaderPanel<>(componentId, new PrismReferenceWrapperHeaderModel(mainModel, itemName, pageBase)); + return new PrismReferenceHeaderPanel<>(componentId, new PrismReferenceWrapperHeaderModel(mainModel, itemName, pageBase)) { + @Override + protected boolean isAddButtonVisible() { + return false; + } + + @Override + protected boolean isButtonEnabled() { + return false; + } + }; } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerColumnHeaderPanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerColumnHeaderPanel.java index 85f8054e328..576ae33ed33 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerColumnHeaderPanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerColumnHeaderPanel.java @@ -46,4 +46,14 @@ protected void refreshPanel(AjaxRequestTarget target) { protected PrismContainerValue createNewValue(PrismContainerWrapper parent) { return null; } + + @Override + protected boolean isAddButtonVisible() { + return false; + } + + @Override + protected boolean isButtonEnabled() { + return false; + } } From 54a0bbf6aff917482691e5bffd6c69c8c6d59bea Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Wed, 13 May 2020 11:57:42 +0200 Subject: [PATCH 39/41] small fixes (java8 compilation issues, visibility for remove button - prismObjectValue) --- .../impl/component/data/column/PrismPropertyWrapperColumn.java | 2 +- .../impl/component/data/column/PrismReferenceWrapperColumn.java | 2 +- .../midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java index 392b0e1f742..72d822fd1f5 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismPropertyWrapperColumn.java @@ -49,7 +49,7 @@ public IModel getDataModel(IModel> rowModel) { @Override protected Component createHeader(String componentId, IModel> mainModel) { - return new PrismPropertyHeaderPanel<>(componentId, new PrismPropertyWrapperHeaderModel(mainModel, itemName, pageBase)) { + return new PrismPropertyHeaderPanel(componentId, new PrismPropertyWrapperHeaderModel(mainModel, itemName, pageBase)) { @Override protected boolean isAddButtonVisible() { diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java index 5a9e74fdb6e..a5694982a59 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/component/data/column/PrismReferenceWrapperColumn.java @@ -51,7 +51,7 @@ public IModel getDataModel(IModel> rowModel) { @Override protected Component createHeader(String componentId, IModel> mainModel) { - return new PrismReferenceHeaderPanel<>(componentId, new PrismReferenceWrapperHeaderModel(mainModel, itemName, pageBase)) { + return new PrismReferenceHeaderPanel(componentId, new PrismReferenceWrapperHeaderModel(mainModel, itemName, pageBase)) { @Override protected boolean isAddButtonVisible() { return false; diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java index 35e62f3f838..9e9791bf2dc 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/prism/panel/PrismContainerValuePanel.java @@ -533,6 +533,6 @@ protected void removeValue(CVW valueToRemove, AjaxRequestTarget target) throws S @Override protected boolean isRemoveButtonVisible() { - return super.isRemoveButtonVisible() && getModelObject().isExpanded(); + return super.isRemoveButtonVisible() && getModelObject().isExpanded() && !(getModelObject() instanceof PrismObjectValueWrapper); } } From 72c9f88f3f2d65172d4fd6d97e57574b36c56be5 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 13 May 2020 12:20:06 +0200 Subject: [PATCH 40/41] Reduce heap usage in cache.searchObjectsIterative Clears objects-to-be-cached list after a limit of 100 objects is reached; in a way similar to 7d20d98d51341b248af1102748395d7f75d47eb5. This should reduce heap usage, although in rather minor way. --- .../cache/handlers/CachingResultHandler.java | 1 + .../repo/cache/TestRepositoryCache.java | 120 ++++++++++++++++-- 2 files changed, 107 insertions(+), 14 deletions(-) diff --git a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/handlers/CachingResultHandler.java b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/handlers/CachingResultHandler.java index ef49e60cf79..0c24e01f509 100644 --- a/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/handlers/CachingResultHandler.java +++ b/repo/repo-cache/src/main/java/com/evolveum/midpoint/repo/cache/handlers/CachingResultHandler.java @@ -65,6 +65,7 @@ public boolean handle(PrismObject object, OperationResult parentResult) { } else { CachePerformanceCollector.INSTANCE.registerOverSizedQuery(type); overflown = true; + objects.clear(); } } diff --git a/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/TestRepositoryCache.java b/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/TestRepositoryCache.java index 6e5a8a973b7..6e112bc2241 100644 --- a/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/TestRepositoryCache.java +++ b/repo/repo-cache/src/test/java/com/evolveum/midpoint/repo/cache/TestRepositoryCache.java @@ -12,10 +12,12 @@ import static com.evolveum.midpoint.prism.util.PrismTestUtil.displayCollection; import static com.evolveum.midpoint.prism.util.PrismTestUtil.getPrismContext; -import java.io.IOException; +import static org.testng.AssertJUnit.fail; + import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import javax.annotation.PostConstruct; import com.evolveum.midpoint.prism.PrismContext; @@ -34,12 +36,12 @@ import com.evolveum.midpoint.util.caching.CachePerformanceCollector; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; -import org.xml.sax.SAXException; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.query.ObjectQuery; @@ -73,46 +75,51 @@ public class TestRepositoryCache extends AbstractSpringTest implements InfraTest @Autowired GlobalObjectCache globalObjectCache; @Autowired GlobalVersionCache globalVersionCache; @Autowired GlobalQueryCache globalQueryCache; + @Autowired PrismContext prismContext; + + @SuppressWarnings("unused") // used when heap dumps are uncommented + private final long identifier = System.currentTimeMillis(); @BeforeSuite - public void setup() throws SchemaException, SAXException, IOException { + public void setup() { PrettyPrinter.setDefaultNamespacePrefix(MidPointConstants.NS_MIDPOINT_PUBLIC_PREFIX); - PrismTestUtil.resetPrismContext(MidPointPrismContextFactory.FACTORY); } @PostConstruct public void initialize() throws SchemaException { + PrismTestUtil.setPrismContext(prismContext); + OperationResult initResult = new OperationResult(CLASS_DOT + "setup"); repositoryCache.postInit(initResult); } @Test - public void testGetUser() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public void test100GetUser() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { testGetUncachedObject(UserType.class); } @Test - public void testGetSystemConfiguration() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public void test110GetSystemConfiguration() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { testGetCachedObject(SystemConfigurationType.class); } @Test - public void testSearchUsers() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public void test200SearchUsers() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { testSearchUncachedObjects(UserType.class); } @Test - public void testSearchArchetypes() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public void test210SearchArchetypes() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { testSearchCachedObjects(ArchetypeType.class); } @Test - public void testSearchUsersIterative() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public void test220SearchUsersIterative() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { testSearchUncachedObjectsIterative(UserType.class); } @Test - public void testSearchArchetypesIterative() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public void test230SearchArchetypesIterative() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { testSearchCachedObjectsIterative(ArchetypeType.class); } @@ -120,7 +127,7 @@ public void testSearchArchetypesIterative() throws SchemaException, ObjectNotFou * MID-6250 */ @Test - public void testModifyInIterativeSearch() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { + public void test300ModifyInIterativeSearch() throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException { given(); PrismContext prismContext = getPrismContext(); OperationResult result = createOperationResult(); @@ -172,7 +179,7 @@ public void testModifyInIterativeSearch() throws SchemaException, ObjectNotFound * MID-6250 */ @Test - public void testAddInIterativeSearch() throws SchemaException, ObjectAlreadyExistsException { + public void test310AddInIterativeSearch() throws SchemaException, ObjectAlreadyExistsException { given(); PrismContext prismContext = getPrismContext(); OperationResult result = createOperationResult(); @@ -223,7 +230,7 @@ public void testAddInIterativeSearch() throws SchemaException, ObjectAlreadyExis * MID-6250 */ @Test - public void testSearchObjectsIterativeSlow() throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { + public void test320SearchObjectsIterativeSlow() throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { OperationResult result = createOperationResult(); deleteExistingObjects(ArchetypeType.class, result); @@ -268,7 +275,7 @@ public void testSearchObjectsIterativeSlow() throws ObjectAlreadyExistsException } @Test - public void testSearchObjectsOverSize() throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { + public void test330SearchObjectsOverSize() throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { OperationResult result = createOperationResult(); deleteExistingObjects(ArchetypeType.class, result); @@ -291,6 +298,75 @@ public void testSearchObjectsOverSize() throws ObjectAlreadyExistsException, Sch assertThat(data.overSizedQueries.get()).as("over-sized counter").isEqualTo(2); // search + searchIterative } + // Must be executed last, because naive deletion such large number of archetypes fails on OOM + @Test + public void test900HeapUsage() throws Exception { + OperationResult result = new OperationResult("testHeapUsage"); + + int size = 2_000_000; + int count = 300; + + // 50 is the default "step" in paged iterative search, so we can expect we always have 50 objects in memory + // And "times 2" is the safety margin. It might or might not be sufficient, as System.gc() is not guaranteed to + // really execute the garbage collection (only suggests JVM to do it). + long tolerance = (50 * size) * 2; + + showMemory("Initial"); + dumpHeap("initial"); + + deleteExistingObjects(ArchetypeType.class, result); + generateLargeObjects(ArchetypeType.class, size, count, result); + showMemory("After generation"); + dumpHeap("after-generation"); + + long usedBefore = getMemoryUsed(); + AtomicInteger seen = new AtomicInteger(); + AtomicLong usedInLastIteration = new AtomicLong(); + repositoryCache.searchObjectsIterative(ArchetypeType.class, null, + (object, parentResult) -> { + if (seen.incrementAndGet() % 100 == 0) { + showMemory("After seeing " + seen.get() + " objects"); + } + if (seen.get() == count) { + usedInLastIteration.set(getMemoryUsed()); + dumpHeap("last-iteration"); + } + return true; + }, null, true, result); + showMemory("Final"); + dumpHeap("final"); + + long difference = usedInLastIteration.get() - usedBefore; + display(String.format("Difference: %,d KB (tolerating %,d KB)", difference / 1024, tolerance / 1024)); + if (difference > tolerance) { + fail("Used too much memory during iterative search: " + difference / 1024 + " KB"); + } + } + + private void showMemory(String desc) { + long used = getMemoryUsed(); + display(String.format("%s: %,d used (%,d KB)%n", desc, used, used / 1024)); + } + + private long getMemoryUsed() { + System.gc(); + return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } + + @SuppressWarnings("unused") + private void dumpHeap(String desc) { + // Enable when needed +// try { +// HotSpotDiagnosticMXBean mxBean = ManagementFactory.newPlatformMXBeanProxy(ManagementFactory.getPlatformMBeanServer(), +// "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class); +// display("Generating heap dump: " + desc + "..."); +// mxBean.dumpHeap("heap-" + identifier + "-" + desc + ".hprof", true); +// display("Done"); +// } catch (IOException e) { +// throw new IllegalStateException(e); +// } + } + private void testGetUncachedObject(Class objectClass) throws ObjectAlreadyExistsException, SchemaException, ObjectNotFoundException { testGetObject(objectClass, false); } @@ -546,6 +622,22 @@ private Set> generateObjects(Class obje return objects; } + private void generateLargeObjects(Class objectClass, int size, int count, OperationResult result) + throws SchemaException, + ObjectAlreadyExistsException { + for (int i = 0; i < count; i++) { + PrismObject object = getPrismContext().createObject(objectClass); + object.asObjectable() + .name(PolyStringType.fromOrig("T:" + i)) + .description(StringUtils.repeat('#', size)); + repositoryCache.addObject(object, null, result); + + if ((i+1) % 100 == 0) { + showMemory("After generating " + (i+1) + " objects"); + } + } + } + private void deleteExistingObjects(Class objectClass, OperationResult result) throws SchemaException, ObjectNotFoundException { From 0595a5a98ca57f1957a6472a27a1b9e893531a38 Mon Sep 17 00:00:00 2001 From: Katarina Valalikova Date: Wed, 13 May 2020 15:42:49 +0200 Subject: [PATCH 41/41] fixing GUI test - delta creation for profiling logger. --- .../wrapper/ItemWrapperFactoryImpl.java | 9 ++++-- ...rofilingClassLoggerWrapperFactoryImpl.java | 31 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java index 6a14ace64ef..070a244387a 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ItemWrapperFactoryImpl.java @@ -168,9 +168,8 @@ protected boolean canCreateWrapper(ItemDefinition def, ItemStatus status, Wra protected List createValuesWrapper(IW itemWrapper, I item, WrapperContext context) throws SchemaException { List pvWrappers = new ArrayList<>(); - List values = item.getValues(); - //TODO : prismContainer.isEmpty() interates and check is all prismcontainervalues are empty.. isn't it confusing? - if (item.isEmpty() && values.isEmpty()) { + List values = getValues(item); + if (values.isEmpty()) { if (shouldCreateEmptyValue(item, context)) { PV prismValue = createNewValue(item); VW valueWrapper = createValueWrapper(itemWrapper, prismValue, ValueStatus.ADDED, context); @@ -190,6 +189,10 @@ protected List createValuesWrapper(IW itemWrapper, I item, WrapperContext co } + protected List getValues(I item) { + return item.getValues(); + } + private boolean determineReadOnly(IW itemWrapper, WrapperContext context) { Boolean readOnly = context.getReadOnly(); diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java index 9d77634f433..aefaea9f723 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/gui/impl/factory/wrapper/ProfilingClassLoggerWrapperFactoryImpl.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import javax.xml.namespace.QName; import org.springframework.stereotype.Component; @@ -40,21 +41,7 @@ public class ProfilingClassLoggerWrapperFactoryImpl extends PrismContainerWrappe public boolean match(ItemDefinition def) { return false; } - - - - @Override - protected boolean canCreateValueWrapper(PrismContainerValue value) { - if(value == null || value.getRealValue() == null) { - return false; - } - String loggerPackage = ((ClassLoggerConfigurationType)value.getRealValue()).getPackage(); - if(loggerPackage == null) { - return false; - } - return loggerPackage.equals(LOGGER_PROFILING); - } - + @Override protected PrismContainerWrapper createWrapperInternal(PrismContainerValueWrapper parent, PrismContainer childContainer, ItemStatus status, WrapperContext ctx) { @@ -68,6 +55,20 @@ public void registerWrapperPanel(PrismContainerWrapper> getValues(PrismContainer item) { + return item.getValues().stream().filter(value -> { + if(value == null || value.getRealValue() == null) { + return false; + } + String loggerPackage = ((ClassLoggerConfigurationType)value.getRealValue()).getPackage(); + if(loggerPackage == null) { + return false; + } + return loggerPackage.equals(LOGGER_PROFILING); + }).collect(Collectors.toList()); + } + @Override protected boolean shouldCreateEmptyValue(PrismContainer item, WrapperContext context) { return true;