From 469ab99e9a4e31c1e95ab37f363c26b62b782542 Mon Sep 17 00:00:00 2001 From: Vlad Temian Date: Wed, 6 Dec 2017 12:27:10 +0200 Subject: [PATCH] Delete a user's public key via admin api (closes #3014) (#3059) * Delete a user's public key via admin api * Test admin ssh endpoint for creating a new ssh key * Adapt public ssh key test to also test the delete operation * Test that deleting a missing key will result in a 404 * Test that a normal user can't delete another user's ssh key * Make DeletePublicKey return err * Update swagger doc --- integrations/api_admin_test.go | 73 ++++++++++++ models/ssh_key.go | 5 +- public/swagger.v1.json | 201 ++++++++++++++++++++++++++++++++- routers/api/v1/admin/user.go | 45 ++++++++ routers/api/v1/api.go | 5 +- routers/api/v1/user/key.go | 6 +- 6 files changed, 324 insertions(+), 11 deletions(-) create mode 100644 integrations/api_admin_test.go diff --git a/integrations/api_admin_test.go b/integrations/api_admin_test.go new file mode 100644 index 00000000000..37e5fd199ad --- /dev/null +++ b/integrations/api_admin_test.go @@ -0,0 +1,73 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "fmt" + "net/http" + "testing" + + "code.gitea.io/gitea/models" + api "code.gitea.io/sdk/gitea" +) + +func TestAPIAdminCreateAndDeleteSSHKey(t *testing.T) { + prepareTestEnv(t) + // user1 is an admin user + session := loginUser(t, "user1") + keyOwner := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User) + + urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", keyOwner.Name) + req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n", + "title": "test-key", + }) + resp := session.MakeRequest(t, req, http.StatusCreated) + + var newPublicKey api.PublicKey + DecodeJSON(t, resp, &newPublicKey) + models.AssertExistsAndLoadBean(t, &models.PublicKey{ + ID: newPublicKey.ID, + Name: newPublicKey.Title, + Content: newPublicKey.Key, + Fingerprint: newPublicKey.Fingerprint, + OwnerID: keyOwner.ID, + }) + + req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", + keyOwner.Name, newPublicKey.ID) + session.MakeRequest(t, req, http.StatusNoContent) + models.AssertNotExistsBean(t, &models.PublicKey{ID: newPublicKey.ID}) +} + +func TestAPIAdminDeleteMissingSSHKey(t *testing.T) { + prepareTestEnv(t) + // user1 is an admin user + session := loginUser(t, "user1") + + req := NewRequestf(t, "DELETE", "/api/v1/admin/users/user1/keys/%d", models.NonexistentID) + session.MakeRequest(t, req, http.StatusNotFound) +} + +func TestAPIAdminDeleteUnauthorizedKey(t *testing.T) { + prepareTestEnv(t) + adminUsername := "user1" + normalUsername := "user2" + session := loginUser(t, adminUsername) + + urlStr := fmt.Sprintf("/api/v1/admin/users/%s/keys", adminUsername) + req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ + "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\n", + "title": "test-key", + }) + resp := session.MakeRequest(t, req, http.StatusCreated) + var newPublicKey api.PublicKey + DecodeJSON(t, resp, &newPublicKey) + + session = loginUser(t, normalUsername) + req = NewRequestf(t, "DELETE", "/api/v1/admin/users/%s/keys/%d", + adminUsername, newPublicKey.ID) + session.MakeRequest(t, req, http.StatusForbidden) +} diff --git a/models/ssh_key.go b/models/ssh_key.go index 2d9f521fd4f..9365fab1ab8 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -506,10 +506,7 @@ func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error { func DeletePublicKey(doer *User, id int64) (err error) { key, err := GetPublicKeyByID(id) if err != nil { - if IsErrKeyNotExist(err) { - return nil - } - return fmt.Errorf("GetPublicKeyByID: %v", err) + return err } // Check if user has access to delete this key. diff --git a/public/swagger.v1.json b/public/swagger.v1.json index 4a74027663b..c04e11f24e8 100644 --- a/public/swagger.v1.json +++ b/public/swagger.v1.json @@ -36,6 +36,7 @@ ], "summary": "Create a user", "operationId": "adminCreateUser", + "security": null, "parameters": [ { "name": "body", @@ -68,6 +69,7 @@ ], "summary": "Delete a user", "operationId": "adminDeleteUser", + "security": null, "parameters": [ { "type": "string", @@ -101,6 +103,7 @@ ], "summary": "Edit an existing user", "operationId": "adminEditUser", + "security": null, "parameters": [ { "type": "string", @@ -143,6 +146,7 @@ ], "summary": "Add a public key on behalf of a user", "operationId": "adminCreatePublicKey", + "security": null, "parameters": [ { "type": "string", @@ -165,6 +169,46 @@ } } }, + "/admin/users/{username}/keys/{id}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "admin" + ], + "summary": "Delete a user's public key", + "operationId": "adminDeleteUserPublicKey", + "security": null, + "parameters": [ + { + "type": "string", + "description": "username of user", + "name": "username", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the key to delete", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/empty" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/admin/users/{username}/orgs": { "post": { "consumes": [ @@ -178,6 +222,7 @@ ], "summary": "Create an organization", "operationId": "adminCreateOrg", + "security": null, "parameters": [ { "type": "string", @@ -213,6 +258,7 @@ ], "summary": "Create a repository on behalf a user", "operationId": "adminCreateRepo", + "security": null, "parameters": [ { "type": "string", @@ -248,6 +294,7 @@ ], "summary": "Render a markdown document as HTML", "operationId": "renderMarkdown", + "security": null, "parameters": [ { "name": "body", @@ -280,6 +327,7 @@ ], "summary": "Render raw markdown as HTML", "operationId": "renderMarkdownRaw", + "security": null, "parameters": [ { "type": "string", @@ -310,6 +358,7 @@ ], "summary": "Create a repository in an organization", "operationId": "createOrgRepo", + "security": null, "parameters": [ { "type": "string", @@ -349,6 +398,7 @@ ], "summary": "Get an organization", "operationId": "orgGet", + "security": null, "parameters": [ { "type": "string", @@ -376,6 +426,7 @@ ], "summary": "Edit an organization", "operationId": "orgEdit", + "security": null, "parameters": [ { "type": "string", @@ -409,6 +460,7 @@ ], "summary": "List an organization's webhooks", "operationId": "orgListHooks", + "security": null, "responses": { "200": { "$ref": "#/responses/HookList" @@ -429,6 +481,7 @@ ], "summary": "Create a hook", "operationId": "orgCreateHook", + "security": null, "responses": { "201": { "$ref": "#/responses/Hook" @@ -446,6 +499,7 @@ ], "summary": "Get a hook", "operationId": "orgGetHook", + "security": null, "responses": { "200": { "$ref": "#/responses/Hook" @@ -461,6 +515,7 @@ ], "summary": "Delete a hook", "operationId": "orgDeleteHook", + "security": null, "responses": { "204": { "$ref": "#/responses/empty" @@ -479,6 +534,7 @@ ], "summary": "Update a hook", "operationId": "orgEditHook", + "security": null, "responses": { "200": { "$ref": "#/responses/Hook" @@ -496,6 +552,7 @@ ], "summary": "List an organization's members", "operationId": "orgListMembers", + "security": null, "parameters": [ { "type": "string", @@ -519,6 +576,7 @@ ], "summary": "Check if a user is a member of an organization", "operationId": "orgIsMember", + "security": null, "parameters": [ { "type": "string", @@ -559,6 +617,7 @@ ], "summary": "Remove a member from an organization", "operationId": "orgDeleteMember", + "security": null, "parameters": [ { "type": "string", @@ -595,6 +654,7 @@ ], "summary": "List an organization's public members", "operationId": "orgListPublicMembers", + "security": null, "parameters": [ { "type": "string", @@ -618,6 +678,7 @@ ], "summary": "Check if a user is a public member of an organization", "operationId": "orgIsPublicMember", + "security": null, "parameters": [ { "type": "string", @@ -658,6 +719,7 @@ ], "summary": "Publicize a user's membership", "operationId": "orgPublicizeMember", + "security": null, "parameters": [ { "type": "string", @@ -692,6 +754,7 @@ ], "summary": "Conceal a user's membership", "operationId": "orgConcealMember", + "security": null, "parameters": [ { "type": "string", @@ -725,6 +788,7 @@ ], "summary": "List an organization's repos", "operationId": "orgListRepos", + "security": null, "parameters": [ { "type": "string", @@ -751,6 +815,7 @@ ], "summary": "List an organization's teams", "operationId": "orgListTeams", + "security": null, "parameters": [ { "type": "string", @@ -778,6 +843,7 @@ ], "summary": "Create a team", "operationId": "orgCreateTeam", + "security": null, "parameters": [ { "type": "string", @@ -814,6 +880,7 @@ ], "summary": "Migrate a remote git repository", "operationId": "repoMigrate", + "security": null, "parameters": [ { "name": "body", @@ -840,6 +907,7 @@ ], "summary": "Search for repositories", "operationId": "repoSearch", + "security": null, "parameters": [ { "type": "string", @@ -898,6 +966,7 @@ ], "summary": "Get a repository", "operationId": "repoGet", + "security": null, "parameters": [ { "type": "string", @@ -929,6 +998,7 @@ ], "summary": "Delete a repository", "operationId": "repoDelete", + "security": null, "parameters": [ { "type": "string", @@ -965,6 +1035,7 @@ ], "summary": "Get an archive of a repository", "operationId": "repoGetArchive", + "security": null, "parameters": [ { "type": "string", @@ -1003,6 +1074,7 @@ ], "summary": "List a repository's branches", "operationId": "repoListBranches", + "security": null, "parameters": [ { "type": "string", @@ -1036,6 +1108,7 @@ ], "summary": "List a repository's branches", "operationId": "repoGetBranch", + "security": null, "parameters": [ { "type": "string", @@ -1076,6 +1149,7 @@ ], "summary": "List a repository's collaborators", "operationId": "repoListCollaborators", + "security": null, "parameters": [ { "type": "string", @@ -1109,6 +1183,7 @@ ], "summary": "Check if a user is a collaborator of a repository", "operationId": "repoCheckCollaborator", + "security": null, "parameters": [ { "type": "string", @@ -1150,6 +1225,7 @@ ], "summary": "Add a collaborator to a repository", "operationId": "repoAddCollaborator", + "security": null, "parameters": [ { "type": "string", @@ -1195,6 +1271,7 @@ ], "summary": "Delete a collaborator from a repository", "operationId": "repoDeleteCollaborator", + "security": null, "parameters": [ { "type": "string", @@ -1235,6 +1312,7 @@ ], "summary": "Get a commit's combined status, by branch/tag/commit reference", "operationId": "repoGetCombinedStatusByRef", + "security": null, "parameters": [ { "type": "string", @@ -1275,6 +1353,7 @@ ], "summary": "Get the EditorConfig definitions of a file in a repository", "operationId": "repoGetEditorConfig", + "security": null, "parameters": [ { "type": "string", @@ -1313,6 +1392,7 @@ ], "summary": "List a repository's forks", "operationId": "listForks", + "security": null, "parameters": [ { "type": "string", @@ -1344,6 +1424,7 @@ ], "summary": "Fork a repository", "operationId": "createFork", + "security": null, "parameters": [ { "type": "string", @@ -1384,6 +1465,7 @@ ], "summary": "List the hooks in a repository", "operationId": "repoListHooks", + "security": null, "parameters": [ { "type": "string", @@ -1418,6 +1500,7 @@ ], "summary": "Create a hook", "operationId": "repoCreateHook", + "security": null, "parameters": [ { "type": "string", @@ -1458,6 +1541,7 @@ ], "summary": "Get a hook", "operationId": "repoGetHook", + "security": null, "parameters": [ { "type": "string", @@ -1496,6 +1580,7 @@ ], "summary": "Edit a hook in a repository", "operationId": "repoEditHook", + "security": null, "parameters": [ { "type": "string", @@ -1536,6 +1621,7 @@ ], "summary": "List all comments on an issue", "operationId": "issueGetComments", + "security": null, "parameters": [ { "type": "string", @@ -1585,6 +1671,7 @@ ], "summary": "Replace an issue's labels", "operationId": "issueReplaceLabels", + "security": null, "parameters": [ { "type": "string", @@ -1633,6 +1720,7 @@ ], "summary": "Add a label to an issue", "operationId": "issueAddLabel", + "security": null, "parameters": [ { "type": "string", @@ -1678,6 +1766,7 @@ ], "summary": "Remove all labels from an issue", "operationId": "issueClearLabels", + "security": null, "parameters": [ { "type": "string", @@ -1718,6 +1807,7 @@ ], "summary": "Remove a label from an issue", "operationId": "issueRemoveLabel", + "security": null, "parameters": [ { "type": "string", @@ -1765,6 +1855,7 @@ ], "summary": "List a repository's issues", "operationId": "issueListIssues", + "security": null, "parameters": [ { "type": "string", @@ -1811,6 +1902,7 @@ ], "summary": "Create an issue", "operationId": "issueCreateIssue", + "security": null, "parameters": [ { "type": "string", @@ -1851,6 +1943,7 @@ ], "summary": "List all comments in a repository", "operationId": "issueGetRepoComments", + "security": null, "parameters": [ { "type": "string", @@ -1887,6 +1980,7 @@ ], "summary": "Delete a comment", "operationId": "issueDeleteComment", + "security": null, "parameters": [ { "type": "string", @@ -1928,6 +2022,7 @@ ], "summary": "Edit a comment", "operationId": "issueEditComment", + "security": null, "parameters": [ { "type": "string", @@ -1975,6 +2070,7 @@ ], "summary": "Get an issue by id", "operationId": "issueGetIssue", + "security": null, "parameters": [ { "type": "string", @@ -2016,6 +2112,7 @@ ], "summary": "Edit an issue", "operationId": "issueEditIssue", + "security": null, "parameters": [ { "type": "string", @@ -2066,6 +2163,7 @@ ], "summary": "Add a comment to an issue", "operationId": "issueCreateComment", + "security": null, "parameters": [ { "type": "string", @@ -2111,6 +2209,7 @@ "summary": "Delete a comment", "operationId": "issueDeleteCommentDeprecated", "deprecated": true, + "security": null, "parameters": [ { "type": "string", @@ -2160,6 +2259,7 @@ "summary": "Edit a comment", "operationId": "issueEditCommentDeprecated", "deprecated": true, + "security": null, "parameters": [ { "type": "string", @@ -2214,6 +2314,7 @@ ], "summary": "Get an issue's labels", "operationId": "issueGetLabels", + "security": null, "parameters": [ { "type": "string", @@ -2257,6 +2358,7 @@ ], "summary": "List an issue's tracked times", "operationId": "issueTrackedTimes", + "security": null, "parameters": [ { "type": "string", @@ -2298,6 +2400,7 @@ ], "summary": "Add a tracked time to a issue", "operationId": "issueAddTime", + "security": null, "parameters": [ { "type": "string", @@ -2351,6 +2454,7 @@ ], "summary": "List a repository's keys", "operationId": "repoListKeys", + "security": null, "parameters": [ { "type": "string", @@ -2385,6 +2489,7 @@ ], "summary": "Add a key to a repository", "operationId": "repoCreateKey", + "security": null, "parameters": [ { "type": "string", @@ -2425,6 +2530,7 @@ ], "summary": "Get a repository's key by id", "operationId": "repoGetKey", + "security": null, "parameters": [ { "type": "string", @@ -2460,6 +2566,7 @@ ], "summary": "Delete a key from a repository", "operationId": "repoDeleteKey", + "security": null, "parameters": [ { "type": "string", @@ -2500,6 +2607,7 @@ ], "summary": "Get all of a repository's labels", "operationId": "issueListLabels", + "security": null, "parameters": [ { "type": "string", @@ -2534,6 +2642,7 @@ ], "summary": "Create a label", "operationId": "issueCreateLabel", + "security": null, "parameters": [ { "type": "string", @@ -2574,6 +2683,7 @@ ], "summary": "Get a single label", "operationId": "issueGetLabel", + "security": null, "parameters": [ { "type": "string", @@ -2609,6 +2719,7 @@ ], "summary": "Delete a label", "operationId": "issueDeleteLabel", + "security": null, "parameters": [ { "type": "string", @@ -2650,6 +2761,7 @@ ], "summary": "Update a label", "operationId": "issueEditLabel", + "security": null, "parameters": [ { "type": "string", @@ -2697,6 +2809,7 @@ ], "summary": "Get all of a repository's milestones", "operationId": "issueGetMilestones", + "security": null, "parameters": [ { "type": "string", @@ -2738,6 +2851,7 @@ ], "summary": "Create a milestone", "operationId": "issueCreateMilestone", + "security": null, "parameters": [ { "type": "string", @@ -2778,6 +2892,7 @@ ], "summary": "Get a milestone", "operationId": "issueGetMilestone", + "security": null, "responses": { "200": { "$ref": "#/responses/Milestone" @@ -2790,6 +2905,7 @@ ], "summary": "Delete a milestone", "operationId": "issueDeleteMilestone", + "security": null, "parameters": [ { "type": "string", @@ -2831,6 +2947,7 @@ ], "summary": "Update a milestone", "operationId": "issueEditMilestone", + "security": null, "parameters": [ { "type": "string", @@ -2871,6 +2988,7 @@ ], "summary": "Sync a mirrored repository", "operationId": "repoMirrorSync", + "security": null, "parameters": [ { "type": "string", @@ -2904,6 +3022,7 @@ ], "summary": "List a repo's pull requests", "operationId": "repoListPullRequests", + "security": null, "parameters": [ { "type": "string", @@ -2938,6 +3057,7 @@ ], "summary": "Create a pull request", "operationId": "repoCreatePullRequest", + "security": null, "parameters": [ { "type": "string", @@ -2978,6 +3098,7 @@ ], "summary": "Get a pull request", "operationId": "repoGetPullRequest", + "security": null, "parameters": [ { "type": "string", @@ -3019,6 +3140,7 @@ ], "summary": "Update a pull request", "operationId": "repoEditPullRequest", + "security": null, "parameters": [ { "type": "string", @@ -3066,6 +3188,7 @@ ], "summary": "Check if a pull request has been merged", "operationId": "repoPullRequestIsMerged", + "security": null, "parameters": [ { "type": "string", @@ -3113,6 +3236,7 @@ ], "summary": "Merge a pull request", "operationId": "repoMergePullRequest", + "security": null, "parameters": [ { "type": "string", @@ -3156,6 +3280,7 @@ ], "summary": "Get a file from a repository", "operationId": "repoGetRawFile", + "security": null, "parameters": [ { "type": "string", @@ -3197,6 +3322,7 @@ ], "summary": "Create a release", "operationId": "repoCreateRelease", + "security": null, "parameters": [ { "type": "string", @@ -3234,6 +3360,7 @@ ], "summary": "Delete a release", "operationId": "repoDeleteRelease", + "security": null, "parameters": [ { "type": "string", @@ -3275,6 +3402,7 @@ ], "summary": "Update a release", "operationId": "repoEditRelease", + "security": null, "parameters": [ { "type": "string", @@ -3322,6 +3450,7 @@ ], "summary": "List a repo's stargazers", "operationId": "repoListStargazers", + "security": null, "parameters": [ { "type": "string", @@ -3355,6 +3484,7 @@ ], "summary": "Get a commit's statuses", "operationId": "repoListStatuses", + "security": null, "parameters": [ { "type": "string", @@ -3393,6 +3523,7 @@ ], "summary": "Create a commit status", "operationId": "repoCreateStatus", + "security": null, "parameters": [ { "type": "string", @@ -3440,6 +3571,7 @@ ], "summary": "List a repo's watchers", "operationId": "repoListSubscribers", + "security": null, "parameters": [ { "type": "string", @@ -3470,6 +3602,7 @@ ], "summary": "Check if the current user is watching a repo", "operationId": "userCurrentCheckSubscription", + "security": null, "parameters": [ { "type": "string", @@ -3498,6 +3631,7 @@ ], "summary": "Watch a repo", "operationId": "userCurrentPutSubscription", + "security": null, "parameters": [ { "type": "string", @@ -3526,6 +3660,7 @@ ], "summary": "Unwatch a repo", "operationId": "userCurrentDeleteSubscription", + "security": null, "parameters": [ { "type": "string", @@ -3559,6 +3694,7 @@ ], "summary": "List a repo's tracked times", "operationId": "repoTrackedTimes", + "security": null, "parameters": [ { "type": "string", @@ -3592,6 +3728,7 @@ ], "summary": "List a user's tracked times in a repo", "operationId": "userTrackedTimes", + "security": null, "parameters": [ { "type": "string", @@ -3632,6 +3769,7 @@ ], "summary": "Delete a hook in a repository", "operationId": "repoDeleteHook", + "security": null, "parameters": [ { "type": "string", @@ -3675,6 +3813,7 @@ ], "summary": "Get a repository by id", "operationId": "repoGetByID", + "security": null, "parameters": [ { "type": "integer", @@ -3701,6 +3840,7 @@ ], "summary": "Get a team", "operationId": "orgGetTeam", + "security": null, "parameters": [ { "type": "integer", @@ -3722,6 +3862,7 @@ ], "summary": "Delete a team", "operationId": "orgDeleteTeam", + "security": null, "parameters": [ { "type": "integer", @@ -3752,6 +3893,7 @@ ], "summary": "Edit a team", "operationId": "orgEditTeam", + "security": null, "parameters": [ { "type": "integer", @@ -3785,6 +3927,7 @@ ], "summary": "List a team's members", "operationId": "orgListTeamMembers", + "security": null, "parameters": [ { "type": "integer", @@ -3811,6 +3954,7 @@ ], "summary": "Add a team member", "operationId": "orgAddTeamMember", + "security": null, "parameters": [ { "type": "integer", @@ -3842,6 +3986,7 @@ ], "summary": "Remove a team member", "operationId": "orgAddTeamMember", + "security": null, "parameters": [ { "type": "integer", @@ -3875,6 +4020,7 @@ ], "summary": "List a team's repos", "operationId": "orgListTeamRepos", + "security": null, "parameters": [ { "type": "integer", @@ -3901,6 +4047,7 @@ ], "summary": "Add a repository to a team", "operationId": "orgAddTeamMember", + "security": null, "parameters": [ { "type": "integer", @@ -3940,6 +4087,7 @@ ], "summary": "Remove a repository from a team", "operationId": "orgAddTeamMember", + "security": null, "parameters": [ { "type": "integer", @@ -3980,6 +4128,7 @@ ], "summary": "Get the authenticated user", "operationId": "userGetCurrent", + "security": null, "responses": { "200": { "$ref": "#/responses/User" @@ -3997,6 +4146,7 @@ ], "summary": "List the authenticated user's email addresses", "operationId": "userListEmails", + "security": null, "responses": { "200": { "$ref": "#/responses/EmailList" @@ -4012,6 +4162,7 @@ ], "summary": "Add email addresses", "operationId": "userAddEmail", + "security": null, "parameters": [ { "name": "body", @@ -4036,6 +4187,7 @@ ], "summary": "Delete email addresses", "operationId": "userDeleteEmail", + "security": null, "parameters": [ { "name": "body", @@ -4062,6 +4214,7 @@ ], "summary": "List the authenticated user's followers", "operationId": "userCurrentListFollowers", + "security": null, "responses": { "200": { "$ref": "#/responses/UserList" @@ -4079,6 +4232,7 @@ ], "summary": "List the users that the authenticated user is following", "operationId": "userCurrentListFollowing", + "security": null, "responses": { "200": { "$ref": "#/responses/UserList" @@ -4093,6 +4247,7 @@ ], "summary": "Check whether a user is followed by the authenticated user", "operationId": "userCurrentCheckFollowing", + "security": null, "parameters": [ { "type": "string", @@ -4119,6 +4274,7 @@ ], "summary": "Follow a user", "operationId": "userCurrentPutFollow", + "security": null, "parameters": [ { "type": "string", @@ -4140,6 +4296,7 @@ ], "summary": "Unfollow a user", "operationId": "userCurrentDeleteFollow", + "security": null, "parameters": [ { "type": "string", @@ -4166,6 +4323,7 @@ ], "summary": "List the authenticated user's GPG keys", "operationId": "userCurrentListGPGKeys", + "security": null, "responses": { "200": { "$ref": "#/responses/GPGKeyList" @@ -4184,6 +4342,7 @@ ], "summary": "Create a GPG key", "operationId": "userCurrentPostGPGKey", + "security": null, "parameters": [ { "name": "Form", @@ -4213,6 +4372,7 @@ ], "summary": "Get a GPG key", "operationId": "userCurrentGetGPGKey", + "security": null, "parameters": [ { "type": "integer", @@ -4240,6 +4400,7 @@ ], "summary": "Remove a GPG key", "operationId": "userCurrentDeleteGPGKey", + "security": null, "parameters": [ { "type": "integer", @@ -4269,6 +4430,7 @@ ], "summary": "List the authenticated user's public keys", "operationId": "userCurrentListKeys", + "security": null, "responses": { "200": { "$ref": "#/responses/PublicKeyList" @@ -4287,6 +4449,7 @@ ], "summary": "Create a public key", "operationId": "userCurrentPostKey", + "security": null, "parameters": [ { "name": "body", @@ -4316,6 +4479,7 @@ ], "summary": "Get a public key", "operationId": "userCurrentGetKey", + "security": null, "parameters": [ { "type": "integer", @@ -4343,6 +4507,7 @@ ], "summary": "Delete a public key", "operationId": "userCurrentDeleteKey", + "security": null, "parameters": [ { "type": "integer", @@ -4358,6 +4523,9 @@ }, "403": { "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" } } } @@ -4372,6 +4540,7 @@ ], "summary": "List the current user's organizations", "operationId": "orgListCurrentUserOrgs", + "security": null, "responses": { "200": { "$ref": "#/responses/OrganizationList" @@ -4389,6 +4558,7 @@ ], "summary": "List the repos that the authenticated user owns or has access to", "operationId": "userCurrentListRepos", + "security": null, "responses": { "200": { "$ref": "#/responses/RepositoryList" @@ -4408,6 +4578,7 @@ ], "summary": "Create a repository", "operationId": "createCurrentUserRepo", + "security": null, "parameters": [ { "name": "body", @@ -4434,6 +4605,7 @@ ], "summary": "The repos that the authenticated user has starred", "operationId": "userCurrentListStarred", + "security": null, "responses": { "200": { "$ref": "#/responses/RepositoryList" @@ -4448,6 +4620,7 @@ ], "summary": "Whether the authenticated is starring the repo", "operationId": "userCurrentCheckStarring", + "security": null, "parameters": [ { "type": "string", @@ -4479,6 +4652,7 @@ ], "summary": "Star the given repo", "operationId": "userCurrentPutStar", + "security": null, "parameters": [ { "type": "string", @@ -4507,6 +4681,7 @@ ], "summary": "Unstar the given repo", "operationId": "userCurrentDeleteStar", + "security": null, "parameters": [ { "type": "string", @@ -4540,6 +4715,7 @@ ], "summary": "List repositories watched by the authenticated user", "operationId": "userCurrentListSubscriptions", + "security": null, "responses": { "200": { "$ref": "#/responses/RepositoryList" @@ -4557,6 +4733,7 @@ ], "summary": "List the current user's tracked times", "operationId": "userCurrentTrackedTimes", + "security": null, "responses": { "200": { "$ref": "#/responses/TrackedTimeList" @@ -4574,6 +4751,7 @@ ], "summary": "List a user's organizations", "operationId": "orgListUserOrgs", + "security": null, "parameters": [ { "type": "string", @@ -4599,6 +4777,7 @@ ], "summary": "Search for users", "operationId": "userSearch", + "security": null, "parameters": [ { "type": "string", @@ -4627,6 +4806,7 @@ ], "summary": "Check if one user is following another user", "operationId": "userCheckFollowing", + "security": null, "parameters": [ { "type": "string", @@ -4663,6 +4843,7 @@ ], "summary": "Get a user", "operationId": "userGet", + "security": null, "parameters": [ { "type": "string", @@ -4692,6 +4873,7 @@ ], "summary": "List the given user's followers", "operationId": "userListFollowers", + "security": null, "parameters": [ { "type": "string", @@ -4718,6 +4900,7 @@ ], "summary": "List the users that the given user is following", "operationId": "userListFollowing", + "security": null, "parameters": [ { "type": "string", @@ -4744,6 +4927,7 @@ ], "summary": "List the given user's GPG keys", "operationId": "userListGPGKeys", + "security": null, "parameters": [ { "type": "string", @@ -4770,6 +4954,7 @@ ], "summary": "List the given user's public keys", "operationId": "userListKeys", + "security": null, "parameters": [ { "type": "string", @@ -4796,6 +4981,7 @@ ], "summary": "List the repos owned by the given user", "operationId": "userListRepos", + "security": null, "parameters": [ { "type": "string", @@ -4822,6 +5008,7 @@ ], "summary": "The repos that the given user has starred", "operationId": "userListStarred", + "security": null, "parameters": [ { "type": "string", @@ -4848,6 +5035,7 @@ ], "summary": "List the repositories watched by a user", "operationId": "userListSubscriptions", + "security": null, "parameters": [ { "type": "string", @@ -4873,6 +5061,7 @@ ], "summary": "List the authenticated user's access tokens", "operationId": "userGetTokens", + "security": null, "responses": { "200": { "$ref": "#/responses/AccessTokenList" @@ -4891,6 +5080,7 @@ ], "summary": "Create an access token", "operationId": "userCreateToken", + "security": null, "parameters": [ { "type": "string", @@ -4916,6 +5106,7 @@ ], "summary": "Returns the version of the Gitea application", "operationId": "getVersion", + "security": null, "responses": { "200": { "$ref": "#/responses/ServerVersion" @@ -6268,6 +6459,10 @@ "format": "date-time", "x-go-name": "Created" }, + "fingerprint": { + "type": "string", + "x-go-name": "Fingerprint" + }, "id": { "type": "integer", "format": "int64", @@ -6281,10 +6476,6 @@ "type": "string", "x-go-name": "Title" }, - "fingerprint": { - "type": "string", - "x-go-name": "Fingerprint" - }, "url": { "type": "string", "x-go-name": "URL" @@ -7182,4 +7373,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 19f24aed8f8..fc1d3da2f14 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -236,3 +236,48 @@ func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) { } user.CreateUserPublicKey(ctx, form, u.ID) } + +// DeleteUserPublicKey api for deleting a user's public key +func DeleteUserPublicKey(ctx *context.APIContext) { + // swagger:operation DELETE /admin/users/{username}/keys/{id} admin adminDeleteUserPublicKey + // --- + // summary: Delete a user's public key + // produces: + // - application/json + // parameters: + // - name: username + // in: path + // description: username of user + // type: string + // required: true + // - name: id + // in: path + // description: id of the key to delete + // type: integer + // required: true + // responses: + // "204": + // "$ref": "#/responses/empty" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + u := user.GetUserByParams(ctx) + if ctx.Written() { + return + } + + if err := models.DeletePublicKey(u, ctx.ParamsInt64(":id")); err != nil { + if models.IsErrKeyNotExist(err) { + ctx.Status(404) + } else if models.IsErrKeyAccessDenied(err) { + ctx.Error(403, "", "You do not have access to this key") + } else { + ctx.Error(500, "DeleteUserPublicKey", err) + } + return + } + log.Trace("Key deleted by admin(%s): %s", ctx.User.Name, u.Name) + + ctx.Status(204) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 2aa27af0914..f6ed844d4a8 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -542,7 +542,10 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/:username", func() { m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser). Delete(admin.DeleteUser) - m.Post("/keys", bind(api.CreateKeyOption{}), admin.CreatePublicKey) + m.Group("/keys", func() { + m.Post("", bind(api.CreateKeyOption{}), admin.CreatePublicKey) + m.Delete("/:id", admin.DeleteUserPublicKey) + }) m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg) m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo) }) diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 3649dac9b96..c36ef763dd9 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -178,8 +178,12 @@ func DeletePublicKey(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "403": // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" if err := models.DeletePublicKey(ctx.User, ctx.ParamsInt64(":id")); err != nil { - if models.IsErrKeyAccessDenied(err) { + if models.IsErrKeyNotExist(err) { + ctx.Status(404) + } else if models.IsErrKeyAccessDenied(err) { ctx.Error(403, "", "You do not have access to this key") } else { ctx.Error(500, "DeletePublicKey", err)