Commit 0d72e121 authored by Rémy Coutable's avatar Rémy Coutable Committed by Ruben Davila

Merge branch 'issue_20978' into 'master'

Allow to set request_access_enabled for groups and projects using API

Closes #20978

See merge request !6359
parent 6fad5c49
...@@ -5,6 +5,7 @@ v 8.12.0 (unreleased) ...@@ -5,6 +5,7 @@ v 8.12.0 (unreleased)
- Only check :can_resolve permission if the note is resolvable - Only check :can_resolve permission if the note is resolvable
- Bump fog-aws to v0.11.0 to support ap-south-1 region - Bump fog-aws to v0.11.0 to support ap-south-1 region
- Add ability to fork to a specific namespace using API. (ritave) - 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 - Cleanup misalignments in Issue list view !6206
- Only create a protected branch upon a push to a new branch if a rule for that branch doesn't exist - Only create a protected branch upon a push to a new branch if a rule for that branch doesn't exist
- Prune events older than 12 months. (ritave) - Prune events older than 12 months. (ritave)
......
...@@ -84,7 +84,8 @@ Parameters: ...@@ -84,7 +84,8 @@ Parameters:
"forks_count": 0, "forks_count": 0,
"open_issues_count": 3, "open_issues_count": 3,
"public_builds": true, "public_builds": true,
"shared_with_groups": [] "shared_with_groups": [],
"request_access_enabled": false
} }
] ]
``` ```
...@@ -118,6 +119,7 @@ Example response: ...@@ -118,6 +119,7 @@ Example response:
"visibility_level": 20, "visibility_level": 20,
"avatar_url": null, "avatar_url": null,
"web_url": "https://gitlab.example.com/groups/twitter", "web_url": "https://gitlab.example.com/groups/twitter",
"request_access_enabled": false,
"projects": [ "projects": [
{ {
"id": 7, "id": 7,
...@@ -163,7 +165,8 @@ Example response: ...@@ -163,7 +165,8 @@ Example response:
"forks_count": 0, "forks_count": 0,
"open_issues_count": 3, "open_issues_count": 3,
"public_builds": true, "public_builds": true,
"shared_with_groups": [] "shared_with_groups": [],
"request_access_enabled": false
}, },
{ {
"id": 6, "id": 6,
...@@ -209,7 +212,8 @@ Example response: ...@@ -209,7 +212,8 @@ Example response:
"forks_count": 0, "forks_count": 0,
"open_issues_count": 8, "open_issues_count": 8,
"public_builds": true, "public_builds": true,
"shared_with_groups": [] "shared_with_groups": [],
"request_access_enabled": false
} }
], ],
"shared_projects": [ "shared_projects": [
...@@ -289,6 +293,7 @@ Parameters: ...@@ -289,6 +293,7 @@ Parameters:
- `description` (optional) - The group's description - `description` (optional) - The group's description
- `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public. - `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 - `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 ## Transfer project to group
...@@ -319,6 +324,7 @@ PUT /groups/:id ...@@ -319,6 +324,7 @@ PUT /groups/:id
| `description` | string | no | The description of the group | | `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. | | `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 | | `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 ```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/groups/5?name=Experimental" curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/groups/5?name=Experimental"
...@@ -336,6 +342,7 @@ Example response: ...@@ -336,6 +342,7 @@ Example response:
"visibility_level": 10, "visibility_level": 10,
"avatar_url": null, "avatar_url": null,
"web_url": "http://gitlab.example.com/groups/h5bp", "web_url": "http://gitlab.example.com/groups/h5bp",
"request_access_enabled": false,
"projects": [ "projects": [
{ {
"id": 9, "id": 9,
...@@ -380,7 +387,8 @@ Example response: ...@@ -380,7 +387,8 @@ Example response:
"forks_count": 0, "forks_count": 0,
"open_issues_count": 3, "open_issues_count": 3,
"public_builds": true, "public_builds": true,
"shared_with_groups": [] "shared_with_groups": [],
"request_access_enabled": false
} }
] ]
} }
......
...@@ -85,7 +85,8 @@ Parameters: ...@@ -85,7 +85,8 @@ Parameters:
"runners_token": "b8547b1dc37721d05889db52fa2f02", "runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true, "public_builds": true,
"shared_with_groups": [], "shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false "only_allow_merge_if_build_succeeds": false,
"request_access_enabled": false
}, },
{ {
"id": 6, "id": 6,
...@@ -146,7 +147,8 @@ Parameters: ...@@ -146,7 +147,8 @@ Parameters:
"runners_token": "b8547b1dc37721d05889db52fa2f02", "runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true, "public_builds": true,
"shared_with_groups": [], "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: ...@@ -283,7 +285,8 @@ Parameters:
"group_access_level": 10 "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: ...@@ -453,6 +456,7 @@ Parameters:
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional) - `lfs_enabled` (optional)
- `request_access_enabled` (optional) - Allow users to request member access.
### Create project for user ### Create project for user
...@@ -480,6 +484,7 @@ Parameters: ...@@ -480,6 +484,7 @@ Parameters:
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional) - `lfs_enabled` (optional)
- `request_access_enabled` (optional) - Allow users to request member access.
### Edit project ### Edit project
...@@ -508,6 +513,7 @@ Parameters: ...@@ -508,6 +513,7 @@ Parameters:
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (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 On success, method returns 200 with the updated project. If parameters are
invalid, 400 is returned. invalid, 400 is returned.
...@@ -588,7 +594,8 @@ Example response: ...@@ -588,7 +594,8 @@ Example response:
"star_count": 1, "star_count": 1,
"public_builds": true, "public_builds": true,
"shared_with_groups": [], "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: ...@@ -655,7 +662,8 @@ Example response:
"star_count": 0, "star_count": 0,
"public_builds": true, "public_builds": true,
"shared_with_groups": [], "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: ...@@ -742,7 +750,8 @@ Example response:
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b", "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true, "public_builds": true,
"shared_with_groups": [], "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: ...@@ -829,7 +838,8 @@ Example response:
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b", "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true, "public_builds": true,
"shared_with_groups": [], "shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false "only_allow_merge_if_build_succeeds": false,
"request_access_enabled": false
} }
``` ```
......
...@@ -100,6 +100,7 @@ module API ...@@ -100,6 +100,7 @@ module API
SharedGroup.represent(project.project_group_links.all, options) SharedGroup.represent(project.project_group_links.all, options)
end end
expose :only_allow_merge_if_build_succeeds expose :only_allow_merge_if_build_succeeds
expose :request_access_enabled
end end
class Member < UserBasic class Member < UserBasic
...@@ -125,6 +126,7 @@ module API ...@@ -125,6 +126,7 @@ module API
expose :lfs_enabled?, as: :lfs_enabled expose :lfs_enabled?, as: :lfs_enabled
expose :avatar_url expose :avatar_url
expose :web_url expose :web_url
expose :request_access_enabled
end end
class GroupDetail < Group class GroupDetail < Group
......
...@@ -23,18 +23,19 @@ module API ...@@ -23,18 +23,19 @@ module API
# Create group. Available only for users who can create groups. # Create group. Available only for users who can create groups.
# #
# Parameters: # Parameters:
# name (required) - The name of the group # name (required) - The name of the group
# path (required) - The path of the group # path (required) - The path of the group
# description (optional) - The description of the group # description (optional) - The description of the group
# visibility_level (optional) - The visibility level of the group # visibility_level (optional) - The visibility level of the group
# lfs_enabled (optional) - Enable/disable LFS for the projects in this 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: # Example Request:
# POST /groups # POST /groups
post do post do
authorize! :create_group authorize! :create_group
required_attributes! [:name, :path] 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) @group = Group.new(attrs)
if @group.save if @group.save
...@@ -48,18 +49,19 @@ module API ...@@ -48,18 +49,19 @@ module API
# Update group. Available only for users who can administrate groups. # Update group. Available only for users who can administrate groups.
# #
# Parameters: # Parameters:
# id (required) - The ID of a group # id (required) - The ID of a group
# path (optional) - The path of the group # path (optional) - The path of the group
# description (optional) - The description of the group # description (optional) - The description of the group
# visibility_level (optional) - The visibility level of the group # visibility_level (optional) - The visibility level of the group
# lfs_enabled (optional) - Enable/disable LFS for the projects in this 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: # Example Request:
# PUT /groups/:id # PUT /groups/:id
put ':id' do put ':id' do
group = find_group(params[:id]) group = find_group(params[:id])
authorize! :admin_group, group 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 if ::Groups::UpdateService.new(group, current_user, attrs).execute
present group, with: Entities::GroupDetail present group, with: Entities::GroupDetail
......
...@@ -91,8 +91,8 @@ module API ...@@ -91,8 +91,8 @@ module API
# Create new project # Create new project
# #
# Parameters: # Parameters:
# name (required) - name for new project # name (required) - name for new project
# description (optional) - short project description # description (optional) - short project description
# issues_enabled (optional) # issues_enabled (optional)
# merge_requests_enabled (optional) # merge_requests_enabled (optional)
# builds_enabled (optional) # builds_enabled (optional)
...@@ -100,33 +100,35 @@ module API ...@@ -100,33 +100,35 @@ module API
# snippets_enabled (optional) # snippets_enabled (optional)
# container_registry_enabled (optional) # container_registry_enabled (optional)
# shared_runners_enabled (optional) # shared_runners_enabled (optional)
# namespace_id (optional) - defaults to user namespace # namespace_id (optional) - defaults to user namespace
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - 0 by default # visibility_level (optional) - 0 by default
# import_url (optional) # import_url (optional)
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional) # lfs_enabled (optional)
# request_access_enabled (optional) - Allow users to request member access
# Example Request # Example Request
# POST /projects # POST /projects
post do post do
required_attributes! [:name] required_attributes! [:name]
attrs = attributes_for_keys [:name, attrs = attributes_for_keys [:builds_enabled,
:path, :container_registry_enabled,
:description, :description,
:import_url,
:issues_enabled, :issues_enabled,
:lfs_enabled,
:merge_requests_enabled, :merge_requests_enabled,
:builds_enabled, :name,
:wiki_enabled,
:snippets_enabled,
:container_registry_enabled,
:shared_runners_enabled,
:namespace_id, :namespace_id,
:only_allow_merge_if_build_succeeds,
:path,
:public, :public,
:visibility_level,
:import_url,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds, :request_access_enabled,
:lfs_enabled] :shared_runners_enabled,
:snippets_enabled,
:visibility_level,
:wiki_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(current_user, attrs).execute @project = ::Projects::CreateService.new(current_user, attrs).execute
if @project.saved? if @project.saved?
...@@ -143,10 +145,10 @@ module API ...@@ -143,10 +145,10 @@ module API
# Create new project for a specified user. Only available to admin users. # Create new project for a specified user. Only available to admin users.
# #
# Parameters: # Parameters:
# user_id (required) - The ID of a user # user_id (required) - The ID of a user
# name (required) - name for new project # name (required) - name for new project
# description (optional) - short project description # description (optional) - short project description
# default_branch (optional) - 'master' by default # default_branch (optional) - 'master' by default
# issues_enabled (optional) # issues_enabled (optional)
# merge_requests_enabled (optional) # merge_requests_enabled (optional)
# builds_enabled (optional) # builds_enabled (optional)
...@@ -154,31 +156,33 @@ module API ...@@ -154,31 +156,33 @@ module API
# snippets_enabled (optional) # snippets_enabled (optional)
# container_registry_enabled (optional) # container_registry_enabled (optional)
# shared_runners_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) # visibility_level (optional)
# import_url (optional) # import_url (optional)
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional) # lfs_enabled (optional)
# request_access_enabled (optional) - Allow users to request member access
# Example Request # Example Request
# POST /projects/user/:user_id # POST /projects/user/:user_id
post "user/:user_id" do post "user/:user_id" do
authenticated_as_admin! authenticated_as_admin!
user = User.find(params[:user_id]) user = User.find(params[:user_id])
attrs = attributes_for_keys [:name, attrs = attributes_for_keys [:builds_enabled,
:description,
:default_branch, :default_branch,
:description,
:import_url,
:issues_enabled, :issues_enabled,
:lfs_enabled,
:merge_requests_enabled, :merge_requests_enabled,
:builds_enabled, :name,
:wiki_enabled, :only_allow_merge_if_build_succeeds,
:snippets_enabled,
:shared_runners_enabled,
:public, :public,
:visibility_level,
:import_url,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds, :request_access_enabled,
:lfs_enabled] :shared_runners_enabled,
:snippets_enabled,
:visibility_level,
:wiki_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(user, attrs).execute @project = ::Projects::CreateService.new(user, attrs).execute
if @project.saved? if @project.saved?
...@@ -242,22 +246,23 @@ module API ...@@ -242,22 +246,23 @@ module API
# Example Request # Example Request
# PUT /projects/:id # PUT /projects/:id
put ':id' do put ':id' do
attrs = attributes_for_keys [:name, attrs = attributes_for_keys [:builds_enabled,
:path, :container_registry_enabled,
:description,
:default_branch, :default_branch,
:description,
:issues_enabled, :issues_enabled,
:lfs_enabled,
:merge_requests_enabled, :merge_requests_enabled,
:builds_enabled, :name,
:wiki_enabled, :only_allow_merge_if_build_succeeds,
:snippets_enabled, :path,
:container_registry_enabled,
:shared_runners_enabled,
:public, :public,
:visibility_level,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds, :request_access_enabled,
:lfs_enabled] :shared_runners_enabled,
:snippets_enabled,
:visibility_level,
:wiki_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
authorize_admin_project authorize_admin_project
authorize! :rename_project, user_project if attrs[:name].present? authorize! :rename_project, user_project if attrs[:name].present?
......
...@@ -120,10 +120,11 @@ describe API::API, api: true do ...@@ -120,10 +120,11 @@ describe API::API, api: true do
context 'when authenticated as the group owner' do context 'when authenticated as the group owner' do
it 'updates the group' 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(response).to have_http_status(200)
expect(json_response['name']).to eq(new_group_name) expect(json_response['name']).to eq(new_group_name)
expect(json_response['request_access_enabled']).to eq(true)
end end
it 'returns 404 for a non existing group' do it 'returns 404 for a non existing group' do
...@@ -238,8 +239,14 @@ describe API::API, api: true do ...@@ -238,8 +239,14 @@ describe API::API, api: true do
context "when authenticated as user with group permissions" do context "when authenticated as user with group permissions" do
it "creates group" 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(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 end
it "does not create group, duplicate" do it "does not create group, duplicate" do
......
...@@ -225,7 +225,8 @@ describe API::API, api: true do ...@@ -225,7 +225,8 @@ describe API::API, api: true do
issues_enabled: false, issues_enabled: false,
merge_requests_enabled: false, merge_requests_enabled: false,
wiki_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 post api('/projects', user), project
...@@ -352,7 +353,8 @@ describe API::API, api: true do ...@@ -352,7 +353,8 @@ describe API::API, api: true do
description: FFaker::Lorem.sentence, description: FFaker::Lorem.sentence,
issues_enabled: false, issues_enabled: false,
merge_requests_enabled: false, merge_requests_enabled: false,
wiki_enabled: false wiki_enabled: false,
request_access_enabled: true
}) })
post api("/projects/user/#{user.id}", admin), project post api("/projects/user/#{user.id}", admin), project
...@@ -887,6 +889,15 @@ describe API::API, api: true do ...@@ -887,6 +889,15 @@ describe API::API, api: true do
expect(json_response['message']['name']).to eq(['has already been taken']) expect(json_response['message']['name']).to eq(['has already been taken'])
end 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 it 'updates path & name to existing path & name in different namespace' do
project_param = { path: project4.path, name: project4.name } project_param = { path: project4.path, name: project4.name }
put api("/projects/#{project3.id}", user), project_param put api("/projects/#{project3.id}", user), project_param
...@@ -948,7 +959,8 @@ describe API::API, api: true do ...@@ -948,7 +959,8 @@ describe API::API, api: true do
wiki_enabled: true, wiki_enabled: true,
snippets_enabled: true, snippets_enabled: true,
merge_requests_enabled: true, merge_requests_enabled: true,
description: 'new description' } description: 'new description',
request_access_enabled: true }
put api("/projects/#{project.id}", user3), project_param put api("/projects/#{project.id}", user3), project_param
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment