<report>
<name>Collection report 1</name>
<reportEngine>collection</reportEngine>
<objectCollection>
<collection>
<collectionRef oid="---COLLECTION_OID---" type="ObjectCollectionType"/>
</collection>
</objectCollection>
</report>
Report Configuration
Since 4.4
This functionality is available since version 4.4.
|
MidPoint 4.4 and later
This feature is available only in midPoint 4.4 and later. For configuration before 4.4 please see Report Configuration before midPoint version 4.4.
|
Native reports are intended to be native reporting mechanism for midPoint. The aim is to use what midPoint provides without the need for Jasper framework. Jasper framework was removed in midPoint 4.4. It is recommended to use reports based on midPoint concepts, namely dashboards or objectCollection. Current implementation supports exporting reports to CSV and HTML file, other formats such as XSLX are planned to be added later.
One of the requirements for Native reports was supporting multithreading and bucketing. Since 4.4, multithreading is fully supported for all possible kinds of reported objects such as containers, objects and audit records. Bucketing is supported for objects and audit records, because currently bucketing of task don’t support containers.
Every report output is stored in export directory in midPoint.home
. It is possible to send generated reports via email using notifier handler.
Every report object consists of configuration of three basic parts:
-
behavior
- represents the direction of the report. There are two possible types ofbehavior
: export and import.Export behavior represents basic reporting mechanism, where the file with reported objects is generated. This behavior is default and was common also in versions before 4.4.
Import behavior can be used in situations, when the objects need to be imported to the midPoint from external file. Supported format is the CSV. Import behavior is experimental.
-
engine
- defines the report generation base. There are two supported options, reports based on object collections and reports based on dashboard. Only one can be defined at the same time. -
fileFormat
- represents format of the generated report file. Supported formats for now are CSV and HTML. Default format for the dashboard-based reports is HTML and for object collection based reports is CSV.
All configuration attributes of report:
Name | Type | Description |
---|---|---|
fileFormat |
FileFormatConfigurationType |
Define report output file format. |
dashboard |
DashboardReportEngineConfigurationType |
Configuration for dashboard-based reports. When defined, dashboard engine is used to generate reports. |
objectCollection |
ObjectCollectionReportEngineConfigurationType |
Configuration for object collection-based reports. When defined, object collection engine is used to generate report. |
defaultScriptConfiguration |
ScriptExpressionEvaluatorConfigurationType |
Default configuration for the scripts executed inside the report. |
postReportScript |
CommandLineScriptType |
Command-line script that will be executed after the report is complete and the output file is completely produced. Output filename will be passed to the script as the "file" argument. |
Export behaviour
For now, export to CSV and HTML is supported.
Configuration attributes:
Name | Type | Description |
---|---|---|
type |
FileFormatTypeType |
Report data type. Possible values: csv and html. |
csv |
CsvFileFormatType |
Configuration attribute for csv export. |
html |
HtmlFileFormatType |
Configuration attribute for html export. |
HTML
Configuration for HTML file format doesn’t contain any other attributes.
CSV
Configuration for CSV file format contains attributes:
Name | Type | Description | Default |
---|---|---|---|
multivalueDelimiter |
string |
Multi-value delimiter. |
, |
fieldDelimiter |
string |
Field delimiter. |
; |
escape |
string |
The escape character of the format. |
\ |
quote |
string |
Character for the quote. |
" |
quoteMode |
QuoteModeType |
Quote Mode for records. Possible values is all, allNonNull, minimal, nonNumeric and none. |
nonNumeric |
recordSeparator |
string |
Separator of line of record. |
\r\n |
trailingDelimiter |
boolean |
Define, whether to add a trailing delimiter. |
false |
trim |
boolean |
Define, whether to trim leading and trailing blanks. |
false |
createHeader |
boolean |
Create header in csv output file. |
true |
encoding |
string |
Encoding of csv file. |
utf-8 |
QuoteModeType
-
all
- Quotes all fields. -
allNonNull
- Quotes all non-null fields. -
minimal
- Quotes fields which contain special characters such as the field delimiter, quotes character or any of the characters in the line separator string. -
nonNumeric
- Quotes all non-numeric fields. -
none
- Never quotes fields. When the delimiter occurs in data, the printer prefixes it with the escape character. If the escape character is not set, format validation throws an exception.
Report engine
Report engine is defined with the configuring either objectCollection
or dashboard
element in report.
Collection report
Collection reports generate reports based on Object collection configuration. Collection reports support audit records, containers and objects, such as users, roles, organizations and so on. Basic part of configuration for Collection reports are query, columns, parameters, sub-reports and condition.
Name | Type | Description |
---|---|---|
collection |
CollectionRefSpecificationType |
Specification of an explicit or implicit object collection that will be used to select objects for report. |
view |
GuiObjectListViewType |
Specifies a view of an object collection that is reported. |
useOnlyReportView |
boolean |
Specifies that during report creation, only view defined in report will be used. No other views defined outside the report will merger or considered. |
condition |
ExpressionType |
Condition for the searched objects. Generated report will contain only objects satisfying the condition. Condition is used only for generated reports. Use wisely, performance might suffer. |
parameter |
SearchFilterParameterType |
Parameter used in filter expression. |
subreport |
SubreportParameterType |
Subreport with expression. |
Objects to be reported
Objects to be reported for object collection based reports are defined using collection
configuration property. There are three possibilities how the collection
can be defined:
-
using reference to the existing collection,
-
writing filter directly in collection attribute without reference to base collection,
-
combination of previous two options, and so writing filter directly in collection attribute and using reference to existing collection.
Following are the examples for what the collection definition might look like.
<report>
<name>Collection report 2</name>
<reportEngine>collection</reportEngine>
<objectCollection>
<collection>
<filter>
<all/>
</filter>
<baseCollectionRef>
<collectionRef oid="---COLLECTION_OID---" type="ObjectCollectionType"/>
</baseCollectionRef>
</collection>
</objectCollection>
</report>
If collection
contains reference to existing collection and custom filter, midPoint has two filters for report, one from report and one from base collection. MidPoint makes the conjunction with the filters. Let’s have an example where the first filter says that we want to see users with membership in Organization Evolveum. The second filter is for users with role End user. As the result we will see users that have memberships in organization Evolveum and role 'End user' at the same time.

Columns
Columns are defined using view
. Configuration for view
can be used at more places in midpoint, such as configuration in adminGuiConfiguration
. For reports, there might be a view defined in report and view defined in object collection. When the view is defined in both places, merging of these two definitions is performed.
For example, let’s have a view defined for report and also view defined for object collection used to generate the report. Report view contains definition for Name and Email column, while view in object collection contains definition for Given name and Family name columns. The report generated based on this configuration will contain columns for Name, Email, Given name and Family name.

Of course when we want to use only view in report it is possible by attribute useOnlyReportView
.
Defining columns can be skipped when report in meant for audit records or any of the midPoint objects (such as UserType
, RoleType
, ServiceType
,…). If no custom columns are defined for report, midPoint will use default (system defined) view for specific type of object. However, then the report is defined for containers, the columns definition cannot be omitted.
Parameters
There are situation when we want to run report with slightly different settings. In such cases, we don’t want to define the report for each case separately. Rather, we want to have one report definition and run the report with different parameters. Imagine that you need to report all users who have account on specific resources. In such a case, resource will be a parameter to the report, so we don’t need to prepare report definition for each resource. The resource parameter will be set before the report is run.
Usage of the parameter is very simple. We just use the name of the parameter in expression of query.

<report xmlns="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3"
xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3">
<name>Collection report 2</name>
<reportEngine>collection</reportEngine>
<objectCollection>
<collection>
<filter>
<q:ref>
<q:path>assignment/construction/resourceRef</q:path>
<expression>
<queryInterpretationOfNoValue>filterAll</queryInterpretationOfNoValue>
<script>
<objectVariableMode>prismReference</objectVariableMode>
<code>
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
if (!resource) {
return null;
}
ObjectReferenceType ort = new ObjectReferenceType();
ort.setOid(resource.getOid());
ort.setRelation(resource.getRelation());
ort.setType(resource.getTargetType());
return ort;
</code>
</script>
</expression>
</q:ref>
</filter>
</collection>
<view>
<type>UserType</type>
</view>
<parameter>
<name>resource</name>
<type>c:ObjectReferenceType</type>
<targetType>c:ResourceType</targetType>
<display>
<label>
<orig>resource</orig>
<translation>
<key>ObjectTypeGuiDescriptor.resource</key>
</translation>
</label>
</display>
</parameter>
</objectCollection>
</report>
We can use following attributes for parameter:
Name | Type | Description |
---|---|---|
name |
String |
Name of parameter. |
type |
QName |
Type of parameter value. |
targetType |
QName |
Type of target, when type of parameter value is ObjectReferenceType. |
allowedValuesLookupTable |
ObjectReferenceType |
Reference of Lookup Table which defines possible values of parameter. |
allowedValuesExpression |
ExpressionType |
Expression that determines allowed value. Expected List<DisplayableValue>. |
Subreports
Subreport is defined by expression and can be used in situations when we need to collect additional information for the processed object (row). To avoid performing expensive operations (such as search) for each column, there is a possibility to define it once per row and use it later in the column expression as in the example below.

In the example above, we have the report, where for each shadow (row) we want to search for the owner of the shadow.
Therefore, the subreport is defined with the expression to look for the shadow owner.
The result of the expression is stored to the property called user
and later used in the column expression to pull the desired information.
In this case, we need to get the e-mail address of the user.
The return from the expression in subreport is represented by the collection.
We can use following attributes for subreport:
Name | Type | Description |
---|---|---|
name |
String |
Name of subreport. |
type |
QName |
Type of parameter value. |
order |
Integer |
Order in which this entry is to be evaluated. (Related to other entries.) Smaller numbers go first. Entries with no order go last. |
Example of generated report
In the picture below we can see example of generated HTML report of all users in midPoint. Report contains columns for Name, Full Name, Administrative status, Roles, Organizations and Accounts of every user.

Dashboard Report
Dashboard engine generates reports by dashboard object. Dashboard object can be shown in GUI. Dashboard contains widgets, which showed actual state of midpoint. Documentation for dashboard configuration can be found in Dashboard configuration.
Dashboards support both currently supported file formats, but there is a difference. CSV contains only table of widget with basic message and status, unlike HTML, which as default showing table for widgets and one table for every widget with objects which meet the condition in widget. We can turn off this default behaviour via attribute showOnlyWidgetTable and show only table of widgets.
Following code shows basic configuration of dashboard report.
<report>
<name>System Status Dashboard report</name>
<reportEngine>dashboard</reportEngine>
<dashboard>
<dashboardRef oid="--OID OF DASHBOARD--" >
</dashboardRef>
<showOnlyWidgetsTable>false</showOnlyWidgetsTable>
</dashboard>
</report>
Configuration for dashboard report contains definition for view, view attribute in dashboard report is multivalue. We can configure view for every type of objects, which are showed via widgets. View defines columns of table in report.

For example in our example it is first line under screenshot. We can see views for types ResourceType
, AuditEventRecordType
and TaskType
.
The final view of widget is merged from three levels of view. View in report for type of object, view in dashboard for widget and from view in object collection which contains configuration for widget. Columns from views are merged in a same way as in Collection report. Also, we can use attribute useOnlyReportView
for using only view defined by report.
When we don’t use view, report will be contained default columns. In expression of column you can use variable 'object' which represent searched object or object defined in tag path. For more information about view configuration please see Views.
<presentation>
...
<view>
<column>
<name>nameColumn</name>
<c:path>name</c:path>
<display>
<label>Name</label>
</display>
</column>
<column>
<name>members</name>
<display>
<label>Members</label>
</display>
<previousColumn>nameColumn</previousColumn>
<export>
<expression>
<script>
<code>
import com.evolveum.midpoint.prism.query.*
import com.evolveum.midpoint.xml.ns._public.common.common_3.*
query = prismContext.queryFor(UserType.class).item(AssignmentHolderType.F_ROLE_MEMBERSHIP_REF).ref(object.getOid()).build();
objects = midpoint.searchObjects(UserType.class, query)
return objects.size();
</code>
</script>
</expression>
</export>
</column>
</view>
</presentation>
<report>
...
<dashboard>
...
<view>
<column>
<name>givenNameColumn</name>
<c:path>givenName</c:path>
<display>
<label>Given name</label>
</display>
</column>
<type>UserType</type>
</view>
<view>
<column>
<name>nameColumn</name>
<c:path>name</c:path>
<display>
<label>Name</label>
</display>
</column>
<type>RoleType</type>
</view>
</dashboard>
</report>
Report for asynchronous widget
Dashboard report has two kinds of output. Common case is to generated output file with reported data. However, it is also possible to save results of report to dashboard which configuration was used for generating report. Maybe you ask why? It is very simple, when we click in GUI for showing dashboard, midpoint synchronously send requests to repository for objects because of generating widget on screen. It is all right if we do not have many objects and requests are quick. But when we have to wait very long for showing page with widgets we can use saving result of report to dashboard and next configure dashboard in order to show this value.
Configuration of Dashboard report contains attribute storeExportedWidgetData, and using it you can define whether result of report will be save to file, widget or both.
Name | Description |
---|---|
|
Exported widget data will be stored only in element of widget. |
|
Exported widget data will be stored only in file. |
|
Exported widget data will be stored only in element of widget and file. |
Example of generated report
On screenshot we can see example generated report of Dashboard report in HTML format. On top we see table of widgets with name, message and status. Under table of widgets, report continue with one table for every widget.

Creating of report
We have two choices for it. One of the choices is using classical flow for creating new object, so we find Report menu item on left side of screen and select new Report
Second simplify way how to create report is to click on 'Create report' under table on object list pages (e.g. All users page).

After click you will be redirected to Create report page with predefined filter from search panel over object table and columns from previous table.
Tasks for reports
If we create new report via GUI in 4.4. Midpoint create task with work definition of activity with 'classcal' way for actual kind of report. We know three kind of activities for reports.
ClassicReportExportWorkDefinitionType 'reportExport'
Exports any report in a "classical" way. Supports multithreading but not bucketing (multi-node mode). Support objects, containers and audit reports. Contains two parameter 'reportRef' and 'reportParam'. 'reportRef' represent reference to report object. 'reportParam' represent parameters for report, which are defined via GUI.
ClassicReportImportWorkDefinitionType 'reportImport'
Imports a report in a "classical" way. Supports multithreading but not bucketing (multi-node mode). Contains only one parameter 'reportRef'.
DistributedReportExportWorkDefinitionType 'distributedReportExport'
Exports report of repository objects or audit reports. Supports bucketing (multi-node mode) and multithreading. Contains two parameter 'reportRef' and 'reportParam'. 'reportRef' represent reference to report object. 'reportParam' represent parameters for report, which are defined via GUI.
Convert ClassicReportExportWorkDefinitionType to DistributedReportExportWorkDefinitionType
If we running report via GUI, then the task with classic report export definition will be created. If we are running report for objects or audit records and Midpoint is running in clustered mode, then we can convert the 'classical' way to distributed. We can rewrite it by modifying its xml configuration. In the configuration of task in raw mode we change 'reportExport' to 'distributedReportExport'. We change archetype from 'Report export task' to 'Distributed report export task', by changing oid for assignment, that define archetype. Next we remove current arhetypeRef and rolememberRef attributes for old archetype. And finally we add configuration for Bucket-based work state management.

Also we can create new task with distributed report export definition and set reference to our report.
Import behaviour 'Import report'
Since version 4.2, midPoint supports 'import report'/'reverse report'. This feature is experimental. Report output generated by midPoint can also be used in reverse way - you can import it back to midPoint. In addition, it is also possible to import custom defined report output. Midpoint support two kind of import configuration . Configuration for Object import and Import script.
Object import
MidPoint has to understand the report output data structure to preform import correctly. This is configured in report (ReportType), in similar way as for exporting. For now, only ObjectCollectionReportEngineConfigurationType and CSV format is supported (CSV FileFormatType).
Example of imported file:
"Name";"Administrative status";"Valid from";"Nick";"AssignmentOid";"Subtype"
"testUser01";"enabled";"2020-07-07T00:00:00.000+02:00";"nick1";"00000000-0000-0000-0000-000000000008,00000000-0000-0000-0000-000000000004";"sub1,sub22"
"testUser02";"enabled";"2020-07-07T00:00:00.000+02:00";"NICK2";;
Example below shows report (ReportType) configuration for importing CSV file with header and two records above.
{
"@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3",
"report" : {
"name" : "Object Collection import report with view",
"objectCollection" : {
"view" : {
"column" : [ {
"name" : "nameColumnCollection",
"path" : "name",
"display" : {
"label" : "Name (Collection)"
}
}, {
"name" : "activationColumn",
"path" : "activation/administrativeStatus",
"previousColumn" : "nameColumnCollection"
}, {
"name" : "validFromColumn",
"path" : "activation/validFrom",
"previousColumn" : "activationColumn"
}, {
"name" : "nickColumn",
"path" : "nickName",
"display" : {
"label" : "Nick"
},
"previousColumn" : "validFromColumn",
"import" : {
"expression" : {
"script" : [ {
"@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ScriptExpressionEvaluatorType",
"code" : "import com.evolveum.midpoint.prism.polystring.PolyString\n\n return new PolyString(\"New nick: \" + input)\n "
} ]
}
}
}, {
"name" : "assignmentColumn",
"path" : "assignment",
"display" : {
"label" : "AssignmentOid"
},
"previousColumn" : "nickColumn",
"import" : {
"expression" : {
"script" : [ {
"@type" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3#ScriptExpressionEvaluatorType",
"code" : "\n import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;\n import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;\n import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType;\n\n assignments = new ArrayList();\n\n for (String oid : input) {\n if (oid != null) {\n role = new ObjectReferenceType();\n role.setOid(oid);\n role.setType(RoleType.COMPLEX_TYPE);\n\n AssignmentType assignment = new AssignmentType();\n assignment.asPrismContainerValue()\n assignment.setTargetRef(role);\n assignments.add(assignment)\n }\n }\n return assignments\n "
} ]
}
}
}, {
"name" : "subtypeColumn",
"path" : "subtype",
"previousColumn" : "assignmentColumn"
} ],
"type" : "UserType"
}
},
"behavior" : {
"direction" : "import"
}
}
}
<report>
<name>Object Collection import report with view</name>
<objectCollection>
<view>
<column>
<name>nameColumnCollection</name>
<path>name</path>
<display>
<label>Name (Collection)</label>
</display>
</column>
<column>
<name>activationColumn</name>
<path>activation/administrativeStatus</path>
<previousColumn>nameColumnCollection</previousColumn>
</column>
<column>
<name>validFromColumn</name>
<path>activation/validFrom</path>
<previousColumn>activationColumn</previousColumn>
</column>
<column>
<name>nickColumn</name>
<path>nickName</path>
<display>
<label>Nick</label>
</display>
<previousColumn>validFromColumn</previousColumn>
<import>
<expression>
<script>
<code>import com.evolveum.midpoint.prism.polystring.PolyString
return new PolyString("New nick: " + input)
</code>
</script>
</expression>
</import>
</column>
<column>
<name>assignmentColumn</name>
<path>assignment</path>
<display>
<label>AssignmentOid</label>
</display>
<previousColumn>nickColumn</previousColumn>
<import>
<expression>
<script>
<code>
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.RoleType;
assignments = new ArrayList();
for (String oid : input) {
if (oid != null) {
role = new ObjectReferenceType();
role.setOid(oid);
role.setType(RoleType.COMPLEX_TYPE);
AssignmentType assignment = new AssignmentType();
assignment.asPrismContainerValue()
assignment.setTargetRef(role);
assignments.add(assignment)
}
}
return assignments
</code>
</script>
</expression>
</import>
</column>
<column>
<name>subtypeColumn</name>
<path>subtype</path>
<previousColumn>assignmentColumn</previousColumn>
</column>
<type>UserType</type>
</view>
</objectCollection>
<behavior>
<direction>import</direction>
<!-- In case of non raw execution -->
<!-- <importOptions>-->
<!-- <modelExecutionOptions>-->
<!-- <raw>false</raw>-->
<!-- </modelExecutionOptions>-->
<!-- </importOptions>-->
</behavior>
</report>
Behaviour and Options
We need define that this report is import and not export, for this we need define element behavior. Behavior contains direction Import or Export. Also behavior contains importOptions, which contains next elements:
Name | Description | Type |
---|---|---|
overwrite |
If set to a true value it will cause that objects that are already in the repository will be overwritten by the imported objects. It may not be applicable to all import types. E.g. it makes no sense for import from resource, as this is not storing objects in the repository directly. |
boolean |
keepOid |
If set to a true value it will cause that objects that overwritten objects will reuse the same OID as previous objects. May be potentially dangerous. USE WITH CARE. |
boolean |
stopAfterErrors |
Number of errors that will cause import to stop. If set to one the import will stop on first error. If set to zero or negative value the import will not stop on any error. |
int |
summarizeSucceses |
If set to true the successfully imported items will be summarized in the result. WARNING: setting this to false may result in a very large result structure and may cause overflow of the system memory. |
boolean |
summarizeErrors |
If set to true the import errors will be summarized in the result. |
boolean |
referentialIntegrity |
boolean |
|
validateStaticSchema |
boolean |
|
validateDynamicSchema |
boolean |
|
encryptProtectedValues |
boolean |
|
fetchResourceSchema |
boolean |
|
keepMetadata |
If set to true then the importer will keep the metadata from the source file. If set to false then the imported will re-generate metadata on each object. |
boolean |
modelExecutionOptions |
If present, these options are used for adding objects into the repository. Null option values might be overridden by import-related options. In particular, the missing "raw" option is overridden to "true". So, if you want the operation run in non-raw mode, set "raw" option to "false" (e.g. runs also global templates, policy configuration, etc…). |
ModelExecuteOptionsType |
compatMode |
Compatibility model. If selected then the data parsing will be less strict. E.g. removed element will be ingnored. |
boolean |
In previous example of report we define mapping values from columns to items in new object. Name of column in CSV file have to be same as name defined in view. Definition of name from view have some rules. Name is obtained from Label of DispalyType for column, when Label is empty, then Midpoint finds name for item from item definition based on Path element in column.
Definition of column also contains import/expression which can define script for generating items. Script have to return real value for example String or List of values for multivalue items for example List<AssignmentType>. Script get input variable which is String, when item is singlevalue, or List<String>, when item is multivalue.
Import script
We can define importScript in element behaviour. Import script is ExecuteScriptType type, so we can define more actions. Script contains variables with same name as headers of imported CSV file. For example from next file will be created variables with names username, role_name, action, valid_from and valid_to.
Example of imported file:
"username";"role_name";"action";"valid_from";"valid_to"
"testUser02";"Superuser";"A";"2018-01-01";"2018-05-01"
"testUser01";"Superuser";"D";;
"fakeUser";"Superuser";"M";"2018-01-01";"2018-05-01"
"jack";"Superuser";"M";"2018-01-01";"2018-05-01"
"jack";"FakeRole";"M";"2018-01-01";"2018-05-01"
"jack";"Superuser";;"2018-01-01";"2018-05-01"
In next example we add/modify/delete assignment on user defined variable username (in first line 'testUser02'). Operation define variable action ('A'=add, 'M'=modify, 'R'=remove). Target of assignment define via name of role variable role_name. Variables valid_from and valid_to define property activation/validFrom and activation/validTo of assignment.
<report>
<name>Report with import script</name>
<behavior>
<importScript xmlns:c="http://midpoint.evolveum.com/xml/ns/public/common/common-3"
xmlns:s="http://midpoint.evolveum.com/xml/ns/public/model/scripting-3">
<s:options>
<s:continueOnAnyError>true</s:continueOnAnyError>
</s:options>
<s:pipeline>
<s:search xmlns:q="http://prism.evolveum.com/xml/ns/public/query-3">
<s:type>UserType</s:type>
<s:searchFilter>
<q:equal>
<q:path>name</q:path>
<c:expression>
<c:script>
<c:code>username</c:code>
</c:script>
</c:expression>
</q:equal>
</s:searchFilter>
</s:search>
<s:execute>
<s:forWholeInput>true</s:forWholeInput>
<s:script>
<s:code>
if (input == null || input.getData().isEmpty()){
log.error("Couldn't find user with name" + username + ". Skip this line.")
}
</s:code>
</s:script>
</s:execute>
<s:modify>
<s:parameter>
<s:name>delta</s:name>
<s:execute>
<s:parameter>
<s:name>outputItem</s:name>
<c:value>ObjectDeltaType</c:value>
</s:parameter>
<s:script>
<s:code>
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.RoleType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;
import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;
import javax.xml.datatype.DatatypeFactory;
import com.evolveum.midpoint.schema.DeltaConvertor;
log.info("-----------START-----------");
log.info("username: " + username);
log.info("role_name: " + role_name);
log.info("action: " + action);
log.info("valid_from: " + valid_from);
log.info("valid_to: " + valid_to);
log.info("input: " + input);
user = input;
userBefore = user.clone();
role = midpoint.searchObjectByName(RoleType.class, role_name);
if (role == null) {
log.error("Couldn't find role with name " + role_name);
return null;
}
if (action.equals("A")) {
roleRef = new ObjectReferenceType();
roleRef.setOid(role.getOid());
roleRef.setType(RoleType.COMPLEX_TYPE);
AssignmentType assignment = new AssignmentType();
assignment.setTargetRef(roleRef);
if (valid_from != null || valid_to != null) {
activation = new ActivationType();
format = new SimpleDateFormat("yyyy-MM-dd");
if (valid_from != null) {
date = format.parse(valid_from);
cal = new GregorianCalendar();
cal.setTime(date);
xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
activation.setValidFrom(xmlGregCal);
}
if (valid_to != null) {
date = format.parse(valid_to);
cal = new GregorianCalendar();
cal.setTime(date);
xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
activation.setValidTo(xmlGregCal);
}
assignment.setActivation(activation);
}
user.getAssignment().add(assignment);
} else if (action.equals("M")) {
for (AssignmentType assignment : user.getAssignment()) {
if (assignment.getTargetRef() != null && role.getOid().equals(assignment.getTargetRef().getOid())) {
if (valid_from != null || valid_to != null) {
activation = new ActivationType();
format = new SimpleDateFormat("yyyy-MM-dd");
if (valid_from != null) {
date = format.parse(valid_from);
cal = new GregorianCalendar();
cal.setTime(date);
xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
activation.setValidFrom(xmlGregCal);
}
if (valid_to != null) {
date = format.parse(valid_to);
cal = new GregorianCalendar();
cal.setTime(date);
xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);
activation.setValidTo(xmlGregCal);
}
assignment.setActivation(activation);
}
break;
}
}
} else if (action.equals("D")) {
for (AssignmentType assignment : user.getAssignment()) {
if (assignment.getTargetRef() != null && role.getOid().equals(assignment.getTargetRef().getOid())) {
user.getAssignment().remove(assignment);
break;
}
}
} else {
log.error("Action column have unexpected value '" + action + "'")
return null;
}
if (userBefore.equals(user)) {
log.error("Couldn't create delta, because user before executing of script is same as after executing of script.")
return null;
}
delta = userBefore.asPrismObject().diff(user.asPrismObject(), EquivalenceStrategy.LITERAL_IGNORE_METADATA);
log.info("delta: " + delta);
log.info("-----------FINISH-----------");
return DeltaConvertor.toObjectDeltaType(delta);
</s:code>
</s:script>
</s:execute>
</s:parameter>
</s:modify>
</s:pipeline>
</importScript>
<direction>import</direction>
</behavior>
</report>
{
"@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3",
"report" : {
"name" : "Report with import script",
"behavior" : {
"direction" : "import",
"importScript" : {
"@ns" : "http://midpoint.evolveum.com/xml/ns/public/model/scripting-3",
"pipeline" : [ {
"@element" : "search",
"type" : "UserType",
"searchFilter" : {
"@ns" : "http://prism.evolveum.com/xml/ns/public/query-3",
"equal" : {
"path" : "name",
"http://midpoint.evolveum.com/xml/ns/public/common/common-3#expression" : {
"@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3",
"script" : {
"code" : "username"
}
}
}
}
}, {
"@element" : "execute",
"script" : {
"@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3",
"code" : "\n if (input == null || input.getData().isEmpty()){\n log.error(\"Couldn't find user with name\" + username + \". Skip this line.\")\n }\n "
},
"forWholeInput" : true
}, {
"@element" : "modify",
"parameter" : [ {
"name" : "delta",
"execute" : {
"parameter" : [ {
"name" : "outputItem",
"http://midpoint.evolveum.com/xml/ns/public/common/common-3#value" : "ObjectDeltaType"
} ],
"script" : {
"@ns" : "http://midpoint.evolveum.com/xml/ns/public/common/common-3",
"code" : "\n import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;\n import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;\n import com.evolveum.midpoint.xml.ns._public.common.common_3.RoleType;\n import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;\n import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;\n import java.text.SimpleDateFormat;\n import java.util.GregorianCalendar;\n import com.evolveum.midpoint.prism.equivalence.EquivalenceStrategy;\n import javax.xml.datatype.DatatypeFactory;\n import com.evolveum.midpoint.schema.DeltaConvertor;\n\n log.info(\"-----------START-----------\");\n log.info(\"username: \" + username);\n log.info(\"role_name: \" + role_name);\n log.info(\"action: \" + action);\n log.info(\"valid_from: \" + valid_from);\n log.info(\"valid_to: \" + valid_to);\n log.info(\"input: \" + input);\n\n user = input;\n userBefore = user.clone();\n role = midpoint.searchObjectByName(RoleType.class, role_name);\n if (role == null) {\n log.error(\"Couldn't find role with name \" + role_name);\n return null;\n }\n if (action.equals(\"A\")) {\n roleRef = new ObjectReferenceType();\n roleRef.setOid(role.getOid());\n roleRef.setType(RoleType.COMPLEX_TYPE);\n AssignmentType assignment = new AssignmentType();\n assignment.setTargetRef(roleRef);\n\n if (valid_from != null || valid_to != null) {\n activation = new ActivationType();\n format = new SimpleDateFormat(\"yyyy-MM-dd\");\n if (valid_from != null) {\n date = format.parse(valid_from);\n cal = new GregorianCalendar();\n cal.setTime(date);\n xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);\n activation.setValidFrom(xmlGregCal);\n }\n\n if (valid_to != null) {\n date = format.parse(valid_to);\n cal = new GregorianCalendar();\n cal.setTime(date);\n xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);\n activation.setValidTo(xmlGregCal);\n }\n assignment.setActivation(activation);\n }\n user.getAssignment().add(assignment);\n } else if (action.equals(\"M\")) {\n for (AssignmentType assignment : user.getAssignment()) {\n if (assignment.getTargetRef() != null && role.getOid().equals(assignment.getTargetRef().getOid())) {\n if (valid_from != null || valid_to != null) {\n activation = new ActivationType();\n format = new SimpleDateFormat(\"yyyy-MM-dd\");\n if (valid_from != null) {\n date = format.parse(valid_from);\n cal = new GregorianCalendar();\n cal.setTime(date);\n xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);\n activation.setValidFrom(xmlGregCal);\n }\n\n if (valid_to != null) {\n date = format.parse(valid_to);\n cal = new GregorianCalendar();\n cal.setTime(date);\n xmlGregCal = DatatypeFactory.newInstance().newXMLGregorianCalendar(cal);\n activation.setValidTo(xmlGregCal);\n }\n assignment.setActivation(activation);\n }\n break;\n }\n }\n } else if (action.equals(\"D\")) {\n for (AssignmentType assignment : user.getAssignment()) {\n if (assignment.getTargetRef() != null && role.getOid().equals(assignment.getTargetRef().getOid())) {\n user.getAssignment().remove(assignment);\n break;\n }\n }\n } else {\n log.error(\"Action column have unexpected value '\" + action + \"'\")\n return null;\n }\n if (userBefore.equals(user)) {\n log.error(\"Couldn't create delta, because user before executing of script is same as after executing of script.\")\n return null;\n }\n delta = userBefore.asPrismObject().diff(user.asPrismObject(), EquivalenceStrategy.LITERAL_IGNORE_METADATA);\n log.info(\"delta: \" + delta);\n log.info(\"-----------FINISH-----------\");\n return DeltaConvertor.toObjectDeltaType(delta);\n "
}
}
} ]
} ],
"options" : {
"continueOnAnyError" : true
}
}
}
}
}
Security Of Report Expressions
Reports often use expressions. Expressions allow to customize midPoint behavior and they are essential for the success of midPoint deployments. However, the expressions are very powerful and they may even be too powerful for some use cases. The expressions can use general-purpose scripting languages such as Groovy or JavaScript. Therefore such expressions have almost unlimited capabilities. Which means that the expressions can damage the system or compromise security of the system. Use the expressions with utmost care.
Currently, there are very little restraints for expression execution. The expression functions provided by midPoint usually check for proper authorizations. But as the expressions can use general-purpose languages, there is no obligation for the expressions to use those libraries. The expression can easily circumvent those weak protections. Therefore do not let any unauthorized user to set up any kind of expression in midPoint. Allowing the right to edit any expression may lead to compromise of system security.
Some expression security can be achieved by using expression profiles. Expression profiles can be used to limit the capabilities of report expressions, e.g. to limit them to safe operations that just manipulate strings and basic data structures. This seems to work reasonably well for ordinary object-based reports. However, when it comes to audit reports, this solution may not be sufficient. Audit records are not midPoint objects, they are just rows in ordinary relational table. Therefore the usual midPoint mechanisms do not apply to them. E.g. they cannot be queries by using midPoint query mechanisms. There is a way how a "safe" expression can construct a string query for audit table. However, there is no protection against SQL injection or similar attacks. Major improvement to auditing capabilities of midPoint would be needed for that purpose.
An example of such an audit report can be found in midPoint tests: https://github.com/Evolveum/midpoint/blob/master/model/report-impl/src/test/resources/reports/report-audit-csv.xml
However, this is just an example.
It may not be complete, it may not be secure.
There are no guarantees.
Use at your own risk.
In case that a secure audit reports are needed, the current recommendation is to make such reports outside of midPoint. The structure of an audit table is documented and it can be used for integration with data warehouse and/or SIEM systems. MidPoint is neither of those systems and it has no ambition to become one. Therefore such integration is likely to be required anyway to construct a complete information security solution.
See Security Guide for more detail regarding security-related functionality of midPoint.