Commit 7c7e9b13e724c622929ee62f4daf632d3c5b707f
Exists in
master
and in
4 other branches
Merge pull request #1478 from AlexDenisov/ssh_keys_api
SSH Keys API implemented
Showing
6 changed files
with
210 additions
and
0 deletions
Show diff stats
doc/api/README.md
| @@ -34,3 +34,4 @@ When listing resources you can pass the following parameters: | @@ -34,3 +34,4 @@ When listing resources you can pass the following parameters: | ||
| 34 | + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) | 34 | + [Snippets](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/snippets.md) |
| 35 | + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) | 35 | + [Issues](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/issues.md) |
| 36 | + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) | 36 | + [Milestones](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/milestones.md) |
| 37 | ++ [SSH Keys](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/keys.md) |
| @@ -0,0 +1,79 @@ | @@ -0,0 +1,79 @@ | ||
| 1 | +## List keys | ||
| 2 | + | ||
| 3 | +Get a list of currently authenticated user's keys. | ||
| 4 | + | ||
| 5 | +``` | ||
| 6 | +GET /keys | ||
| 7 | +``` | ||
| 8 | + | ||
| 9 | +```json | ||
| 10 | +[ | ||
| 11 | + { | ||
| 12 | + "id": 1, | ||
| 13 | + "title" : "Public key" | ||
| 14 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
| 15 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
| 16 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", | ||
| 17 | + }, | ||
| 18 | + { | ||
| 19 | + "id": 3, | ||
| 20 | + "title" : "Another Public key" | ||
| 21 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
| 22 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
| 23 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | ||
| 24 | + } | ||
| 25 | +] | ||
| 26 | +``` | ||
| 27 | + | ||
| 28 | +## Single key | ||
| 29 | + | ||
| 30 | +Get a single key. | ||
| 31 | + | ||
| 32 | +``` | ||
| 33 | +GET /keys/:id | ||
| 34 | +``` | ||
| 35 | + | ||
| 36 | +Parameters: | ||
| 37 | + | ||
| 38 | ++ `id` (required) - The ID of a key | ||
| 39 | + | ||
| 40 | +```json | ||
| 41 | +{ | ||
| 42 | + "id": 1, | ||
| 43 | + "title" : "Public key" | ||
| 44 | + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4 | ||
| 45 | + 596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4 | ||
| 46 | + soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" | ||
| 47 | + } | ||
| 48 | +``` | ||
| 49 | +## Add key | ||
| 50 | + | ||
| 51 | +Create new key owned by currently authenticated user | ||
| 52 | + | ||
| 53 | +``` | ||
| 54 | +POST /keys | ||
| 55 | +``` | ||
| 56 | + | ||
| 57 | +Parameters: | ||
| 58 | + | ||
| 59 | ++ `title` (required) - new SSH Key | ||
| 60 | ++ `key` (optional) - new SSH key's title | ||
| 61 | + | ||
| 62 | +Will return created key with status `201 Created` on success, or `404 Not | ||
| 63 | +found` on fail. | ||
| 64 | + | ||
| 65 | +## Delete key | ||
| 66 | + | ||
| 67 | +Delete key owned by currently authenticated user | ||
| 68 | + | ||
| 69 | +``` | ||
| 70 | +DELETE /keys/:id | ||
| 71 | +``` | ||
| 72 | + | ||
| 73 | +Parameters: | ||
| 74 | + | ||
| 75 | ++ `id` (required) - key ID | ||
| 76 | + | ||
| 77 | +Will return `200 OK` on success, or `404 Not Found` on fail. | ||
| 78 | + | ||
| 79 | + |
lib/api.rb
lib/api/entities.rb
| @@ -48,5 +48,11 @@ module Gitlab | @@ -48,5 +48,11 @@ module Gitlab | ||
| 48 | expose :assignee, :author, using: Entities::UserBasic | 48 | expose :assignee, :author, using: Entities::UserBasic |
| 49 | expose :closed, :updated_at, :created_at | 49 | expose :closed, :updated_at, :created_at |
| 50 | end | 50 | end |
| 51 | + | ||
| 52 | + class Key < Grape::Entity | ||
| 53 | + expose :id, | ||
| 54 | + :title, | ||
| 55 | + :key | ||
| 56 | + end | ||
| 51 | end | 57 | end |
| 52 | end | 58 | end |
| @@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
| 1 | +module Gitlab | ||
| 2 | + # Keys API | ||
| 3 | + class Keys < Grape::API | ||
| 4 | + before { authenticate! } | ||
| 5 | + resource :keys do | ||
| 6 | + # Get currently authenticated user's keys | ||
| 7 | + # | ||
| 8 | + # Example Request: | ||
| 9 | + # GET /keys | ||
| 10 | + get do | ||
| 11 | + present current_user.keys, with: Entities::Key | ||
| 12 | + end | ||
| 13 | + # Get single key owned by currently authenticated user | ||
| 14 | + # | ||
| 15 | + # Example Request: | ||
| 16 | + # GET /keys/:id | ||
| 17 | + get "/:id" do | ||
| 18 | + key = current_user.keys.find params[:id] | ||
| 19 | + present key, with: Entities::Key | ||
| 20 | + end | ||
| 21 | + # Add new ssh key to currently authenticated user | ||
| 22 | + # | ||
| 23 | + # Parameters: | ||
| 24 | + # key (required) - New SSH Key | ||
| 25 | + # title (required) - New SSH Key's title | ||
| 26 | + # Example Request: | ||
| 27 | + # POST /keys | ||
| 28 | + post do | ||
| 29 | + attrs = attributes_for_keys [:title, :key] | ||
| 30 | + key = current_user.keys.new attrs | ||
| 31 | + if key.save | ||
| 32 | + present key, with: Entities::Key | ||
| 33 | + else | ||
| 34 | + not_found! | ||
| 35 | + end | ||
| 36 | + end | ||
| 37 | + # Delete existed ssh key of currently authenticated user | ||
| 38 | + # | ||
| 39 | + # Parameters: | ||
| 40 | + # id (required) - SSH Key ID | ||
| 41 | + # Example Request: | ||
| 42 | + # DELETE /keys/:id | ||
| 43 | + delete "/:id" do | ||
| 44 | + key = current_user.keys.find params[:id] | ||
| 45 | + key.delete | ||
| 46 | + end | ||
| 47 | + end | ||
| 48 | + end | ||
| 49 | +end | ||
| 50 | + |
| @@ -0,0 +1,73 @@ | @@ -0,0 +1,73 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe Gitlab::Keys do | ||
| 4 | + include ApiHelpers | ||
| 5 | + let(:user) { | ||
| 6 | + user = Factory.create :user | ||
| 7 | + user.reset_authentication_token! | ||
| 8 | + user | ||
| 9 | + } | ||
| 10 | + let(:key) { Factory.create :key, { user: user}} | ||
| 11 | + | ||
| 12 | + describe "GET /keys" do | ||
| 13 | + context "when unauthenticated" do | ||
| 14 | + it "should return authentication error" do | ||
| 15 | + get api("/keys") | ||
| 16 | + response.status.should == 401 | ||
| 17 | + end | ||
| 18 | + end | ||
| 19 | + context "when authenticated" do | ||
| 20 | + it "should return array of ssh keys" do | ||
| 21 | + user.keys << key | ||
| 22 | + user.save | ||
| 23 | + get api("/keys", user) | ||
| 24 | + response.status.should == 200 | ||
| 25 | + json_response.should be_an Array | ||
| 26 | + json_response.first["title"].should == key.title | ||
| 27 | + end | ||
| 28 | + end | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + describe "GET /keys/:id" do | ||
| 32 | + it "should returm single key" do | ||
| 33 | + user.keys << key | ||
| 34 | + user.save | ||
| 35 | + get api("/keys/#{key.id}", user) | ||
| 36 | + response.status.should == 200 | ||
| 37 | + json_response["title"].should == key.title | ||
| 38 | + end | ||
| 39 | + it "should return 404 Not Found within invalid ID" do | ||
| 40 | + get api("/keys/42", user) | ||
| 41 | + response.status.should == 404 | ||
| 42 | + end | ||
| 43 | + end | ||
| 44 | + | ||
| 45 | + describe "POST /keys" do | ||
| 46 | + it "should not create invalid ssh key" do | ||
| 47 | + post api("/keys", user), { title: "invalid key" } | ||
| 48 | + response.status.should == 404 | ||
| 49 | + end | ||
| 50 | + it "should create ssh key" do | ||
| 51 | + key_attrs = Factory.attributes :key | ||
| 52 | + expect { | ||
| 53 | + post api("/keys", user), key_attrs | ||
| 54 | + }.to change{ user.keys.count }.by(1) | ||
| 55 | + end | ||
| 56 | + end | ||
| 57 | + | ||
| 58 | + describe "DELETE /keys/:id" do | ||
| 59 | + it "should delete existed key" do | ||
| 60 | + user.keys << key | ||
| 61 | + user.save | ||
| 62 | + expect { | ||
| 63 | + delete api("/keys/#{key.id}", user) | ||
| 64 | + }.to change{user.keys.count}.by(-1) | ||
| 65 | + end | ||
| 66 | + it "should return 404 Not Found within invalid ID" do | ||
| 67 | + delete api("/keys/42", user) | ||
| 68 | + response.status.should == 404 | ||
| 69 | + end | ||
| 70 | + end | ||
| 71 | + | ||
| 72 | +end | ||
| 73 | + |