diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java index cfdafca0f8e..8b706fae1df 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelAuthorizationAction.java @@ -92,6 +92,22 @@ public enum ModelAuthorizationAction implements DisplayableValue { READ_THREADS("readThreads", "Read threads", "READ_THREADS_HELP"), + /** + * Ability to read the content of the midPoint log file, and to determine its size. + * + * Covers both {@link ModelDiagnosticService#getLogFileContent(Long, Long, Task, OperationResult)} and + * {@link ModelDiagnosticService#getLogFileSize(Task, OperationResult)}. The two are not separated, because + * the size is strictly less information than the content, and clients are expected to use them together + * (see the `ReturnedDataPosition` / `CurrentLogFileSize` headers of the `/log` REST operation). + * + * Previously, these operations required the `authorization-3#all` authorization, i.e. effectively a superuser. + * Existing superusers are not affected by the change, as `#all` is applicable to any action. + * + * @see RestAuthorizationAction#GET_LOG + * @see RestAuthorizationAction#GET_LOG_SIZE + */ + READ_LOG("readLog", "Read log", "READ_LOG_HELP"), + /** Ability to complete a work item (case- or certification- related). */ COMPLETE_WORK_ITEM("completeWorkItem", "Complete work item", "COMPLETE_WORK_ITEM_HELP"), diff --git a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java index 661e9963e6e..424562f4421 100644 --- a/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java +++ b/model/model-api/src/main/java/com/evolveum/midpoint/model/api/ModelDiagnosticService.java @@ -127,14 +127,21 @@ String exportDataModel(ResourceType resource, DataModelVisualizer.Target target, /** * Returns the contents of the log file. * + * Requires the {@link ModelAuthorizationAction#READ_LOG} authorization. + * (Before, the `authorization-3#all` one - i.e. effectively a superuser - was required.) + * * @param fromPosition From absolute log file position (if non-negative); or counted from the end (if negative). * @param maxSize Max number of bytes to return. - * @param task - * @param parentResult */ LogFileContentType getLogFileContent(Long fromPosition, Long maxSize, Task task, OperationResult parentResult) throws SecurityViolationException, IOException, SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException; + /** + * Returns the size of the log file, in bytes. + * + * Requires the {@link ModelAuthorizationAction#READ_LOG} authorization. + * (Before, the `authorization-3#all` one - i.e. effectively a superuser - was required.) + */ long getLogFileSize(Task task, OperationResult parentResult) throws SchemaException, SecurityViolationException, ObjectNotFoundException, ExpressionEvaluationException, ConfigurationException, CommunicationException; // change the return type eventually diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java index 933e45cc404..77a628b4071 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/ModelDiagController.java @@ -24,6 +24,7 @@ import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration; import com.evolveum.midpoint.init.SystemUtil; import com.evolveum.midpoint.model.api.DataModelVisualizer; +import com.evolveum.midpoint.model.api.ModelAuthorizationAction; import com.evolveum.midpoint.model.api.ModelDiagnosticService; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; @@ -596,12 +597,20 @@ public String exportDataModel(ResourceType resource, DataModelVisualizer.Target } } + /** + * Note about authorizations: This method (and {@link #getLogFileSize(Task, OperationResult)}) is covered by the + * {@link ModelAuthorizationAction#READ_LOG} authorization. To avoid the need of having `rest-3#all` authorization + * to use the corresponding REST methods, there are also special (much more specific, i.e. weaker) REST + * authorizations: `rest-3#getLog` and `rest-3#getLogSize` (see `RestAuthorizationAction`). + * So, a user reading the log needs just these (rather weak) authorizations, instead of the `authorization-3#all` + * one that was required before. + */ @Override public LogFileContentType getLogFileContent(Long fromPosition, Long maxSize, Task task, OperationResult parentResult) throws SecurityViolationException, IOException, SchemaException, ObjectNotFoundException, ExpressionEvaluationException, CommunicationException, ConfigurationException { OperationResult result = parentResult.createSubresult(GET_LOG_FILE_CONTENT); try { - securityEnforcer.authorizeAll(task, result); + securityEnforcer.authorize(ModelAuthorizationAction.READ_LOG.getUrl(), task, result); File logFile = getLogFile(result); LogFileContentType rv = getLogFileFragment(logFile, fromPosition, maxSize); result.recordSuccess(); @@ -653,7 +662,7 @@ public long getLogFileSize(Task task, OperationResult parentResult) ConfigurationException, CommunicationException { OperationResult result = parentResult.createSubresult(GET_LOG_FILE_SIZE); try { - securityEnforcer.authorizeAll(task, result); + securityEnforcer.authorize(ModelAuthorizationAction.READ_LOG.getUrl(), task, result); File logFile = getLogFile(result); long size = logFile.length(); result.recordSuccess(); diff --git a/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/RestAuthorizationAction.java b/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/RestAuthorizationAction.java index 9200d7003c4..8da05371cbc 100644 --- a/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/RestAuthorizationAction.java +++ b/repo/security-api/src/main/java/com/evolveum/midpoint/security/api/RestAuthorizationAction.java @@ -47,7 +47,9 @@ public enum RestAuthorizationAction implements DisplayableValue { RUN_TASK("runTask", "Run task", "RUN_TASK_HELP"), EXECUTE_SCRIPT("executeScript", "Execute script", "EXECUTE_SCRIPT_HELP"), COMPARE_OBJECT("compareObject", "Compare object", "COMPARE_OBJECT_HELP"), + /** Besides this one, the `authorization-model-3#readLog` action is required as well. */ GET_LOG_SIZE("getLogSize", "Get log size", "GET_LOG_SIZE_HELP"), + /** Besides this one, the `authorization-model-3#readLog` action is required as well. */ GET_LOG("getLog", "Get log", "GET_LOG_HELP"), RESET_CREDENTIAL("resetCredential", "Reset credential", "RESET_CREDENTIAL_HELP"), GET_THREADS("getThreads", "Get threads", "GET_THREADS_HELP"), diff --git a/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/AbstractRestServiceInitializer.java b/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/AbstractRestServiceInitializer.java index 2ae8d8b97ef..aecd02e71c3 100644 --- a/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/AbstractRestServiceInitializer.java +++ b/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/AbstractRestServiceInitializer.java @@ -77,11 +77,15 @@ public abstract class AbstractRestServiceInitializer extends AbstractGuiIntegrat public static final File USER_REST_LIMITED_FILE = new File(BASE_REPO_DIR, "user-rest-limited.xml"); public static final String USER_REST_LIMITED_NAME = "rest-limited"; public static final String USER_REST_LIMITED_PASSWORD = "r3stL1m1t3d"; + public static final File USER_REST_LOG_FILE = new File(BASE_REPO_DIR, "user-rest-log.xml"); + public static final String USER_REST_LOG_NAME = "rest-log"; + public static final String USER_REST_LOG_PASSWORD = "r3stL0gR34d"; public static final File ROLE_SUPERUSER_FILE = new File(BASE_REPO_DIR, "role-superuser.xml"); public static final File ROLE_ENDUSER_FILE = new File(BASE_REPO_DIR, "role-enduser.xml"); public static final File ROLE_REST_FILE = new File(BASE_REPO_DIR, "role-rest.xml"); public static final File ROLE_REST_LIMITED_FILE = new File(BASE_REPO_DIR, "role-rest-limited.xml"); + public static final File ROLE_REST_LOG_FILE = new File(BASE_REPO_DIR, "role-rest-log.xml"); public static final File ROLE_READER_FILE = new File(BASE_REPO_DIR, "role-reader.xml"); public static final File SYSTEM_CONFIGURATION_FILE = new File(BASE_REPO_DIR, "system-configuration.xml"); diff --git a/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/RestServiceInitializer.java b/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/RestServiceInitializer.java index 0aafab299bd..fb9c51f9886 100644 --- a/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/RestServiceInitializer.java +++ b/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/RestServiceInitializer.java @@ -30,6 +30,7 @@ public void initSystem(Task initTask, OperationResult result) throws Exception { addObject(endRole, executeOptions().overwrite(), initTask, result); addObject(ROLE_REST_FILE, initTask, result); addObject(ROLE_REST_LIMITED_FILE, initTask, result); + addObject(ROLE_REST_LOG_FILE, initTask, result); addObject(ROLE_READER_FILE, initTask, result); PrismObject adminUser = parseObject(USER_ADMINISTRATOR_FILE); addObject(adminUser, executeOptions().overwrite(), initTask, result); @@ -38,6 +39,7 @@ public void initSystem(Task initTask, OperationResult result) throws Exception { addObject(USER_SOMEBODY_FILE, initTask, result); addObject(USER_JACK_FILE, initTask, result); addObject(USER_REST_LIMITED_FILE, initTask, result); + addObject(USER_REST_LOG_FILE, initTask, result); addObject(parseObject(VALUE_POLICY_GENERAL), executeOptions().overwrite(), initTask, result); addObject(VALUE_POLICY_NUMERIC, initTask, result); addObject(VALUE_POLICY_SIMPLE, initTask, result); diff --git a/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/TestAbstractRestService.java b/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/TestAbstractRestService.java index 9e29980e93f..32f8c5a6045 100644 --- a/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/TestAbstractRestService.java +++ b/testing/rest/src/test/java/com/evolveum/midpoint/testing/rest/TestAbstractRestService.java @@ -1661,6 +1661,34 @@ public void test690RestMethodNotAuthorized() { getDummyAuditService().assertFailedLogin(SchemaConstants.CHANNEL_REST_URI); } + /* User has REST autz for the log operations, but not the model-level "readLog" one. So this fails. */ + @Test + public void test691GetLogNotAuthorizedAtModelLevel() { + WebClient client = prepareClient(USER_REST_LIMITED_NAME, USER_REST_LIMITED_PASSWORD); + client.path("/log"); + + when(); + Response response = client.get(); + + then(); + displayResponse(response); + assertStatus(response, 403); + } + + /* The same for the "log size" operation. */ + @Test + public void test692GetLogSizeNotAuthorizedAtModelLevel() { + WebClient client = prepareClient(USER_REST_LIMITED_NAME, USER_REST_LIMITED_PASSWORD); + client.path("/log/size"); + + when(); + Response response = client.get(); + + then(); + displayResponse(response); + assertStatus(response, 403); + } + /* User has both REST and model autz for "get object" operation. So this succeeds. */ @Test public void test695RestMethodAuthorized() { @@ -1675,6 +1703,53 @@ public void test695RestMethodAuthorized() { assertStatus(response, 200); } + /* User has both the REST and the model-level "readLog" autz - and is not a superuser. So this succeeds. */ + @Test + public void test696GetLogAuthorized() { + WebClient client = prepareClient(USER_REST_LOG_NAME, USER_REST_LOG_PASSWORD); + client.path("/log"); + client.query("maxSize", 1024); + + when(); + Response response = client.get(); + + then(); + displayResponse(response); + assertStatus(response, 200); + assertNotNull("No CurrentLogFileSize header", response.getHeaderString("CurrentLogFileSize")); + assertNotNull("No log content returned", response.readEntity(String.class)); + } + + /* The same for the "log size" operation. */ + @Test + public void test697GetLogSizeAuthorized() { + WebClient client = prepareClient(USER_REST_LOG_NAME, USER_REST_LOG_PASSWORD); + client.path("/log/size"); + + when(); + Response response = client.get(); + + then(); + displayResponse(response); + assertStatus(response, 200); + long size = Long.parseLong(response.readEntity(String.class).trim()); + AssertJUnit.assertTrue("Log file size is not positive: " + size, size > 0); + } + + /* Superuser has the "all" authorization, which is applicable to any action. So this still succeeds. */ + @Test + public void test698GetLogSizeAsSuperuser() { + WebClient client = prepareClient(); + client.path("/log/size"); + + when(); + Response response = client.get(); + + then(); + displayResponse(response); + assertStatus(response, 200); + } + private WebClient prepareClient() { return prepareClient(USER_ADMINISTRATOR_USERNAME, USER_ADMINISTRATOR_PASSWORD); } diff --git a/testing/rest/src/test/resources/repo/role-rest-limited.xml b/testing/rest/src/test/resources/repo/role-rest-limited.xml index 0a417f3581b..82a8fc0201f 100644 --- a/testing/rest/src/test/resources/repo/role-rest-limited.xml +++ b/testing/rest/src/test/resources/repo/role-rest-limited.xml @@ -19,4 +19,12 @@ This one can be applied. http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#read + + + The REST-level authorizations for the log operations are present, but the model-level "readLog" + one is not. Hence, the operations pass the REST layer and fail at the model layer. + + http://midpoint.evolveum.com/xml/ns/public/security/authorization-rest-3#getLog + http://midpoint.evolveum.com/xml/ns/public/security/authorization-rest-3#getLogSize + diff --git a/testing/rest/src/test/resources/repo/role-rest-log.xml b/testing/rest/src/test/resources/repo/role-rest-log.xml new file mode 100644 index 00000000000..12dbca3b5b2 --- /dev/null +++ b/testing/rest/src/test/resources/repo/role-rest-log.xml @@ -0,0 +1,27 @@ + + + + + rest-log + + Reading of the log file, without any "all" authorization. This is what a read-only administrator + needs in order to read the log via REST. + + + REST-level authorizations for the log operations. + http://midpoint.evolveum.com/xml/ns/public/security/authorization-rest-3#getLog + http://midpoint.evolveum.com/xml/ns/public/security/authorization-rest-3#getLogSize + + + + Model-level authorization to read the log. Together with the REST authorizations above this is + sufficient; no "all" authorization is needed. + + http://midpoint.evolveum.com/xml/ns/public/security/authorization-model-3#readLog + + diff --git a/testing/rest/src/test/resources/repo/user-rest-log.xml b/testing/rest/src/test/resources/repo/user-rest-log.xml new file mode 100644 index 00000000000..d28c798fffe --- /dev/null +++ b/testing/rest/src/test/resources/repo/user-rest-log.xml @@ -0,0 +1,25 @@ + + + + + rest-log + + + + + + r3stL0gR34d + + + + http://midpoint.evolveum.com/xml/ns/public/security/question-2#q001 + r3stL0gR34d + + + +