Commit f3ea58ef authored by Gabriel Mazetto's avatar Gabriel Mazetto

Merge branch...

Merge branch '14284-allow-all-deploy-keys-to-be-retrieved-via-api-regardless-of-project-affiliation' into 'master'

Add /deploy_keys API to retrieve all deploy keys regardless of project affiliation

## What does this MR do?

Add /deploy_keys API to retrieve all deploy keys regardless of project affiliation

## Are there points in the code the reviewer needs to double check?

Is documentation clear?

## Why was this MR needed?

User request

## What are the relevant issue numbers?

#14284

See merge request !4426
parents cfa9ea60 04803174
...@@ -30,6 +30,8 @@ v 8.10.0 (unreleased) ...@@ -30,6 +30,8 @@ v 8.10.0 (unreleased)
- Apply the trusted_proxies config to the rack request object for use with rack_attack - Apply the trusted_proxies config to the rack request object for use with rack_attack
- Upgrade to Rails 4.2.7. !5236 - Upgrade to Rails 4.2.7. !5236
- Extend exposed environment variables for CI builds - Extend exposed environment variables for CI builds
- Deprecate APIs "projects/:id/keys/...". Use "projects/:id/deploy_keys/..." instead
- Add API "deploy_keys" for admins to get all deploy keys
- Allow to pull code with deploy key from public projects - Allow to pull code with deploy key from public projects
- Use limit parameter rather than hardcoded value in `ldap:check` rake task (Mike Ricketts) - Use limit parameter rather than hardcoded value in `ldap:check` rake task (Mike Ricketts)
- Add Sidekiq queue duration to transaction metrics. - Add Sidekiq queue duration to transaction metrics.
......
...@@ -24,6 +24,6 @@ With those IDs, add the same deploy key to all: ...@@ -24,6 +24,6 @@ With those IDs, add the same deploy key to all:
``` ```
for project_id in 321 456 987; do for project_id in 321 456 987; do
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" \ curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" \
--data '{"title": "my key", "key": "ssh-rsa AAAA..."}' https://gitlab.example.com/api/v3/projects/${project_id}/keys --data '{"title": "my key", "key": "ssh-rsa AAAA..."}' https://gitlab.example.com/api/v3/projects/${project_id}/deploy_keys
done done
``` ```
# Deploy Keys # Deploy Keys
## List deploy keys ## List all deploy keys
Get a list of all deploy keys across all projects.
```
GET /deploy_keys
```
```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/deploy_keys"
```
Example response:
```json
[
{
"id": 1,
"title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2013-10-02T10:12:29Z"
},
{
"id": 3,
"title": "Another Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"created_at": "2013-10-02T11:12:29Z"
}
]
```
## List project deploy keys
Get a list of a project's deploy keys. Get a list of a project's deploy keys.
``` ```
GET /projects/:id/keys GET /projects/:id/deploy_keys
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -13,7 +44,7 @@ GET /projects/:id/keys ...@@ -13,7 +44,7 @@ GET /projects/:id/keys
| `id` | integer | yes | The ID of the project | | `id` | integer | yes | The ID of the project |
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/keys" curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/deploy_keys"
``` ```
Example response: Example response:
...@@ -40,7 +71,7 @@ Example response: ...@@ -40,7 +71,7 @@ Example response:
Get a single key. Get a single key.
``` ```
GET /projects/:id/keys/:key_id GET /projects/:id/deploy_keys/:key_id
``` ```
Parameters: Parameters:
...@@ -51,7 +82,7 @@ Parameters: ...@@ -51,7 +82,7 @@ Parameters:
| `key_id` | integer | yes | The ID of the deploy key | | `key_id` | integer | yes | The ID of the deploy key |
```bash ```bash
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/keys/11" curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/deploy_keys/11"
``` ```
Example response: Example response:
...@@ -73,7 +104,7 @@ If the deploy key already exists in another project, it will be joined to curren ...@@ -73,7 +104,7 @@ If the deploy key already exists in another project, it will be joined to curren
project only if original one was is accessible by the same user. project only if original one was is accessible by the same user.
``` ```
POST /projects/:id/keys POST /projects/:id/deploy_keys
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -83,7 +114,7 @@ POST /projects/:id/keys ...@@ -83,7 +114,7 @@ POST /projects/:id/keys
| `key` | string | yes | New deploy key | | `key` | string | yes | New deploy key |
```bash ```bash
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA..."}' "https://gitlab.example.com/api/v3/projects/5/keys/" curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA..."}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/"
``` ```
Example response: Example response:
...@@ -102,7 +133,7 @@ Example response: ...@@ -102,7 +133,7 @@ Example response:
Delete a deploy key from a project Delete a deploy key from a project
``` ```
DELETE /projects/:id/keys/:key_id DELETE /projects/:id/deploy_keys/:key_id
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
...@@ -111,7 +142,7 @@ DELETE /projects/:id/keys/:key_id ...@@ -111,7 +142,7 @@ DELETE /projects/:id/keys/:key_id
| `key_id` | integer | yes | The ID of the deploy key | | `key_id` | integer | yes | The ID of the deploy key |
```bash ```bash
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/keys/13" curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/deploy_keys/13"
``` ```
Example response: Example response:
......
...@@ -2,74 +2,87 @@ module API ...@@ -2,74 +2,87 @@ module API
# Projects API # Projects API
class DeployKeys < Grape::API class DeployKeys < Grape::API
before { authenticate! } before { authenticate! }
before { authorize_admin_project }
get "deploy_keys" do
authenticated_as_admin!
keys = DeployKey.all
present keys, with: Entities::SSHKey
end
resource :projects do resource :projects do
# Get a specific project's keys before { authorize_admin_project }
#
# Example Request:
# GET /projects/:id/keys
get ":id/keys" do
present user_project.deploy_keys, with: Entities::SSHKey
end
# Get single key owned by currently authenticated user # Routing "projects/:id/keys/..." is DEPRECATED and WILL BE REMOVED in version 9.0
# Use "projects/:id/deploy_keys/..." instead.
# #
# Example Request: %w(keys deploy_keys).each do |path|
# GET /projects/:id/keys/:id # Get a specific project's deploy keys
get ":id/keys/:key_id" do #
key = user_project.deploy_keys.find params[:key_id] # Example Request:
present key, with: Entities::SSHKey # GET /projects/:id/deploy_keys
end get ":id/#{path}" do
present user_project.deploy_keys, with: Entities::SSHKey
end
# Add new ssh key to currently authenticated user # Get single deploy key owned by currently authenticated user
# If deploy key already exists - it will be joined to project #
# but only if original one was is accessible by same user # Example Request:
# # GET /projects/:id/deploy_keys/:key_id
# Parameters: get ":id/#{path}/:key_id" do
# key (required) - New SSH Key key = user_project.deploy_keys.find params[:key_id]
# title (required) - New SSH Key's title present key, with: Entities::SSHKey
# Example Request: end
# POST /projects/:id/keys
post ":id/keys" do
attrs = attributes_for_keys [:title, :key]
if attrs[:key].present? # Add new deploy key to currently authenticated user
attrs[:key].strip! # If deploy key already exists - it will be joined to project
# but only if original one was accessible by same user
#
# Parameters:
# key (required) - New deploy Key
# title (required) - New deploy Key's title
# Example Request:
# POST /projects/:id/deploy_keys
post ":id/#{path}" do
attrs = attributes_for_keys [:title, :key]
# check if key already exist in project if attrs[:key].present?
key = user_project.deploy_keys.find_by(key: attrs[:key]) attrs[:key].strip!
if key
present key, with: Entities::SSHKey # check if key already exist in project
return key = user_project.deploy_keys.find_by(key: attrs[:key])
if key
present key, with: Entities::SSHKey
next
end
# Check for available deploy keys in other projects
key = current_user.accessible_deploy_keys.find_by(key: attrs[:key])
if key
user_project.deploy_keys << key
present key, with: Entities::SSHKey
next
end
end end
# Check for available deploy keys in other projects key = DeployKey.new attrs
key = current_user.accessible_deploy_keys.find_by(key: attrs[:key])
if key if key.valid? && user_project.deploy_keys << key
user_project.deploy_keys << key
present key, with: Entities::SSHKey present key, with: Entities::SSHKey
return else
render_validation_error!(key)
end end
end end
key = DeployKey.new attrs # Delete existing deploy key of currently authenticated user
#
if key.valid? && user_project.deploy_keys << key # Example Request:
present key, with: Entities::SSHKey # DELETE /projects/:id/deploy_keys/:key_id
else delete ":id/#{path}/:key_id" do
render_validation_error!(key) key = user_project.deploy_keys.find params[:key_id]
key.destroy
end end
end end
# Delete existed ssh key of currently authenticated user
#
# Example Request:
# DELETE /projects/:id/keys/:id
delete ":id/keys/:key_id" do
key = user_project.deploy_keys.find params[:key_id]
key.destroy
end
end end
end end
end end
require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:project) { create(:project, creator_id: user.id) }
let!(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
let(:admin) { create(:admin) }
describe 'GET /deploy_keys' do
before { admin }
context 'when unauthenticated' do
it 'should return authentication error' do
get api('/deploy_keys')
expect(response.status).to eq(401)
end
end
context 'when authenticated as non-admin user' do
it 'should return a 403 error' do
get api('/deploy_keys', user)
expect(response.status).to eq(403)
end
end
context 'when authenticated as admin' do
it 'should return all deploy keys' do
get api('/deploy_keys', admin)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['id']).to eq(deploy_keys_project.deploy_key.id)
end
end
end
end
...@@ -647,33 +647,33 @@ describe API::API, api: true do ...@@ -647,33 +647,33 @@ describe API::API, api: true do
let(:deploy_keys_project) { create(:deploy_keys_project, project: project) } let(:deploy_keys_project) { create(:deploy_keys_project, project: project) }
let(:deploy_key) { deploy_keys_project.deploy_key } let(:deploy_key) { deploy_keys_project.deploy_key }
describe 'GET /projects/:id/keys' do describe 'GET /projects/:id/deploy_keys' do
before { deploy_key } before { deploy_key }
it 'should return array of ssh keys' do it 'should return array of ssh keys' do
get api("/projects/#{project.id}/keys", user) get api("/projects/#{project.id}/deploy_keys", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.first['title']).to eq(deploy_key.title) expect(json_response.first['title']).to eq(deploy_key.title)
end end
end end
describe 'GET /projects/:id/keys/:key_id' do describe 'GET /projects/:id/deploy_keys/:key_id' do
it 'should return a single key' do it 'should return a single key' do
get api("/projects/#{project.id}/keys/#{deploy_key.id}", user) get api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['title']).to eq(deploy_key.title) expect(json_response['title']).to eq(deploy_key.title)
end end
it 'should return 404 Not Found with invalid ID' do it 'should return 404 Not Found with invalid ID' do
get api("/projects/#{project.id}/keys/404", user) get api("/projects/#{project.id}/deploy_keys/404", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
end end
describe 'POST /projects/:id/keys' do describe 'POST /projects/:id/deploy_keys' do
it 'should not create an invalid ssh key' do it 'should not create an invalid ssh key' do
post api("/projects/#{project.id}/keys", user), { title: 'invalid key' } post api("/projects/#{project.id}/deploy_keys", user), { title: 'invalid key' }
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['message']['key']).to eq([ expect(json_response['message']['key']).to eq([
'can\'t be blank', 'can\'t be blank',
...@@ -683,7 +683,7 @@ describe API::API, api: true do ...@@ -683,7 +683,7 @@ describe API::API, api: true do
end end
it 'should not create a key without title' do it 'should not create a key without title' do
post api("/projects/#{project.id}/keys", user), key: 'some key' post api("/projects/#{project.id}/deploy_keys", user), key: 'some key'
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
expect(json_response['message']['title']).to eq([ expect(json_response['message']['title']).to eq([
'can\'t be blank', 'can\'t be blank',
...@@ -694,22 +694,22 @@ describe API::API, api: true do ...@@ -694,22 +694,22 @@ describe API::API, api: true do
it 'should create new ssh key' do it 'should create new ssh key' do
key_attrs = attributes_for :key key_attrs = attributes_for :key
expect do expect do
post api("/projects/#{project.id}/keys", user), key_attrs post api("/projects/#{project.id}/deploy_keys", user), key_attrs
end.to change{ project.deploy_keys.count }.by(1) end.to change{ project.deploy_keys.count }.by(1)
end end
end end
describe 'DELETE /projects/:id/keys/:key_id' do describe 'DELETE /projects/:id/deploy_keys/:key_id' do
before { deploy_key } before { deploy_key }
it 'should delete existing key' do it 'should delete existing key' do
expect do expect do
delete api("/projects/#{project.id}/keys/#{deploy_key.id}", user) delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", user)
end.to change{ project.deploy_keys.count }.by(-1) end.to change{ project.deploy_keys.count }.by(-1)
end end
it 'should return 404 Not Found with invalid ID' do it 'should return 404 Not Found with invalid ID' do
delete api("/projects/#{project.id}/keys/404", user) delete api("/projects/#{project.id}/deploy_keys/404", user)
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
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