diff --git a/CHANGELOG b/CHANGELOG index 20948a8db5e..398329b9f10 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ v 8.12.0 (unreleased) - Only check :can_resolve permission if the note is resolvable - Bump fog-aws to v0.11.0 to support ap-south-1 region - Add ability to fork to a specific namespace using API. (ritave) + - Allow to set request_access_enabled for groups and projects - Cleanup misalignments in Issue list view !6206 - Prune events older than 12 months. (ritave) - Prepend blank line to `Closes` message on merge request linked to issue (lukehowell) diff --git a/doc/api/groups.md b/doc/api/groups.md index 3e94e1e4efe..e81d6f9de4b 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -84,7 +84,8 @@ Parameters: "forks_count": 0, "open_issues_count": 3, "public_builds": true, - "shared_with_groups": [] + "shared_with_groups": [], + "request_access_enabled": false } ] ``` @@ -118,6 +119,7 @@ Example response: "visibility_level": 20, "avatar_url": null, "web_url": "https://gitlab.example.com/groups/twitter", + "request_access_enabled": false, "projects": [ { "id": 7, @@ -163,7 +165,8 @@ Example response: "forks_count": 0, "open_issues_count": 3, "public_builds": true, - "shared_with_groups": [] + "shared_with_groups": [], + "request_access_enabled": false }, { "id": 6, @@ -209,7 +212,8 @@ Example response: "forks_count": 0, "open_issues_count": 8, "public_builds": true, - "shared_with_groups": [] + "shared_with_groups": [], + "request_access_enabled": false } ], "shared_projects": [ @@ -289,6 +293,7 @@ Parameters: - `description` (optional) - The group's description - `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public. - `lfs_enabled` (optional) - Enable/disable Large File Storage (LFS) for the projects in this group +- `request_access_enabled` (optional) - Allow users to request member access. ## Transfer project to group @@ -319,6 +324,7 @@ PUT /groups/:id | `description` | string | no | The description of the group | | `visibility_level` | integer | no | The visibility level of the group. 0 for private, 10 for internal, 20 for public. | | `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group | +| `request_access_enabled` | boolean | no | Allow users to request member access. | ```bash curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/groups/5?name=Experimental" @@ -336,6 +342,7 @@ Example response: "visibility_level": 10, "avatar_url": null, "web_url": "http://gitlab.example.com/groups/h5bp", + "request_access_enabled": false, "projects": [ { "id": 9, @@ -380,7 +387,8 @@ Example response: "forks_count": 0, "open_issues_count": 3, "public_builds": true, - "shared_with_groups": [] + "shared_with_groups": [], + "request_access_enabled": false } ] } diff --git a/doc/api/projects.md b/doc/api/projects.md index fe3c8709d13..750ce1508df 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -85,7 +85,8 @@ Parameters: "runners_token": "b8547b1dc37721d05889db52fa2f02", "public_builds": true, "shared_with_groups": [], - "only_allow_merge_if_build_succeeds": false + "only_allow_merge_if_build_succeeds": false, + "request_access_enabled": false }, { "id": 6, @@ -146,7 +147,8 @@ Parameters: "runners_token": "b8547b1dc37721d05889db52fa2f02", "public_builds": true, "shared_with_groups": [], - "only_allow_merge_if_build_succeeds": false + "only_allow_merge_if_build_succeeds": false, + "request_access_enabled": false } ] ``` @@ -283,7 +285,8 @@ Parameters: "group_access_level": 10 } ], - "only_allow_merge_if_build_succeeds": false + "only_allow_merge_if_build_succeeds": false, + "request_access_enabled": false } ``` @@ -453,6 +456,7 @@ Parameters: - `public_builds` (optional) - `only_allow_merge_if_build_succeeds` (optional) - `lfs_enabled` (optional) +- `request_access_enabled` (optional) - Allow users to request member access. ### Create project for user @@ -480,6 +484,7 @@ Parameters: - `public_builds` (optional) - `only_allow_merge_if_build_succeeds` (optional) - `lfs_enabled` (optional) +- `request_access_enabled` (optional) - Allow users to request member access. ### Edit project @@ -508,6 +513,7 @@ Parameters: - `public_builds` (optional) - `only_allow_merge_if_build_succeeds` (optional) - `lfs_enabled` (optional) +- `request_access_enabled` (optional) - Allow users to request member access. On success, method returns 200 with the updated project. If parameters are invalid, 400 is returned. @@ -588,7 +594,8 @@ Example response: "star_count": 1, "public_builds": true, "shared_with_groups": [], - "only_allow_merge_if_build_succeeds": false + "only_allow_merge_if_build_succeeds": false, + "request_access_enabled": false } ``` @@ -655,7 +662,8 @@ Example response: "star_count": 0, "public_builds": true, "shared_with_groups": [], - "only_allow_merge_if_build_succeeds": false + "only_allow_merge_if_build_succeeds": false, + "request_access_enabled": false } ``` @@ -742,7 +750,8 @@ Example response: "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b", "public_builds": true, "shared_with_groups": [], - "only_allow_merge_if_build_succeeds": false + "only_allow_merge_if_build_succeeds": false, + "request_access_enabled": false } ``` @@ -829,7 +838,8 @@ Example response: "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b", "public_builds": true, "shared_with_groups": [], - "only_allow_merge_if_build_succeeds": false + "only_allow_merge_if_build_succeeds": false, + "request_access_enabled": false } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index bfee4b6c752..0235ba3d580 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -100,6 +100,7 @@ module API SharedGroup.represent(project.project_group_links.all, options) end expose :only_allow_merge_if_build_succeeds + expose :request_access_enabled end class Member < UserBasic @@ -125,6 +126,7 @@ module API expose :lfs_enabled?, as: :lfs_enabled expose :avatar_url expose :web_url + expose :request_access_enabled end class GroupDetail < Group diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 60ac9bdfa33..953fa474e88 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -23,18 +23,19 @@ module API # Create group. Available only for users who can create groups. # # Parameters: - # name (required) - The name of the group - # path (required) - The path of the group - # description (optional) - The description of the group - # visibility_level (optional) - The visibility level of the group - # lfs_enabled (optional) - Enable/disable LFS for the projects in this group + # name (required) - The name of the group + # path (required) - The path of the group + # description (optional) - The description of the group + # visibility_level (optional) - The visibility level of the group + # lfs_enabled (optional) - Enable/disable LFS for the projects in this group + # request_access_enabled (optional) - Allow users to request member access # Example Request: # POST /groups post do authorize! :create_group required_attributes! [:name, :path] - attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled] + attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled, :request_access_enabled] @group = Group.new(attrs) if @group.save @@ -48,18 +49,19 @@ module API # Update group. Available only for users who can administrate groups. # # Parameters: - # id (required) - The ID of a group - # path (optional) - The path of the group - # description (optional) - The description of the group - # visibility_level (optional) - The visibility level of the group - # lfs_enabled (optional) - Enable/disable LFS for the projects in this group + # id (required) - The ID of a group + # path (optional) - The path of the group + # description (optional) - The description of the group + # visibility_level (optional) - The visibility level of the group + # lfs_enabled (optional) - Enable/disable LFS for the projects in this group + # request_access_enabled (optional) - Allow users to request member access # Example Request: # PUT /groups/:id put ':id' do group = find_group(params[:id]) authorize! :admin_group, group - attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled] + attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled, :request_access_enabled] if ::Groups::UpdateService.new(group, current_user, attrs).execute present group, with: Entities::GroupDetail diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 644d836ed0b..5eb83c2c8f8 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -91,8 +91,8 @@ module API # Create new project # # Parameters: - # name (required) - name for new project - # description (optional) - short project description + # name (required) - name for new project + # description (optional) - short project description # issues_enabled (optional) # merge_requests_enabled (optional) # builds_enabled (optional) @@ -100,33 +100,35 @@ module API # snippets_enabled (optional) # container_registry_enabled (optional) # shared_runners_enabled (optional) - # namespace_id (optional) - defaults to user namespace - # public (optional) - if true same as setting visibility_level = 20 - # visibility_level (optional) - 0 by default + # namespace_id (optional) - defaults to user namespace + # public (optional) - if true same as setting visibility_level = 20 + # visibility_level (optional) - 0 by default # import_url (optional) # public_builds (optional) # lfs_enabled (optional) + # request_access_enabled (optional) - Allow users to request member access # Example Request # POST /projects post do required_attributes! [:name] - attrs = attributes_for_keys [:name, - :path, - :description, - :issues_enabled, - :merge_requests_enabled, - :builds_enabled, - :wiki_enabled, - :snippets_enabled, + attrs = attributes_for_keys [:builds_enabled, :container_registry_enabled, - :shared_runners_enabled, - :namespace_id, - :public, - :visibility_level, + :description, :import_url, - :public_builds, + :issues_enabled, + :lfs_enabled, + :merge_requests_enabled, + :name, + :namespace_id, :only_allow_merge_if_build_succeeds, - :lfs_enabled] + :path, + :public, + :public_builds, + :request_access_enabled, + :shared_runners_enabled, + :snippets_enabled, + :visibility_level, + :wiki_enabled] attrs = map_public_to_visibility_level(attrs) @project = ::Projects::CreateService.new(current_user, attrs).execute if @project.saved? @@ -143,10 +145,10 @@ module API # Create new project for a specified user. Only available to admin users. # # Parameters: - # user_id (required) - The ID of a user - # name (required) - name for new project - # description (optional) - short project description - # default_branch (optional) - 'master' by default + # user_id (required) - The ID of a user + # name (required) - name for new project + # description (optional) - short project description + # default_branch (optional) - 'master' by default # issues_enabled (optional) # merge_requests_enabled (optional) # builds_enabled (optional) @@ -154,31 +156,33 @@ module API # snippets_enabled (optional) # container_registry_enabled (optional) # shared_runners_enabled (optional) - # public (optional) - if true same as setting visibility_level = 20 + # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) # import_url (optional) # public_builds (optional) # lfs_enabled (optional) + # request_access_enabled (optional) - Allow users to request member access # Example Request # POST /projects/user/:user_id post "user/:user_id" do authenticated_as_admin! user = User.find(params[:user_id]) - attrs = attributes_for_keys [:name, - :description, + attrs = attributes_for_keys [:builds_enabled, :default_branch, - :issues_enabled, - :merge_requests_enabled, - :builds_enabled, - :wiki_enabled, - :snippets_enabled, - :shared_runners_enabled, - :public, - :visibility_level, + :description, :import_url, - :public_builds, + :issues_enabled, + :lfs_enabled, + :merge_requests_enabled, + :name, :only_allow_merge_if_build_succeeds, - :lfs_enabled] + :public, + :public_builds, + :request_access_enabled, + :shared_runners_enabled, + :snippets_enabled, + :visibility_level, + :wiki_enabled] attrs = map_public_to_visibility_level(attrs) @project = ::Projects::CreateService.new(user, attrs).execute if @project.saved? @@ -242,22 +246,23 @@ module API # Example Request # PUT /projects/:id put ':id' do - attrs = attributes_for_keys [:name, - :path, - :description, - :default_branch, - :issues_enabled, - :merge_requests_enabled, - :builds_enabled, - :wiki_enabled, - :snippets_enabled, + attrs = attributes_for_keys [:builds_enabled, :container_registry_enabled, - :shared_runners_enabled, - :public, - :visibility_level, - :public_builds, + :default_branch, + :description, + :issues_enabled, + :lfs_enabled, + :merge_requests_enabled, + :name, :only_allow_merge_if_build_succeeds, - :lfs_enabled] + :path, + :public, + :public_builds, + :request_access_enabled, + :shared_runners_enabled, + :snippets_enabled, + :visibility_level, + :wiki_enabled] attrs = map_public_to_visibility_level(attrs) authorize_admin_project authorize! :rename_project, user_project if attrs[:name].present? diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 4860b23c2ed..1f68ef1af8f 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -120,10 +120,11 @@ describe API::API, api: true do context 'when authenticated as the group owner' do it 'updates the group' do - put api("/groups/#{group1.id}", user1), name: new_group_name + put api("/groups/#{group1.id}", user1), name: new_group_name, request_access_enabled: true expect(response).to have_http_status(200) expect(json_response['name']).to eq(new_group_name) + expect(json_response['request_access_enabled']).to eq(true) end it 'returns 404 for a non existing group' do @@ -238,8 +239,14 @@ describe API::API, api: true do context "when authenticated as user with group permissions" do it "creates group" do - post api("/groups", user3), attributes_for(:group) + group = attributes_for(:group, { request_access_enabled: false }) + + post api("/groups", user3), group expect(response).to have_http_status(201) + + expect(json_response["name"]).to eq(group[:name]) + expect(json_response["path"]).to eq(group[:path]) + expect(json_response["request_access_enabled"]).to eq(group[:request_access_enabled]) end it "does not create group, duplicate" do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 28aa56e8644..192c7d14c13 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -225,7 +225,8 @@ describe API::API, api: true do issues_enabled: false, merge_requests_enabled: false, wiki_enabled: false, - only_allow_merge_if_build_succeeds: false + only_allow_merge_if_build_succeeds: false, + request_access_enabled: true }) post api('/projects', user), project @@ -352,7 +353,8 @@ describe API::API, api: true do description: FFaker::Lorem.sentence, issues_enabled: false, merge_requests_enabled: false, - wiki_enabled: false + wiki_enabled: false, + request_access_enabled: true }) post api("/projects/user/#{user.id}", admin), project @@ -887,6 +889,15 @@ describe API::API, api: true do expect(json_response['message']['name']).to eq(['has already been taken']) end + it 'updates request_access_enabled' do + project_param = { request_access_enabled: false } + + put api("/projects/#{project.id}", user), project_param + + expect(response).to have_http_status(200) + expect(json_response['request_access_enabled']).to eq(false) + end + it 'updates path & name to existing path & name in different namespace' do project_param = { path: project4.path, name: project4.name } put api("/projects/#{project3.id}", user), project_param @@ -948,7 +959,8 @@ describe API::API, api: true do wiki_enabled: true, snippets_enabled: true, merge_requests_enabled: true, - description: 'new description' } + description: 'new description', + request_access_enabled: true } put api("/projects/#{project.id}", user3), project_param expect(response).to have_http_status(403) end