From 67701771afeb68274e04172f1b65fc994f87215c Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Tue, 9 Aug 2022 23:56:18 +0200 Subject: [PATCH] Add support for NuGet API keys (#20721) (#20734) Co-authored-by: delvh Co-authored-by: wxiaoguang Co-authored-by: Lauris BH --- docs/content/doc/packages/nuget.en-us.md | 2 ++ integrations/api_packages_nuget_test.go | 11 ++++++ routers/api/packages/api.go | 1 + routers/api/packages/nuget/auth.go | 45 ++++++++++++++++++++++++ 4 files changed, 59 insertions(+) create mode 100644 routers/api/packages/nuget/auth.go diff --git a/docs/content/doc/packages/nuget.en-us.md b/docs/content/doc/packages/nuget.en-us.md index 0b92d85a3d4e..40715358204a 100644 --- a/docs/content/doc/packages/nuget.en-us.md +++ b/docs/content/doc/packages/nuget.en-us.md @@ -47,6 +47,8 @@ For example: dotnet nuget add source --name gitea --username testuser --password password123 https://gitea.example.com/api/packages/testuser/nuget/index.json ``` +You can add the source without credentials and use the [`--api-key`](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-nuget-push) parameter when publishing packages. In this case you need to provide a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}). + ## Publish a package Publish a package by running the following command: diff --git a/integrations/api_packages_nuget_test.go b/integrations/api_packages_nuget_test.go index 346f391f82fc..06eb485541ef 100644 --- a/integrations/api_packages_nuget_test.go +++ b/integrations/api_packages_nuget_test.go @@ -24,9 +24,16 @@ import ( "github.com/stretchr/testify/assert" ) +func addNuGetAPIKeyHeader(request *http.Request, token string) *http.Request { + request.Header.Set("X-NuGet-ApiKey", token) + return request +} + func TestPackageNuGet(t *testing.T) { defer prepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) + token := getUserToken(t, user.Name) packageName := "test.package" packageVersion := "1.0.3" @@ -60,6 +67,10 @@ func TestPackageNuGet(t *testing.T) { req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) + req = addNuGetAPIKeyHeader(req, token) resp := MakeRequest(t, req, http.StatusOK) var result nuget.ServiceIndexResponse diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index bb9a42e33dc9..b27569aba45c 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -45,6 +45,7 @@ func Routes() *web.Route { authMethods := []auth.Method{ &auth.OAuth2{}, &auth.Basic{}, + &nuget.Auth{}, &conan.Auth{}, } if setting.Service.EnableReverseProxyAuth { diff --git a/routers/api/packages/nuget/auth.go b/routers/api/packages/nuget/auth.go new file mode 100644 index 000000000000..26a5b9018931 --- /dev/null +++ b/routers/api/packages/nuget/auth.go @@ -0,0 +1,45 @@ +// Copyright 2022 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 nuget + +import ( + "net/http" + + "code.gitea.io/gitea/models" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/services/auth" +) + +type Auth struct{} + +func (a *Auth) Name() string { + return "nuget" +} + +// https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#request-parameters +func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) *user_model.User { + token, err := models.GetAccessTokenBySHA(req.Header.Get("X-NuGet-ApiKey")) + if err != nil { + if !(models.IsErrAccessTokenNotExist(err) || models.IsErrAccessTokenEmpty(err)) { + log.Error("GetAccessTokenBySHA: %v", err) + } + return nil + } + + u, err := user_model.GetUserByID(token.UID) + if err != nil { + log.Error("GetUserByID: %v", err) + return nil + } + + token.UpdatedUnix = timeutil.TimeStampNow() + if err := models.UpdateAccessToken(token); err != nil { + log.Error("UpdateAccessToken: %v", err) + } + + return u +}