Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Jul 16, 2019
2 parents 33d4334 + 24da84d commit 12494e3
Show file tree
Hide file tree
Showing 16 changed files with 355 additions and 20 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2018 Evolveum
* Copyright (c) 2010-2019 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -90,7 +90,9 @@ public enum ModelAuthorizationAction implements DisplayableValue<String> {

RAW_OPERATION("rawOperation", "Raw operation", "RAW_OPERATION_HELP"),
PARTIAL_EXECUTION("partialExecution", "Partial execution", "PARTIAL_EXECUTION_HELP"),
GET_EXTENSION_SCHEMA("getExtensionSchema", "Get extension schema", "GET_EXTENSION_SCHEMA_HELP");
GET_EXTENSION_SCHEMA("getExtensionSchema", "Get extension schema", "GET_EXTENSION_SCHEMA_HELP"),

RUN_REPORT("runReport", "Run report", "RUN_REPORT_HELP");

public static final String[] AUTZ_ACTIONS_URLS_SEARCH = new String[] { READ.getUrl(), SEARCH.getUrl() };
public static final String[] AUTZ_ACTIONS_URLS_GET = new String[] { READ.getUrl(), GET.getUrl() };
Expand Down
Expand Up @@ -45,7 +45,7 @@ public interface ReportManager {
* @param report
* @param parentResult describes report which has to be created
*/
void runReport(PrismObject<ReportType> object, PrismContainer<ReportParameterType> params, Task task, OperationResult parentResult);
void runReport(PrismObject<ReportType> object, PrismContainer<ReportParameterType> params, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException;

/**
* todo comments [lazyman]
Expand Down
Expand Up @@ -61,4 +61,8 @@ Collection<AuditEventRecord> evaluateAuditScript(PrismObject<ReportType> report,

// hack todo fixme
PrismContext getPrismContext();

boolean isAuthorizedToRunReport(PrismObject<ReportType> report, Task task, OperationResult result)
throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException,
ConfigurationException, SecurityViolationException;
}
Expand Up @@ -33,6 +33,7 @@
import javax.xml.namespace.QName;

import com.evolveum.midpoint.repo.common.ObjectResolver;
import com.evolveum.midpoint.report.api.ReportService;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
Expand Down Expand Up @@ -149,6 +150,7 @@ public class ReportHTMLCreateTaskHandler extends ReportJasperCreateTaskHandler {
@Autowired private Clock clock;
@Autowired private TaskManager taskManager;
@Autowired private AuditService auditService;
@Autowired private ReportService reportService;
@Autowired private ModelService modelService;
@Autowired private PrismContext prismContext;
@Autowired @Qualifier("modelObjectResolver") private ObjectResolver objectResolver;
Expand Down Expand Up @@ -260,6 +262,11 @@ public TaskRunResult run(RunningTask task, TaskPartitionDefinitionType partition
try {
ReportType parentReport = objectResolver.resolve(task.getObjectRefOrClone(), ReportType.class, null,
"resolving report", task, result);

if (!reportService.isAuthorizedToRunReport(parentReport.asPrismObject(), task, parentResult)) {
LOGGER.error("Task {} is not authorized to run report {}", task, parentReport);
throw new SecurityViolationException("Not authorized");
}

if (parentReport.getReportEngine() == null) {
throw new IllegalArgumentException("Report Object doesn't have ReportEngine attribute");
Expand Down
Expand Up @@ -156,6 +156,12 @@ public TaskRunResult run(RunningTask task, TaskPartitionDefinitionType partition

try {
ReportType parentReport = objectResolver.resolve(task.getObjectRefOrClone(), ReportType.class, null, "resolving report", task, result);

if (!reportService.isAuthorizedToRunReport(parentReport.asPrismObject(), task, parentResult)) {
LOGGER.error("Task {} is not authorized to run report {}", task, parentReport);
throw new SecurityViolationException("Not authorized");
}

Map<String, Object> parameters = completeReport(parentReport, task, result);

JasperReport jasperReport = loadJasperReport(parentReport);
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2015 Evolveum
* Copyright (c) 2010-2019 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -32,6 +32,7 @@
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.report.api.ReportManager;
import com.evolveum.midpoint.report.api.ReportService;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
Expand Down Expand Up @@ -93,6 +94,7 @@ public class ReportManagerImpl implements ReportManager, ChangeHook, ReadHook {
@Autowired private HookRegistry hookRegistry;
@Autowired private TaskManager taskManager;
@Autowired private PrismContext prismContext;
@Autowired private ReportService reportService;
@Autowired private ModelService modelService;
@Autowired private ClusterExecutionHelper clusterExecutionHelper;

Expand Down Expand Up @@ -133,7 +135,13 @@ private boolean isRaw(Collection<SelectorOptions<GetOperationOptions>> options)
*/

@Override
public void runReport(PrismObject<ReportType> object, PrismContainer<ReportParameterType> paramContainer, Task task, OperationResult parentResult) {
public void runReport(PrismObject<ReportType> object, PrismContainer<ReportParameterType> paramContainer, Task task, OperationResult parentResult) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {

if (!reportService.isAuthorizedToRunReport(object, task, parentResult)) {
LOGGER.error("User is not authorized to run report {}", object);
throw new SecurityViolationException("Not authorized");
}

if(isDashboarReport(object)) {
task.setHandlerUri(ReportHTMLCreateTaskHandler.REPORT_HTML_CREATE_TASK_URI);
} else {
Expand All @@ -155,7 +163,9 @@ public void runReport(PrismObject<ReportType> object, PrismContainer<ReportParam
parentResult.setBackgroundTaskOid(task.getOid());
}

private boolean isDashboarReport(PrismObject<ReportType> object) {


private boolean isDashboarReport(PrismObject<ReportType> object) {
if(object.getRealValue() != null && object.getRealValue().getReportEngine() != null
&& object.getRealValue().getReportEngine().equals(ReportEngineSelectionType.DASHBOARD)) {
return true;
Expand Down Expand Up @@ -265,6 +275,9 @@ public void invokeOnException(@NotNull ModelContext context, @NotNull Throwable
public void cleanupReports(CleanupPolicyType cleanupPolicy, OperationResult parentResult) {
OperationResult result = parentResult.createSubresult(CLEANUP_REPORT_OUTPUTS);

// This operation does not need any extra authorization check. All model operations carried out by this
// method are executed through modelService. Therefore usual object authorizations are checked.

if (cleanupPolicy.getMaxAge() == null) {
return;
}
Expand Down Expand Up @@ -335,6 +348,9 @@ public void cleanupReports(CleanupPolicyType cleanupPolicy, OperationResult pare
@Override
public void deleteReportOutput(ReportOutputType reportOutput, OperationResult parentResult) throws Exception {
String oid = reportOutput.getOid();

// This operation does not need any extra authorization check. All model operations carried out by this
// method are executed through modelService. Therefore usual object authorizations are checked.

Task task = taskManager.createTaskInstance(DELETE_REPORT_OUTPUT);
parentResult.addSubresult(task.getResult());
Expand Down Expand Up @@ -389,6 +405,10 @@ public InputStream getReportOutputData(String reportOutputOid, OperationResult p

OperationResult result = parentResult.createSubresult(REPORT_OUTPUT_DATA);
result.addParam("oid", reportOutputOid);

// This operation does not need any extra authorization check. All model operations carried out by this
// method are executed through modelService. Therefore usual object authorizations are checked.
// Here we assume that anyone that can read the ReportOutputType object can also read report data. Which is a fair assumption.

try {
ReportOutputType reportOutput = modelService.getObject(ReportOutputType.class, reportOutputOid, null, task,
Expand Down
Expand Up @@ -339,16 +339,11 @@ private TypedValue<VariablesMap> getConvertedParams(VariablesMap parameters) {
}

private Collection<FunctionLibrary> createFunctionLibraries() {
// FunctionLibrary functionLib = ExpressionUtil.createBasicFunctionLibrary(prismContext,
// prismContext.getDefaultProtector());
FunctionLibrary midPointLib = new FunctionLibrary();
midPointLib.setVariableName("report");
midPointLib.setNamespace("http://midpoint.evolveum.com/xml/ns/public/function/report-3");
ReportFunctions reportFunctions = new ReportFunctions(prismContext, schemaHelper, model, taskManager, auditService);
midPointLib.setGenericFunctions(reportFunctions);
//
// MidpointFunctionsImpl mp = new MidpointFunctionsImpl();
// mp.

Collection<FunctionLibrary> functions = new ArrayList<>();
functions.add(basicFunctionLibrary);
Expand Down Expand Up @@ -433,4 +428,10 @@ private String getScriptLanguageName(PrismObject<ReportType> report) {
public PrismObject<ReportType> getReportDefinition(String reportOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
return model.getObject(ReportType.class, reportOid, null, task, result);
}

@Override
public boolean isAuthorizedToRunReport(PrismObject<ReportType> report, Task task, OperationResult result) throws SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException, SecurityViolationException {
AuthorizationParameters<ReportType,ObjectType> params = AuthorizationParameters.Builder.buildObject(report);
return securityEnforcer.isAuthorized(ModelAuthorizationAction.RUN_REPORT.getUrl(), null, params, null, task, result);
}
}
Expand Up @@ -19,6 +19,8 @@
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;

Expand All @@ -30,6 +32,7 @@
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;
Expand All @@ -40,6 +43,8 @@
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;
Expand Down Expand Up @@ -79,7 +84,7 @@ public ObjectListType evaluateScript(String reportOid, String script, RemoteRepo

try {

PrismObject<ReportType> report = authorizeReportProcessing(reportOid, task, operationResult);
PrismObject<ReportType> report = authorizeReportProcessing("evaluateScript", reportOid, task, operationResult);

VariablesMap params = getParamsMap(parameters);
Collection resultList = reportService.evaluateScript(report, script, params, task, operationResult);
Expand All @@ -98,7 +103,7 @@ public AuditEventRecordListType evaluateAuditScript(String reportOid, String scr
OperationResult operationResult = task.getResult();

try {
PrismObject<ReportType> report = authorizeReportProcessing(reportOid, task, operationResult);
PrismObject<ReportType> report = authorizeReportProcessing("evaluateAuditScript", reportOid, task, operationResult);

VariablesMap params = getParamsMap(parameters);
Collection<AuditEventRecord> resultList = reportService.evaluateAuditScript(report, script, params, task, operationResult);
Expand Down Expand Up @@ -185,7 +190,7 @@ public ObjectListType processReport(String reportOid, String query, RemoteReport

try {

PrismObject<ReportType> report = authorizeReportProcessing(reportOid, task, operationResult);
PrismObject<ReportType> report = authorizeReportProcessing("processReport", reportOid, task, operationResult);

VariablesMap parametersMap = getParamsMap(parameters);
ObjectQuery q = reportService.parseQuery(report, query, parametersMap, task, operationResult);
Expand All @@ -201,12 +206,16 @@ public ObjectListType processReport(String reportOid, String query, RemoteReport

}

private PrismObject<ReportType> authorizeReportProcessing(String reportOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, SecurityViolationException, CommunicationException, ConfigurationException, ExpressionEvaluationException {
private PrismObject<ReportType> 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<ReportType> report = reportService.getReportDefinition(reportOid, task, result);
// TODO TODO TODO: authorization
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;
}

Expand Down
Expand Up @@ -74,6 +74,15 @@ public class AbstractReportIntegrationTest extends AbstractModelIntegrationTest
protected final static File ROLE_SUPERUSER_FILE = new File(TEST_DIR_COMMON, "role-superuser.xml");

protected final static File USER_ADMINISTRATOR_FILE = new File(TEST_DIR_COMMON, "user-administrator.xml");

protected final static File USER_READER_FILE = new File(TEST_DIR_COMMON, "user-reader.xml");
protected final static String USER_READER_USERNAME = "reader";
protected final static File USER_RUNNER_FILE = new File(TEST_DIR_COMMON, "user-runner.xml");
protected final static String USER_RUNNER_USERNAME = "runner";
protected final static File USER_READER_RUNNER_FILE = new File(TEST_DIR_COMMON, "user-reader-runner.xml");
protected final static String USER_READER_RUNNER_USERNAME = "reader-runner";
protected final static File ROLE_READER_FILE = new File(TEST_DIR_COMMON, "role-reader.xml");
protected final static File ROLE_RUNNER_FILE = new File(TEST_DIR_COMMON, "role-runner.xml");

@Autowired protected ReportManager reportManager;
@Autowired
Expand All @@ -86,7 +95,7 @@ public class AbstractReportIntegrationTest extends AbstractModelIntegrationTest
public void initSystem(Task initTask, OperationResult initResult) throws Exception {
super.initSystem(initTask, initResult);

repoAddObjectFromFile(USER_JACK_FILE, true, initResult).asObjectable();
repoAddObjectFromFile(USER_JACK_FILE, true, initResult);
repoAddObjectFromFile(ROLE_SUPERUSER_FILE, initResult);
// System Configuration
modelService.postInit(initResult);
Expand Down
Expand Up @@ -66,6 +66,12 @@ public class TestReportWebService extends AbstractReportIntegrationTest {
@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);
Expand Down Expand Up @@ -148,8 +154,97 @@ public void test112ProcessReportUserListInvalidReportOid() throws Exception {
}
}

// TODO: test that violates authorization to run report
// TODO: test that violates authorization to read report
/**
* MID-5463
*/
@Test
public void test115ProcessReportUserListUnauthorizedReader() throws Exception {
final String TEST_NAME = "test115ProcessReportUserListUnauthorizedReader";
displayTestTitle(TEST_NAME);

login(USER_READER_USERNAME);

String query = createAllQueryString(UserType.class);
RemoteReportParametersType parameters = createReportParameters();

try {

// WHEN
displayWhen(TEST_NAME);
reportWebService.processReport(REPORT_USER_LIST_EXPRESSIONS_CSV_OID, query, parameters, null);

assertNotReached();

} catch (Fault f) {
// THEN
displayThen(TEST_NAME);
display("Expected fault", f);
} finally {
login(USER_ADMINISTRATOR_USERNAME);
}
}

/**
* MID-5463
*/
@Test
public void test116ProcessReportUserListUnauthorizedRunner() throws Exception {
final String TEST_NAME = "test116ProcessReportUserListUnauthorizedRunner";
displayTestTitle(TEST_NAME);

login(USER_RUNNER_USERNAME);

String query = createAllQueryString(UserType.class);
RemoteReportParametersType parameters = createReportParameters();

try {

// WHEN
displayWhen(TEST_NAME);
reportWebService.processReport(REPORT_USER_LIST_EXPRESSIONS_CSV_OID, query, parameters, null);

assertNotReached();

} catch (Fault f) {
// THEN
displayThen(TEST_NAME);
display("Expected fault", f);
} finally {
login(USER_ADMINISTRATOR_USERNAME);
}
}

/**
* MID-5463
*/
@Test
public void test119ProcessReportUserListReaderRunner() throws Exception {
final String TEST_NAME = "test119ProcessReportUserListReaderRunner";
displayTestTitle(TEST_NAME);

login(USER_READER_RUNNER_USERNAME);

String query = createAllQueryString(UserType.class);
RemoteReportParametersType parameters = createReportParameters();

ObjectListType userList;
try {

// WHEN
displayWhen(TEST_NAME);
userList = reportWebService.processReport(REPORT_USER_LIST_EXPRESSIONS_CSV_OID, query, parameters, null);

} finally {
login(USER_ADMINISTRATOR_USERNAME);
}

// THEN
displayThen(TEST_NAME);
display("Returned user list ("+userList.getObject().size()+" objects)", userList);

assertUserList(userList);
}

// TODO: test that violates safe profile

private String createAllQueryString(Class<?> type) {
Expand Down

0 comments on commit 12494e3

Please sign in to comment.