From 693db80296ab2e3c9ad1dc1c1d8a5ff4990b81fb Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Fri, 26 Apr 2024 11:48:42 +0200 Subject: [PATCH] api: Allow unauthenticated access to user's SSH keys This patch relaxes constraints on getting user's SSH keys via the JSON API. The same has been allowed by both GitHub and Gitlab and the output is already readable via http://domain/user.keys endpoint. The benefit of allowing it via the API are twofold: first this is a structured output and second it can be CORS-enabled. As a privacy precaution the `Title` property is set to an empty string if the request is unauthenticated. Fixes: https://github.com/go-gitea/gitea/issues/30681 --- routers/api/v1/api.go | 8 +++++++- routers/api/v1/user/key.go | 13 +++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 73071aa8df..a3a482c6e5 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -945,7 +945,6 @@ func Routes() *web.Route { // Users (requires user scope) m.Group("/users", func() { m.Group("/{username}", func() { - m.Get("/keys", user.ListPublicKeys) m.Get("/gpg_keys", user.ListGPGKeys) m.Get("/followers", user.ListFollowers) @@ -960,6 +959,13 @@ func Routes() *web.Route { }, context.UserAssignmentAPI()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) + // Users SSH keys (publicly readable) + m.Group("/users", func() { + m.Group("/{username}", func() { + m.Get("/keys", user.ListPublicKeys) + }, context.UserAssignmentAPI()) + }) + // Users (requires user scope) m.Group("/user", func() { m.Get("", user.GetAuthenticatedUser) diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index d9456e7ec6..b70fbf6d66 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -7,6 +7,7 @@ import ( std_ctx "context" "fmt" "net/http" + "strings" asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" @@ -89,8 +90,16 @@ func listPublicKeys(ctx *context.APIContext, user *user_model.User) { apiKeys := make([]*api.PublicKey, len(keys)) for i := range keys { apiKeys[i] = convert.ToPublicKey(apiLink, keys[i]) - if ctx.Doer.IsAdmin || ctx.Doer.ID == keys[i].OwnerID { - apiKeys[i], _ = appendPrivateInformation(ctx, apiKeys[i], keys[i], user) + if ctx.Doer != nil { + if ctx.Doer.IsAdmin || ctx.Doer.ID == keys[i].OwnerID { + apiKeys[i], _ = appendPrivateInformation(ctx, apiKeys[i], keys[i], user) + } + } else { + // unauthenticated requests will not receive the title property + // to preserve privacy + apiKeys[i].Title = "" + // the key comment is truncated to preserve privacy + apiKeys[i].Key = strings.Join(strings.Split(apiKeys[i].Key, " ")[:2], " ") } }