diff --git a/tests/acceptance/features/api/oauthSetup.feature b/tests/acceptance/features/api/oauthSetup.feature new file mode 100644 index 000000000..693e16a60 --- /dev/null +++ b/tests/acceptance/features/api/oauthSetup.feature @@ -0,0 +1,643 @@ +# SPDX-FileCopyrightText: 2022-2024 Jankari Tech Pvt. Ltd. +# SPDX-License-Identifier: AGPL-3.0-or-later +Feature: setup the integration with OAuth method + + Scenario: setup without team folder + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": false, + "setup_app_password": false + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id", + "nextcloud_client_secret" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"}, + "nextcloud_client_secret": {"pattern": "[A-Za-z0-9]+"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + + Scenario Outline: try to setup with invalid data + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": , + "openproject_client_id": , + "openproject_client_secret": , + "default_enable_navigation": , + "default_enable_unified_search": , + "setup_project_folder": , + "setup_app_password": + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid data"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | instance_url | openproject_client_id | openproject_client_secret | enable_navigation | enable_unified_search | setup_project_folder | setup_app_password | + | null | null | null | null | null | null | null | + | null | "id" | "secret" | false | false | false | false | + | "http://some-host.de" | null | "secret" | false | false | false | false | + | "http://some-host.de" | "id" | null | false | false | false | false | + | "http://some-host.de" | "id" | "secret" | null | false | false | false | + | "http://some-host.de" | "id" | "secret" | true | null | "" | "" | + | "" | "" | "" | "" | "" | false | false | + | "" | "id" | "secret" | false | false | false | false | + | "http://some-host.de" | "" | "secret" | false | false | false | false | + | "http://some-host.de" | "id" | "" | false | false | false | false | + | "http://some-host.de" | "id" | "secret" | "" | false | false | false | + | "http://some-host.de" | "id" | "secret" | true | "" | false | false | + | "ftp://somehost.de" | "the-id" | "secret" | true | false | "a string" | "a string" | + | "http://somehost.de" | false | "secret" | true | false | false | false | + | "http://somehost.de" | "id" | false | true | false | false | false | + | "http://somehost.de" | "the-id" | "secret" | "a string" | false | false | false | + | "http://somehost.de" | "the-id" | "secret" | false | "a string" | false | false | + + + Scenario: try to setup with invalid keys + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "instance_url": "http://openproject.de", + "client_id": "the-client" + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid key"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + + + Scenario Outline: try to setup with missing keys + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid key"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | values | + | {"openproject_client_id": "the-client-id", "openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false} | + | {"openproject_instance_url": "http://some-host.de","openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false, "setup_project_folder": false, "setup_app_password": false } | + | {"openproject_instance_url": "http://some-host.de", "openproject_client_id": "the-client-id", "default_enable_navigation": false, "default_enable_unified_search": false, "setup_project_folder": false, "setup_app_password": false} | + | {"openproject_instance_url": "http://some-host.de", "openproject_client_id": "the-client-id", "openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "setup_project_folder": false , "setup_app_password": false} | + + + Scenario Outline: try to setup with invalid json data + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + + """ + Then the HTTP status code should be "400" or "500" + Examples: + | data | + | "{}" | + | {"values": {"openproject_instance_url": "http://some-host.de","openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false,}} | + | {"values": {"openproject_instance_url": "http://some-host.de","openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false} | + | {"values": | + | "values" | + | "" | + + Scenario: non-admin user tries to setup without team folder + Given user "Carol" has been created + When the user "Carol" sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": false, + "setup_app_password": false + } + } + """ + Then the HTTP status code should be "403" + And the data of the response should match + """ + { + "type": "object", + "required": ["message"], + "properties": { + "message": {"const": "Logged in account must be an admin"} + }, + "not": { + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id", + "nextcloud_client_secret", + "openproject_revocation_status" + ] + } + } + """ + + Scenario Outline: update a setting + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/.*\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key | value | + | openproject_instance_url | "http://some-host.de" | + | openproject_client_id | "client-value" | + | openproject_client_secret | "secret-value" | + | default_enable_navigation | false | + | default_enable_unified_search | true | + + + Scenario Outline: update multiple settings at once + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": , + "": + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/.*\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key1 | value1 | key2 | value2 | + | openproject_instance_url | "http://some-host.de" | openproject_client_id | "client-value" | + | openproject_client_secret | "secret-value" | openproject_client_id | "client-value" | + | openproject_client_secret | "secret-value" | default_enable_navigation | false | + | default_enable_navigation | false | default_enable_unified_search | false | + + + Scenario Outline: try to update a setting with invalid data + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": ""} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key | value | error-message | + | openproject_instance_url | null | invalid data | + | openproject_instance_url | "" | invalid data | + | openproject_instance_url | false | invalid data | + | openproject_client_id | null | invalid data | + | openproject_client_id | "" | invalid data | + | openproject_client_id | false | invalid data | + | openproject_client_secret | null | invalid data | + | openproject_client_secret | "" | invalid data | + | openproject_client_secret | false | invalid data | + | default_enable_navigation | null | invalid data | + | default_enable_navigation | "" | invalid data | + | default_enable_navigation | "string" | invalid data | + | default_enable_unified_search | null | invalid data | + | default_enable_unified_search | "" | invalid data | + | default_enable_unified_search | "string" | invalid data | + | instance_url | "http://op.de" | invalid key | + + + Scenario Outline: try to update multiple settings where at least one is invalid + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": , + "": + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid data"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key1 | value1 | key2 | value2 | + | openproject_instance_url | "http://some-host.de" | openproject_client_id | null | + | openproject_instance_url | "ftp://some-host.de" | openproject_client_id | "some id" | + | openproject_client_secret | "" | openproject_client_id | "client-value" | + | openproject_client_secret | "secret" | openproject_client_id | false | + | openproject_client_secret | "secret-value" | default_enable_navigation | "string" | + | default_enable_navigation | null | default_enable_unified_search | false | + + + Scenario Outline: try to update settings with invalid json data + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + + """ + Then the HTTP status code should be "400" or "500" + Examples: + | data | + | { "values": { "openproject_instance_url": "http://some-host.de"} }} | + | { "values": { "openproject_instance_url": "http://some-host.de"} | + | "values": { "openproject_instance_url": "http://some-host.de"} } | + | { "values": | + | "{}" | + | "" | + + + Scenario: non-admin user tries to update the settings + Given user "Carol" has been created + When the user "Carol" sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "openproject_instance_url": "http://some-host.de" + } + } + """ + Then the HTTP status code should be "403" + And the data of the response should match + """ + { + "type": "object", + "required": ["message"], + "properties": { + "message": {"const": "Logged in account must be an admin"} + }, + "not": { + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id", + "nextcloud_client_secret", + "openproject_revocation_status" + ] + } + } + """ + + + Scenario: reset the integration setup + When the administrator sends a DELETE request to the "setup" endpoint + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": ["status"], + "properties": { + "status": {"const": true} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + + + Scenario: non-admin user tries to reset the integration setup + Given user "Carol" has been created + When the user "Carol" sends a DELETE request to the "setup" endpoint + Then the HTTP status code should be "403" + + Scenario Outline: try to setup with incomplete team folder + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": , + "setup_app_password": + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid data"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | setup_project_folder | setup_app_password | + | true | false | + | false | true | + + + # this test wil not pass locally if your system already has a `OpenProject` user/group setup and 'OpenProjectNoAutomaticProjectFolders' group setup + Scenario: Set up whole integration with project folder and user app password + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "authorization_method": "oauth2", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": true, + "setup_app_password": true + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id", + "nextcloud_client_secret", + "openproject_user_app_password" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"}, + "nextcloud_client_secret": {"pattern": "[A-Za-z0-9]+"}, + "openproject_user_app_password": {"pattern": "[A-Za-z0-9]+"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + And user "OpenProject" should be present in the server + And group "OpenProject" should be present in the server + And user "OpenProject" should be the subadmin of the group "OpenProject" + And group "OpenProjectNoAutomaticProjectFolders" should be present in the server + And user "OpenProject" should be the subadmin of the group "OpenProjectNoAutomaticProjectFolders" + And groupfolder "OpenProject" should be present in the server + And groupfolder "OpenProject" should be assigned to the group "OpenProject" with all permissions + And groupfolder "OpenProject" should have advance permissions enabled + And groupfolder "OpenProject" should be managed by the user "OpenProject" + # the next step is only for the tests, because that user has a random password + Given the administrator has changed the password of "OpenProject" to the default testing password + And user "OpenProject" should have a folder called "OpenProject" + # folders inside the OpenProject folder can only be deleted/renamed by the OpenProject user + And user "Carol" has been created + And user "Carol" has been added to the group "OpenProject" + And user "OpenProject" has created folder "/OpenProject/project-abc" + Then user "Carol" should have a folder called "OpenProject/project-abc" + When user "Carol" deletes folder "/OpenProject/project-abc" + Then the HTTP status code should be 500 + When user "Carol" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" + Then the HTTP status code should be 500 + When user "OpenProject" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" + Then the HTTP status code should be 201 + When user "OpenProject" deletes folder "/OpenProject/project-123" + Then the HTTP status code should be 204 + + # folders 2 levels down inside the OpenProject folder can be deleted by any user even if the parent is also called "OpenProject" + Given user "OpenProject" has created folder "/OpenProject/OpenProject/project-abc" + When user "Carol" renames folder "/OpenProject/OpenProject/project-abc" to "/OpenProject/OpenProject/project-123" + Then the HTTP status code should be 201 + When user "Carol" deletes folder "/OpenProject/OpenProject/project-123" + Then the HTTP status code should be 204 + + # a user, who is not in the OpenProject group can delete/rename items inside a folder that is called OpenProject + Given user "Brian" has been created + And user "Brian" has created folder "/OpenProject/project-abc" + When user "Brian" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" + Then the HTTP status code should be 201 + When user "Brian" deletes folder "/OpenProject/project-123" + Then the HTTP status code should be 204 + + # check deleting / disabling the OpenProject user/group + When the administrator deletes the user "OpenProject" + Then the HTTP status code should be 400 + And user "OpenProject" should be present in the server + When the administrator deletes the group "OpenProject" + Then the HTTP status code should be 400 + And group "OpenProject" should be present in the server + When the administrator disables the user "OpenProject" + Then the HTTP status code should be 400 + + # resending setup request will fail + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": true, + "setup_app_password": true + } + } + """ + Then the HTTP status code should be "409" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "The user \"OpenProject\" already exists"} + } + } + """ + + # sending a PATCH request with setup_project_folder=true will also fail + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values" : { + "setup_project_folder": true + } + } + """ + Then the HTTP status code should be "409" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "The user \"OpenProject\" already exists"} + } + } + """ + + # we can make api request using the created app password for user "OpenProject" + When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using current app password + Then the HTTP status code should be "207" + + # this is to provide test coverage for issues like this + # https://community.openproject.org/projects/nextcloud-integration/work_packages/49621 + When a new browser session for "Openproject" starts + # but other values can be updated by sending a PATCH request + # also we can replace old app password by sending PATCH request to get new user app password + And the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values" : { + "default_enable_navigation": true, + "setup_app_password": true + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"}, + "openproject_user_app_password": {"pattern": "[A-Za-z0-9]+"} + } + } + """ + And the newly generated app password should be different from the previous one + + # user "OpenProject" can make api request using the newly created app password + When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using new app password + Then the HTTP status code should be "207" + + # user "OpenProject" cannot make api request using the old app password + When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using old app password + Then the HTTP status code should be "401" diff --git a/tests/acceptance/features/api/oidcSetup.feature b/tests/acceptance/features/api/oidcSetup.feature new file mode 100644 index 000000000..c240488e8 --- /dev/null +++ b/tests/acceptance/features/api/oidcSetup.feature @@ -0,0 +1,685 @@ +# SPDX-FileCopyrightText: 2026 Jankari Tech Pvt. Ltd. +# SPDX-License-Identifier: AGPL-3.0-or-later +Feature: setup the integration with OIDC method + + + Scenario: setup without team folder (External IDP with token exchange) + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "authorization_method": "oidc", + "sso_provider_type": "external", + "oidc_provider": "Keycloak", + "token_exchange": true, + "targeted_audience_client_id": "openproject", + "setup_project_folder": false, + "setup_app_password": false, + "default_enable_navigation": false, + "default_enable_unified_search": false + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": ["status"], + "properties": { + "status": {"const": true} + }, + "not": { + "required": ["nextcloud_oauth_client_name"] + } + } + """ + + + Scenario: setup without team folder (External IDP without token exchange) + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "authorization_method": "oidc", + "sso_provider_type": "external", + "oidc_provider": "Keycloak", + "token_exchange": false, + "setup_project_folder": false, + "setup_app_password": false, + "default_enable_navigation": false, + "default_enable_unified_search": false + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": ["status"], + "properties": { + "status": {"const": true} + }, + "not": { + "required": ["nextcloud_oauth_client_name"] + } + } + """ + + + Scenario: setup without team folder (Nextcloud IDP) + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "authorization_method": "oidc", + "sso_provider_type": "nextcloud_hub", + "targeted_audience_client_id": "openproject", + "setup_project_folder": false, + "setup_app_password": false, + "default_enable_navigation": false, + "default_enable_unified_search": false + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": ["status"], + "properties": { + "status": {"const": true} + }, + "not": { + "required": ["nextcloud_oauth_client_name"] + } + } + """ + + + Scenario Outline: try to setup with invalid data + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "authorization_method": "oidc", + "sso_provider_type": , + "targeted_audience_client_id": , + "setup_project_folder": false, + "setup_app_password": false, + "default_enable_navigation": false, + "default_enable_unified_search": false + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid data"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | auth_method | provider_type | target_client_id | + | null | "nextcloud_hub" | "client-id" | + | "" | "nextcloud_hub" | "client-id" | + | true | "nextcloud_hub" | "client-id" | + | unknown | "nextcloud_hub" | "client-id" | + | oauth2 | null | "client-id" | + | oauth2 | "" | "client-id" | + | oauth2 | true | "client-id" | + | oauth2 | unknown | "client-id" | + | oauth2 | "nextcloud_hub" | null | + | oauth2 | "nextcloud_hub" | "" | + | oauth2 | "nextcloud_hub" | false | + | oauth2 | "nextcloud_hub" | [] | + + + Scenario: try to setup with invalid keys + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "instance_url": "http://openproject.de", + "client_id": "the-client" + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid key"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + + + Scenario Outline: try to setup with missing keys + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid key"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | values | + | {"openproject_client_id": "the-client-id", "openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false} | + | {"openproject_instance_url": "http://some-host.de","openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false, "setup_project_folder": false, "setup_app_password": false } | + | {"openproject_instance_url": "http://some-host.de", "openproject_client_id": "the-client-id", "default_enable_navigation": false, "default_enable_unified_search": false, "setup_project_folder": false, "setup_app_password": false} | + | {"openproject_instance_url": "http://some-host.de", "openproject_client_id": "the-client-id", "openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "setup_project_folder": false , "setup_app_password": false} | + + + Scenario: non-admin user tries to setup without team folder + Given user "Carol" has been created + When the user "Carol" sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": false, + "setup_app_password": false + } + } + """ + Then the HTTP status code should be "403" + And the data of the response should match + """ + { + "type": "object", + "required": ["message"], + "properties": { + "message": {"const": "Logged in account must be an admin"} + }, + "not": { + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id", + "nextcloud_client_secret", + "openproject_revocation_status" + ] + } + } + """ + + Scenario Outline: update a setting + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/.*\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key | value | + | openproject_instance_url | "http://some-host.de" | + | openproject_client_id | "client-value" | + | openproject_client_secret | "secret-value" | + | default_enable_navigation | false | + | default_enable_unified_search | true | + + + Scenario Outline: update multiple settings at once + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": , + "": + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/.*\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key1 | value1 | key2 | value2 | + | openproject_instance_url | "http://some-host.de" | openproject_client_id | "client-value" | + | openproject_client_secret | "secret-value" | openproject_client_id | "client-value" | + | openproject_client_secret | "secret-value" | default_enable_navigation | false | + | default_enable_navigation | false | default_enable_unified_search | false | + + + Scenario Outline: try to update a setting with invalid data + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": ""} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key | value | error-message | + | openproject_instance_url | null | invalid data | + | openproject_instance_url | "" | invalid data | + | openproject_instance_url | false | invalid data | + | openproject_client_id | null | invalid data | + | openproject_client_id | "" | invalid data | + | openproject_client_id | false | invalid data | + | openproject_client_secret | null | invalid data | + | openproject_client_secret | "" | invalid data | + | openproject_client_secret | false | invalid data | + | default_enable_navigation | null | invalid data | + | default_enable_navigation | "" | invalid data | + | default_enable_navigation | "string" | invalid data | + | default_enable_unified_search | null | invalid data | + | default_enable_unified_search | "" | invalid data | + | default_enable_unified_search | "string" | invalid data | + | instance_url | "http://op.de" | invalid key | + + + Scenario Outline: try to update multiple settings where at least one is invalid + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "": , + "": + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid data"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | key1 | value1 | key2 | value2 | + | openproject_instance_url | "http://some-host.de" | openproject_client_id | null | + | openproject_instance_url | "ftp://some-host.de" | openproject_client_id | "some id" | + | openproject_client_secret | "" | openproject_client_id | "client-value" | + | openproject_client_secret | "secret" | openproject_client_id | false | + | openproject_client_secret | "secret-value" | default_enable_navigation | "string" | + | default_enable_navigation | null | default_enable_unified_search | false | + + + Scenario Outline: try to update settings with invalid json data + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + + """ + Then the HTTP status code should be "400" or "500" + Examples: + | data | + | { "values": { "openproject_instance_url": "http://some-host.de"} }} | + | { "values": { "openproject_instance_url": "http://some-host.de"} | + | "values": { "openproject_instance_url": "http://some-host.de"} } | + | { "values": | + | "{}" | + | "" | + + + Scenario: non-admin user tries to update the settings + Given user "Carol" has been created + When the user "Carol" sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values": { + "openproject_instance_url": "http://some-host.de" + } + } + """ + Then the HTTP status code should be "403" + And the data of the response should match + """ + { + "type": "object", + "required": ["message"], + "properties": { + "message": {"const": "Logged in account must be an admin"} + }, + "not": { + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id", + "nextcloud_client_secret", + "openproject_revocation_status" + ] + } + } + """ + + + Scenario: reset the integration setup + When the administrator sends a DELETE request to the "setup" endpoint + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": ["status"], + "properties": { + "status": {"const": true} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + + + Scenario: non-admin user tries to reset the integration setup + Given user "Carol" has been created + When the user "Carol" sends a DELETE request to the "setup" endpoint + Then the HTTP status code should be "403" + + Scenario Outline: try to setup with incomplete team folder + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": , + "setup_app_password": + } + } + """ + Then the HTTP status code should be "400" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "invalid data"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + Examples: + | setup_project_folder | setup_app_password | + | true | false | + | false | true | + + + # this test wil not pass locally if your system already has a `OpenProject` user/group setup and 'OpenProjectNoAutomaticProjectFolders' group setup + Scenario: Set up whole integration with project folder and user app password + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": true, + "setup_app_password": true + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id", + "nextcloud_client_secret", + "openproject_user_app_password" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"}, + "nextcloud_client_secret": {"pattern": "[A-Za-z0-9]+"}, + "openproject_user_app_password": {"pattern": "[A-Za-z0-9]+"} + }, + "not": { + "required": ["openproject_revocation_status"] + } + } + """ + And user "OpenProject" should be present in the server + And group "OpenProject" should be present in the server + And user "OpenProject" should be the subadmin of the group "OpenProject" + And group "OpenProjectNoAutomaticProjectFolders" should be present in the server + And user "OpenProject" should be the subadmin of the group "OpenProjectNoAutomaticProjectFolders" + And groupfolder "OpenProject" should be present in the server + And groupfolder "OpenProject" should be assigned to the group "OpenProject" with all permissions + And groupfolder "OpenProject" should have advance permissions enabled + And groupfolder "OpenProject" should be managed by the user "OpenProject" + # the next step is only for the tests, because that user has a random password + Given the administrator has changed the password of "OpenProject" to the default testing password + And user "OpenProject" should have a folder called "OpenProject" + # folders inside the OpenProject folder can only be deleted/renamed by the OpenProject user + And user "Carol" has been created + And user "Carol" has been added to the group "OpenProject" + And user "OpenProject" has created folder "/OpenProject/project-abc" + Then user "Carol" should have a folder called "OpenProject/project-abc" + When user "Carol" deletes folder "/OpenProject/project-abc" + Then the HTTP status code should be 500 + When user "Carol" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" + Then the HTTP status code should be 500 + When user "OpenProject" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" + Then the HTTP status code should be 201 + When user "OpenProject" deletes folder "/OpenProject/project-123" + Then the HTTP status code should be 204 + + # folders 2 levels down inside the OpenProject folder can be deleted by any user even if the parent is also called "OpenProject" + Given user "OpenProject" has created folder "/OpenProject/OpenProject/project-abc" + When user "Carol" renames folder "/OpenProject/OpenProject/project-abc" to "/OpenProject/OpenProject/project-123" + Then the HTTP status code should be 201 + When user "Carol" deletes folder "/OpenProject/OpenProject/project-123" + Then the HTTP status code should be 204 + + # a user, who is not in the OpenProject group can delete/rename items inside a folder that is called OpenProject + Given user "Brian" has been created + And user "Brian" has created folder "/OpenProject/project-abc" + When user "Brian" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" + Then the HTTP status code should be 201 + When user "Brian" deletes folder "/OpenProject/project-123" + Then the HTTP status code should be 204 + + # check deleting / disabling the OpenProject user/group + When the administrator deletes the user "OpenProject" + Then the HTTP status code should be 400 + And user "OpenProject" should be present in the server + When the administrator deletes the group "OpenProject" + Then the HTTP status code should be 400 + And group "OpenProject" should be present in the server + When the administrator disables the user "OpenProject" + Then the HTTP status code should be 400 + + # resending setup request will fail + When the administrator sends a POST request to the "setup" endpoint with this data: + """ + { + "values" : { + "openproject_instance_url": "http://some-host.de", + "openproject_client_id": "the-client-id", + "openproject_client_secret": "the-client-secret", + "default_enable_navigation": false, + "default_enable_unified_search": false, + "setup_project_folder": true, + "setup_app_password": true + } + } + """ + Then the HTTP status code should be "409" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "The user \"OpenProject\" already exists"} + } + } + """ + + # sending a PATCH request with setup_project_folder=true will also fail + When the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values" : { + "setup_project_folder": true + } + } + """ + Then the HTTP status code should be "409" + And the data of the response should match + """ + { + "type": "object", + "required": ["error"], + "properties": { + "error": {"const": "The user \"OpenProject\" already exists"} + } + } + """ + + # we can make api request using the created app password for user "OpenProject" + When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using current app password + Then the HTTP status code should be "207" + + # this is to provide test coverage for issues like this + # https://community.openproject.org/projects/nextcloud-integration/work_packages/49621 + When a new browser session for "Openproject" starts + # but other values can be updated by sending a PATCH request + # also we can replace old app password by sending PATCH request to get new user app password + And the administrator sends a PATCH request to the "setup" endpoint with this data: + """ + { + "values" : { + "default_enable_navigation": true, + "setup_app_password": true + } + } + """ + Then the HTTP status code should be "200" + And the data of the response should match + """ + { + "type": "object", + "required": [ + "nextcloud_oauth_client_name", + "openproject_redirect_uri", + "nextcloud_client_id" + ], + "properties": { + "nextcloud_oauth_client_name": {"const": "OpenProject client"}, + "openproject_redirect_uri": {"pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, + "nextcloud_client_id": {"pattern": "[A-Za-z0-9]+"}, + "openproject_user_app_password": {"pattern": "[A-Za-z0-9]+"} + } + } + """ + And the newly generated app password should be different from the previous one + + # user "OpenProject" can make api request using the newly created app password + When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using new app password + Then the HTTP status code should be "207" + + # user "OpenProject" cannot make api request using the old app password + When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using old app password + Then the HTTP status code should be "401" diff --git a/tests/acceptance/features/api/setup.feature b/tests/acceptance/features/api/setup.feature deleted file mode 100644 index 8c3137240..000000000 --- a/tests/acceptance/features/api/setup.feature +++ /dev/null @@ -1,872 +0,0 @@ -# SPDX-FileCopyrightText: 2022-2024 Jankari Tech Pvt. Ltd. -# SPDX-License-Identifier: AGPL-3.0-or-later -Feature: setup the integration through an API - - Scenario: valid setup without team folder - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : { - "openproject_instance_url": "http://some-host.de", - "openproject_client_id": "the-client-id", - "openproject_client_secret": "the-client-secret", - "default_enable_navigation": false, - "default_enable_unified_search": false, - "setup_project_folder": false, - "setup_app_password": false - } - } - """ - Then the HTTP status code should be "200" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "nextcloud_oauth_client_name", - "openproject_redirect_uri", - "nextcloud_client_id", - "nextcloud_client_secret" - ], - "properties": { - "nextcloud_oauth_client_name": {"type": "string", "pattern": "^OpenProject client$"}, - "openproject_redirect_uri": {"type": "string", "pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, - "nextcloud_client_id": {"type": "string", "pattern": "[A-Za-z0-9]+"}, - "nextcloud_client_secret": {"type": "string", "pattern": "[A-Za-z0-9]+"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - - Scenario Outline: setup with invalid data - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : { - "openproject_instance_url": , - "openproject_client_id": , - "openproject_client_secret": , - "default_enable_navigation": , - "default_enable_unified_search": , - "setup_project_folder": , - "setup_app_password": - } - } - """ - Then the HTTP status code should be "400" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^invalid data$"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - Examples: - | instance_url | openproject_client_id | openproject_client_secret | enable_navigation | enable_unified_search | setup_project_folder | setup_app_password | - | null | null | null | null | null | null | null | - | null | "id" | "secret" | false | false | false | false | - | "http://some-host.de" | null | "secret" | false | false | false | false | - | "http://some-host.de" | "id" | null | false | false | false | false | - | "http://some-host.de" | "id" | "secret" | null | false | false | false | - | "http://some-host.de" | "id" | "secret" | true | null | "" | "" | - | "" | "" | "" | "" | "" | false | false | - | "" | "id" | "secret" | false | false | false | false | - | "http://some-host.de" | "" | "secret" | false | false | false | false | - | "http://some-host.de" | "id" | "" | false | false | false | false | - | "http://some-host.de" | "id" | "secret" | "" | false | false | false | - | "http://some-host.de" | "id" | "secret" | true | "" | false | false | - | "ftp://somehost.de" | "the-id" | "secret" | true | false | "a string" | "a string" | - | "http://somehost.de" | false | "secret" | true | false | false | false | - | "http://somehost.de" | "id" | false | true | false | false | false | - | "http://somehost.de" | "the-id" | "secret" | "a string" | false | false | false | - | "http://somehost.de" | "the-id" | "secret" | false | "a string" | false | false | - - - Scenario: setup with invalid keys - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : { - "instance_url": "http://openproject.de", - "client_id": "the-client" - } - } - """ - Then the HTTP status code should be "400" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^invalid key"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - - - Scenario Outline: setup with missing keys - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : - } - """ - Then the HTTP status code should be "400" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^invalid key"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - Examples: - | values | - | {"openproject_client_id": "the-client-id", "openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false} | - | {"openproject_instance_url": "http://some-host.de","openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false, "setup_project_folder": false, "setup_app_password": false } | - | {"openproject_instance_url": "http://some-host.de", "openproject_client_id": "the-client-id", "default_enable_navigation": false, "default_enable_unified_search": false, "setup_project_folder": false, "setup_app_password": false} | - | {"openproject_instance_url": "http://some-host.de", "openproject_client_id": "the-client-id", "openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "setup_project_folder": false , "setup_app_password": false} | - - - Scenario Outline: setup with data that is not even valid JSON - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - - """ - Then the HTTP status code should be "400" or "500" - Examples: - | data | - | "{}" | - | {"values": {"openproject_instance_url": "http://some-host.de","openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false,}} | - | {"values": {"openproject_instance_url": "http://some-host.de","openproject_client_secret": "the-client-secret", "default_enable_navigation": false, "default_enable_unified_search": false} | - | {"values": | - | "values" | - | "" | - - Scenario: non-admin user tries to create the setup without project folder - Given user "Carol" has been created - When the user "Carol" sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : { - "openproject_instance_url": "http://some-host.de", - "openproject_client_id": "the-client-id", - "openproject_client_secret": "the-client-secret", - "default_enable_navigation": false, - "default_enable_unified_search": false, - "setup_project_folder": false, - "setup_app_password": false - } - } - """ - Then the HTTP status code should be "403" - And the data of the response should match - """" - { - "type": "object", - "not": { - "required": [ - "nextcloud_oauth_client_name", - "openproject_redirect_uri", - "nextcloud_client_id", - "nextcloud_client_secret" - ] - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - - Scenario Outline: valid update - When the administrator sends a PATCH request to the "setup" endpoint with this data: - """ - { - "values": { - "": - } - } - """ - Then the HTTP status code should be "200" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "nextcloud_oauth_client_name", - "openproject_redirect_uri", - "nextcloud_client_id" - ], - "properties": { - "nextcloud_oauth_client_name": {"type": "string", "pattern": "^OpenProject client$"}, - "openproject_redirect_uri": {"type": "string", "pattern": "^http:\/\/.*\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, - "nextcloud_client_id": {"type": "string", "pattern": "[A-Za-z0-9]+"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - Examples: - | key | value | - | openproject_instance_url | "http://some-host.de" | - | openproject_client_id | "client-value" | - | openproject_client_secret | "secret-value" | - | default_enable_navigation | false | - | default_enable_unified_search | true | - - - Scenario Outline: valid update of multiple values at once - When the administrator sends a PATCH request to the "setup" endpoint with this data: - """ - { - "values": { - "": , - "": - } - } - """ - Then the HTTP status code should be "200" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "nextcloud_oauth_client_name", - "openproject_redirect_uri", - "nextcloud_client_id" - ], - "properties": { - "nextcloud_oauth_client_name": {"type": "string", "pattern": "^OpenProject client$"}, - "openproject_redirect_uri": {"type": "string", "pattern": "^http:\/\/.*\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, - "nextcloud_client_id": {"type": "string", "pattern": "[A-Za-z0-9]+"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - Examples: - | key1 | value1 | key2 | value2 | - | openproject_instance_url | "http://some-host.de" | openproject_client_id | "client-value" | - | openproject_client_secret | "secret-value" | openproject_client_id | "client-value" | - | openproject_client_secret | "secret-value" | default_enable_navigation | false | - | default_enable_navigation | false | default_enable_unified_search | false | - - - Scenario Outline: update one value with invalid data - When the administrator sends a PATCH request to the "setup" endpoint with this data: - """ - { - "values": { - "": - } - } - """ - Then the HTTP status code should be "400" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^$"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - Examples: - | key | value | error-message | - | openproject_instance_url | null | invalid data | - | openproject_instance_url | "" | invalid data | - | openproject_instance_url | false | invalid data | - | openproject_client_id | null | invalid data | - | openproject_client_id | "" | invalid data | - | openproject_client_id | false | invalid data | - | openproject_client_secret | null | invalid data | - | openproject_client_secret | "" | invalid data | - | openproject_client_secret | false | invalid data | - | default_enable_navigation | null | invalid data | - | default_enable_navigation | "" | invalid data | - | default_enable_navigation | "string" | invalid data | - | default_enable_unified_search | null | invalid data | - | default_enable_unified_search | "" | invalid data | - | default_enable_unified_search | "string" | invalid data | - | instance_url | "http://op.de" | invalid key | - - - Scenario Outline: update of multiple values where at least one has invalid data - When the administrator sends a PATCH request to the "setup" endpoint with this data: - """ - { - "values": { - "": , - "": - } - } - """ - Then the HTTP status code should be "400" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^invalid data$"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - Examples: - | key1 | value1 | key2 | value2 | - | openproject_instance_url | "http://some-host.de" | openproject_client_id | null | - | openproject_instance_url | "ftp://some-host.de" | openproject_client_id | "some id" | - | openproject_client_secret | "" | openproject_client_id | "client-value" | - | openproject_client_secret | "secret" | openproject_client_id | false | - | openproject_client_secret | "secret-value" | default_enable_navigation | "string" | - | default_enable_navigation | null | default_enable_unified_search | false | - - - Scenario Outline: with data that is not even valid JSON - When the administrator sends a PATCH request to the "setup" endpoint with this data: - """ - - """ - Then the HTTP status code should be "400" or "500" - Examples: - | data | - | { "values": { "openproject_instance_url": "http://some-host.de"} }} | - | { "values": { "openproject_instance_url": "http://some-host.de"} | - | "values": { "openproject_instance_url": "http://some-host.de"} } | - | { "values": | - | "{}" | - | "" | - - - Scenario: non-admin tries to update the setup - Given user "Carol" has been created - When the user "Carol" sends a PATCH request to the "setup" endpoint with this data: - """ - { - "values": { - "openproject_instance_url": "http://some-host.de" - } - } - """ - Then the HTTP status code should be "403" - And the data of the response should match - """" - { - "type": "object", - "not": { - "required": [ - "nextcloud_oauth_client_name", - "openproject_redirect_uri", - "nextcloud_client_id" - ] - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - - - Scenario: Reset the integration - When the administrator sends a DELETE request to the "setup" endpoint - Then the HTTP status code should be "200" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "status" - ], - "properties": { - "status": {"type": "boolean", "enum": [ true ]} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - - - Scenario: Trying to reset the integration as non-admin - Given user "Carol" has been created - When the user "Carol" sends a DELETE request to the "setup" endpoint - Then the HTTP status code should be "403" - - Scenario Outline: Trying to setup whole integration, without project folder/user app password and vice-versa - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : { - "openproject_instance_url": "http://some-host.de", - "openproject_client_id": "the-client-id", - "openproject_client_secret": "the-client-secret", - "default_enable_navigation": false, - "default_enable_unified_search": false, - "setup_project_folder": , - "setup_app_password": - } - } - """ - Then the HTTP status code should be "400" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^invalid data$"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - Examples: - | setup_project_folder | setup_app_password | - | true | false | - | false | true | - - - # this test wil not pass locally if your system already has a `OpenProject` user/group setup and 'OpenProjectNoAutomaticProjectFolders' group setup - Scenario: Set up whole integration with project folder and user app password - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : { - "openproject_instance_url": "http://some-host.de", - "openproject_client_id": "the-client-id", - "openproject_client_secret": "the-client-secret", - "default_enable_navigation": false, - "default_enable_unified_search": false, - "setup_project_folder": true, - "setup_app_password": true - } - } - """ - Then the HTTP status code should be "200" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "nextcloud_oauth_client_name", - "openproject_redirect_uri", - "nextcloud_client_id", - "nextcloud_client_secret", - "openproject_user_app_password" - ], - "properties": { - "nextcloud_oauth_client_name": {"type": "string", "pattern": "^OpenProject client$"}, - "openproject_redirect_uri": {"type": "string", "pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, - "nextcloud_client_id": {"type": "string", "pattern": "[A-Za-z0-9]+"}, - "nextcloud_client_secret": {"type": "string", "pattern": "[A-Za-z0-9]+"}, - "openproject_user_app_password": {"type": "string", "pattern": "[A-Za-z0-9]+"} - }, - "not": { - "required": [ - "openproject_revocation_status" - ] - } - } - """ - And user "OpenProject" should be present in the server - And group "OpenProject" should be present in the server - And user "OpenProject" should be the subadmin of the group "OpenProject" - And group "OpenProjectNoAutomaticProjectFolders" should be present in the server - And user "OpenProject" should be the subadmin of the group "OpenProjectNoAutomaticProjectFolders" - And groupfolder "OpenProject" should be present in the server - And groupfolder "OpenProject" should be assigned to the group "OpenProject" with all permissions - And groupfolder "OpenProject" should have advance permissions enabled - And groupfolder "OpenProject" should be managed by the user "OpenProject" - # the next step is only for the tests, because that user has a random password - Given the administrator has changed the password of "OpenProject" to the default testing password - And user "OpenProject" should have a folder called "OpenProject" - # folders inside the OpenProject folder can only be deleted/renamed by the OpenProject user - And user "Carol" has been created - And user "Carol" has been added to the group "OpenProject" - And user "OpenProject" has created folder "/OpenProject/project-abc" - Then user "Carol" should have a folder called "OpenProject/project-abc" - When user "Carol" deletes folder "/OpenProject/project-abc" - Then the HTTP status code should be 500 - When user "Carol" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" - Then the HTTP status code should be 500 - When user "OpenProject" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" - Then the HTTP status code should be 201 - When user "OpenProject" deletes folder "/OpenProject/project-123" - Then the HTTP status code should be 204 - - # folders 2 levels down inside the OpenProject folder can be deleted by any user even if the parent is also called "OpenProject" - Given user "OpenProject" has created folder "/OpenProject/OpenProject/project-abc" - When user "Carol" renames folder "/OpenProject/OpenProject/project-abc" to "/OpenProject/OpenProject/project-123" - Then the HTTP status code should be 201 - When user "Carol" deletes folder "/OpenProject/OpenProject/project-123" - Then the HTTP status code should be 204 - - # a user, who is not in the OpenProject group can delete/rename items inside a folder that is called OpenProject - Given user "Brian" has been created - And user "Brian" has created folder "/OpenProject/project-abc" - When user "Brian" renames folder "/OpenProject/project-abc" to "/OpenProject/project-123" - Then the HTTP status code should be 201 - When user "Brian" deletes folder "/OpenProject/project-123" - Then the HTTP status code should be 204 - - # check deleting / disabling the OpenProject user/group - When the administrator deletes the user "OpenProject" - Then the HTTP status code should be 400 - And user "OpenProject" should be present in the server - When the administrator deletes the group "OpenProject" - Then the HTTP status code should be 400 - And group "OpenProject" should be present in the server - When the administrator disables the user "OpenProject" - Then the HTTP status code should be 400 - - # resending setup request will fail - When the administrator sends a POST request to the "setup" endpoint with this data: - """ - { - "values" : { - "openproject_instance_url": "http://some-host.de", - "openproject_client_id": "the-client-id", - "openproject_client_secret": "the-client-secret", - "default_enable_navigation": false, - "default_enable_unified_search": false, - "setup_project_folder": true, - "setup_app_password": true - } - } - """ - Then the HTTP status code should be "409" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^The user \"OpenProject\" already exists$"} - } - } - """ - - # sending a PATCH request with setup_project_folder=true will also fail - When the administrator sends a PATCH request to the "setup" endpoint with this data: - """ - { - "values" : { - "setup_project_folder": true - } - } - """ - Then the HTTP status code should be "409" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": {"type": "string", "pattern": "^The user \"OpenProject\" already exists$"} - } - } - """ - - # we can make api request using the created app password for user "OpenProject" - When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using current app password - Then the HTTP status code should be "207" - - # this is to provide test coverage for issues like this - # https://community.openproject.org/projects/nextcloud-integration/work_packages/49621 - When a new browser session for "Openproject" starts - # but other values can be updated by sending a PATCH request - # also we can replace old app password by sending PATCH request to get new user app password - And the administrator sends a PATCH request to the "setup" endpoint with this data: - """ - { - "values" : { - "default_enable_navigation": true, - "setup_app_password": true - } - } - """ - Then the HTTP status code should be "200" - And the data of the response should match - """" - { - "type": "object", - "required": [ - "nextcloud_oauth_client_name", - "openproject_redirect_uri", - "nextcloud_client_id" - ], - "properties": { - "nextcloud_oauth_client_name": {"type": "string", "pattern": "^OpenProject client$"}, - "openproject_redirect_uri": {"type": "string", "pattern": "^http:\/\/some-host.de\/oauth_clients\/[A-Za-z0-9]+\/callback$"}, - "nextcloud_client_id": {"type": "string", "pattern": "[A-Za-z0-9]+"}, - "openproject_user_app_password": {"type": "string", "pattern": "[A-Za-z0-9]+"} - } - } - """ - And the newly generated app password should be different from the previous one - - # user "OpenProject" can make api request using the newly created app password - When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using new app password - Then the HTTP status code should be "207" - - # user "OpenProject" cannot make api request using the old app password - When user "OpenProject" sends a "PROPFIND" request to "/remote.php/webdav" using old app password - Then the HTTP status code should be "401" - - - Scenario: check version of uploaded file inside a team folder - Given user "Carol" has been created - And user "Carol" has been added to the group "OpenProject" - And user "Carol" has created folder "/OpenProject/OpenProject/project-demo" - And user "Carol" got a direct-upload token for "/OpenProject/OpenProject/project-demo" - When an anonymous user sends a multipart form data POST request to the "direct-upload/%last-created-direct-upload-token%" endpoint with: - | file_name | file.txt | - | data | 0987654321 | - Then the version folder of file "/OpenProject/OpenProject/project-demo/file.txt" for user "Carol" should contain "1" element - When user "Carol" deletes folder "/OpenProject/OpenProject/project-demo" - Then the HTTP status code should be 204 - - - Scenario: check version of uploaded file after an update inside a team folder - Given user "Carol" has been created - And user "Carol" has been added to the group "OpenProject" - And user "OpenProject" has created folder "/OpenProject/OpenProject/project-test" - And user "Carol" has uploaded file with content "0123456789" to "/OpenProject/OpenProject/project-test/file.txt" - And user "Carol" got a direct-upload token for "/OpenProject/OpenProject/project-test" - When an anonymous user sends a multipart form data POST request to the "direct-upload/%last-created-direct-upload-token%" endpoint with: - | file_name | file.txt | - | data | 1234567890 | - | overwrite | true | - Then the HTTP status code should be "200" - And the version folder of file "/OpenProject/OpenProject/project-test/file.txt" for user "Carol" should contain "2" elements - When user "Carol" deletes folder "/OpenProject/OpenProject/project-test" - Then the HTTP status code should be 204 - - - Scenario: check OpenProjectNoAutomaticProjectFolders group after user is removed from OpenProject group (removed by group admin) - Given user "Carol" has been created - And user "Carol" has been added to the group "OpenProject" - When the group admin "OpenProject" removes the user "Carol" from group "OpenProject" - Then the HTTP status code should be 200 - And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" - And user "Carol" should not belong to group "OpenProject" - - - Scenario: user not in OpenProject group is removed from another group (removed by group admin) - Given group "grp1" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Carol | grp1 | - | OpenProject | grp1 | - And user "OpenProject" has been assigned the role group admin of group "grp1" - # group admin cannot remove a user from their last remaining group - When the group admin "OpenProject" removes the user "Carol" from group "grp1" - Then the HTTP status code should be 400 - And user "Carol" should belong to group "grp1" - And user "Carol" should not belong to group "OpenProjectNoAutomaticProjectFolders" - - - Scenario: user not in OpenProject group but has multiple group memberships is removed from one group (removed by group admin) - Given group "grp1" has been created - And group "grp2" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | grp2 | - | OpenProject | grp1 | - And user "OpenProject" has been assigned the role group admin of group "grp1" - And user "OpenProject" has been assigned the role group admin of group "grp2" - When the group admin "OpenProject" removes the user "Carol" from group "grp1" - Then the HTTP status code should be 200 - And the following users should not belong to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProjectNoAutomaticProjectFolders | - And user "Carol" should belong to group "grp2" - - - Scenario: user in OpenProject and other groups (removed by group admin) - Given group "grp1" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProject | - | OpenProject | grp1 | - And user "OpenProject" has been assigned the role group admin of group "grp1" - When the group admin "OpenProject" removes the user "Carol" from group "grp1" - Then the HTTP status code should be 200 - And the following users should not belong to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProjectNoAutomaticProjectFolders | - # A user cannot be removed from their last group - Given user "Carol" has been added to the group "grp1" - When the group admin "OpenProject" removes the user "Carol" from group "OpenProject" - Then the HTTP status code should be 200 - And user "Carol" should belong to group "grp1" - And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" - And user "Carol" should not belong to group "OpenProject" - - - Scenario: multiple user in OpenProject groups and only one gets removed (removed by group admin) - Given user "Alex" has been created - And user "Brian" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Alex | OpenProject | - | Brian | OpenProject | - | Carol | OpenProject | - When the group admin "OpenProject" removes the user "Carol" from group "OpenProject" - Then the HTTP status code should be 200 - And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" - And the following users should not belong to the following groups - | username | groupname | - | Alex | OpenProjectNoAutomaticProjectFolders | - | Brian | OpenProjectNoAutomaticProjectFolders | - | Carol | OpenProject | - - - Scenario: user is in multiple groups including OpenProject and is removed from another group (removed by group admin) - Given group "grp1" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Carol | OpenProject | - | Carol | grp1 | - | OpenProject | grp1 | - And user "OpenProject" has been assigned the role group admin of group "grp1" - When the group admin "OpenProject" removes the user "Carol" from group "grp1" - Then the HTTP status code should be 200 - And the following users should not belong to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProjectNoAutomaticProjectFolders | - And user "Carol" should belong to group "OpenProject" - - - Scenario: user not in OpenProject group is removed from another group (removed by admin) - Given group "grp1" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Carol | grp1 | - | OpenProject | grp1 | - And user "OpenProject" has been assigned the role group admin of group "grp1" - When the administrator removes the user "Carol" from group "grp1" - Then the HTTP status code should be 200 - And the following users should not belong to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProjectNoAutomaticProjectFolders | - - - Scenario: user not in OpenProject group but has multiple group memberships is removed from one group (removed by admin) - Given group "grp1" has been created - And group "grp2" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | grp2 | - | OpenProject | grp1 | - And user "OpenProject" has been assigned the role group admin of group "grp1" - And user "OpenProject" has been assigned the role group admin of group "grp2" - When the administrator removes the user "Carol" from group "grp1" - Then the HTTP status code should be 200 - And the following users should not belong to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProjectNoAutomaticProjectFolders | - And user "Carol" should belong to group "grp2" - - - Scenario: user in OpenProject and other groups (removed by admin) - Given group "grp1" has been created - And user "Carol" has been created - And the following users have been added to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProject | - | OpenProject | grp1 | - And user "OpenProject" has been assigned the role group admin of group "grp1" - When the administrator removes the user "Carol" from group "grp1" - Then the HTTP status code should be 200 - And the following users should not belong to the following groups - | username | groupname | - | Carol | grp1 | - | Carol | OpenProjectNoAutomaticProjectFolders | - When the administrator removes the user "Carol" from group "OpenProject" - Then the HTTP status code should be 200 - And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" - And user "Carol" should not belong to group "OpenProject" diff --git a/tests/acceptance/features/api/teamFolder.feature b/tests/acceptance/features/api/teamFolder.feature new file mode 100644 index 000000000..6e48604e8 --- /dev/null +++ b/tests/acceptance/features/api/teamFolder.feature @@ -0,0 +1,194 @@ +# SPDX-FileCopyrightText: 2026 Jankari Tech Pvt. Ltd. +# SPDX-License-Identifier: AGPL-3.0-or-later +Feature: setup the integration through an API + + + Scenario: check version of uploaded file inside a team folder + Given user "Carol" has been created + And user "Carol" has been added to the group "OpenProject" + And user "Carol" has created folder "/OpenProject/OpenProject/project-demo" + And user "Carol" got a direct-upload token for "/OpenProject/OpenProject/project-demo" + When an anonymous user sends a multipart form data POST request to the "direct-upload/%last-created-direct-upload-token%" endpoint with: + | file_name | file.txt | + | data | 0987654321 | + Then the version folder of file "/OpenProject/OpenProject/project-demo/file.txt" for user "Carol" should contain "1" element + When user "Carol" deletes folder "/OpenProject/OpenProject/project-demo" + Then the HTTP status code should be 204 + + + Scenario: check version of uploaded file after an update inside a team folder + Given user "Carol" has been created + And user "Carol" has been added to the group "OpenProject" + And user "OpenProject" has created folder "/OpenProject/OpenProject/project-test" + And user "Carol" has uploaded file with content "0123456789" to "/OpenProject/OpenProject/project-test/file.txt" + And user "Carol" got a direct-upload token for "/OpenProject/OpenProject/project-test" + When an anonymous user sends a multipart form data POST request to the "direct-upload/%last-created-direct-upload-token%" endpoint with: + | file_name | file.txt | + | data | 1234567890 | + | overwrite | true | + Then the HTTP status code should be "200" + And the version folder of file "/OpenProject/OpenProject/project-test/file.txt" for user "Carol" should contain "2" elements + When user "Carol" deletes folder "/OpenProject/OpenProject/project-test" + Then the HTTP status code should be 204 + + + Scenario: check OpenProjectNoAutomaticProjectFolders group after user is removed from OpenProject group (removed by group admin) + Given user "Carol" has been created + And user "Carol" has been added to the group "OpenProject" + When the group admin "OpenProject" removes the user "Carol" from group "OpenProject" + Then the HTTP status code should be 200 + And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" + And user "Carol" should not belong to group "OpenProject" + + + Scenario: user not in OpenProject group is removed from another group (removed by group admin) + Given group "grp1" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Carol | grp1 | + | OpenProject | grp1 | + And user "OpenProject" has been assigned the role group admin of group "grp1" + # group admin cannot remove a user from their last remaining group + When the group admin "OpenProject" removes the user "Carol" from group "grp1" + Then the HTTP status code should be 400 + And user "Carol" should belong to group "grp1" + And user "Carol" should not belong to group "OpenProjectNoAutomaticProjectFolders" + + + Scenario: user not in OpenProject group but has multiple group memberships is removed from one group (removed by group admin) + Given group "grp1" has been created + And group "grp2" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | grp2 | + | OpenProject | grp1 | + And user "OpenProject" has been assigned the role group admin of group "grp1" + And user "OpenProject" has been assigned the role group admin of group "grp2" + When the group admin "OpenProject" removes the user "Carol" from group "grp1" + Then the HTTP status code should be 200 + And the following users should not belong to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProjectNoAutomaticProjectFolders | + And user "Carol" should belong to group "grp2" + + + Scenario: user in OpenProject and other groups (removed by group admin) + Given group "grp1" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProject | + | OpenProject | grp1 | + And user "OpenProject" has been assigned the role group admin of group "grp1" + When the group admin "OpenProject" removes the user "Carol" from group "grp1" + Then the HTTP status code should be 200 + And the following users should not belong to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProjectNoAutomaticProjectFolders | + # A user cannot be removed from their last group + Given user "Carol" has been added to the group "grp1" + When the group admin "OpenProject" removes the user "Carol" from group "OpenProject" + Then the HTTP status code should be 200 + And user "Carol" should belong to group "grp1" + And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" + And user "Carol" should not belong to group "OpenProject" + + + Scenario: multiple user in OpenProject groups and only one gets removed (removed by group admin) + Given user "Alex" has been created + And user "Brian" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Alex | OpenProject | + | Brian | OpenProject | + | Carol | OpenProject | + When the group admin "OpenProject" removes the user "Carol" from group "OpenProject" + Then the HTTP status code should be 200 + And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" + And the following users should not belong to the following groups + | username | groupname | + | Alex | OpenProjectNoAutomaticProjectFolders | + | Brian | OpenProjectNoAutomaticProjectFolders | + | Carol | OpenProject | + + + Scenario: user is in multiple groups including OpenProject and is removed from another group (removed by group admin) + Given group "grp1" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Carol | OpenProject | + | Carol | grp1 | + | OpenProject | grp1 | + And user "OpenProject" has been assigned the role group admin of group "grp1" + When the group admin "OpenProject" removes the user "Carol" from group "grp1" + Then the HTTP status code should be 200 + And the following users should not belong to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProjectNoAutomaticProjectFolders | + And user "Carol" should belong to group "OpenProject" + + + Scenario: user not in OpenProject group is removed from another group (removed by admin) + Given group "grp1" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Carol | grp1 | + | OpenProject | grp1 | + And user "OpenProject" has been assigned the role group admin of group "grp1" + When the administrator removes the user "Carol" from group "grp1" + Then the HTTP status code should be 200 + And the following users should not belong to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProjectNoAutomaticProjectFolders | + + + Scenario: user not in OpenProject group but has multiple group memberships is removed from one group (removed by admin) + Given group "grp1" has been created + And group "grp2" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | grp2 | + | OpenProject | grp1 | + And user "OpenProject" has been assigned the role group admin of group "grp1" + And user "OpenProject" has been assigned the role group admin of group "grp2" + When the administrator removes the user "Carol" from group "grp1" + Then the HTTP status code should be 200 + And the following users should not belong to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProjectNoAutomaticProjectFolders | + And user "Carol" should belong to group "grp2" + + + Scenario: user in OpenProject and other groups (removed by admin) + Given group "grp1" has been created + And user "Carol" has been created + And the following users have been added to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProject | + | OpenProject | grp1 | + And user "OpenProject" has been assigned the role group admin of group "grp1" + When the administrator removes the user "Carol" from group "grp1" + Then the HTTP status code should be 200 + And the following users should not belong to the following groups + | username | groupname | + | Carol | grp1 | + | Carol | OpenProjectNoAutomaticProjectFolders | + When the administrator removes the user "Carol" from group "OpenProject" + Then the HTTP status code should be 200 + And user "Carol" should belong to group "OpenProjectNoAutomaticProjectFolders" + And user "Carol" should not belong to group "OpenProject"