feat: add deploy tokens (#37306)

Deploy keys only work over SSH. A deploy token is their counterpart for HTTPS: a repository scoped credential, used as the password of a Git request, with read or read and write access. It covers Git operations and LFS, and can be regenerated in place.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude Mythos <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
ToastyTheBot
2026-08-27 03:32:44 +08:00
committed by GitHub
parent 3c4d5a6a5c
commit 646ea0f253
76 changed files with 1594 additions and 831 deletions
+52
View File
@@ -0,0 +1,52 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package deploykey
import (
"testing"
"gitea.dev/models/db"
"gitea.dev/models/perm"
"gitea.dev/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDeployToken(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
key, err := AddDeployKeyToken(t.Context(), 1, "ci", perm.AccessModeWrite)
require.NoError(t, err)
assert.False(t, key.IsReadOnly())
assert.Len(t, key.Token, len(DeployTokenPrefix)+deployTokenLength)
got, err := VerifyDeployKeyToken(t.Context(), key.Token)
require.NoError(t, err)
assert.Equal(t, key.ID, got.ID)
assert.Empty(t, got.Token, "the token itself is never stored")
_, err = VerifyDeployKeyToken(t.Context(), "not-a-token")
assert.True(t, IsErrDeployKeyNotExist(err))
_, err = AddDeployKeyToken(t.Context(), 1, "ci", perm.AccessModeWrite)
assert.True(t, IsErrDeployKeyNameAlreadyUsed(err))
regenerated, err := RegenerateDeployKeyToken(t.Context(), 1, key.ID)
require.NoError(t, err)
assert.Equal(t, key.Name, regenerated.Name)
assert.Equal(t, key.Mode, regenerated.Mode)
assert.False(t, unittest.AssertExistsAndLoadBean(t, &DeployKey{ID: key.ID}).HasUsed(), "regenerating is not a use")
_, err = VerifyDeployKeyToken(t.Context(), key.Token)
assert.True(t, IsErrDeployKeyNotExist(err), "the old token stops working")
_, err = VerifyDeployKeyToken(t.Context(), regenerated.Token)
require.NoError(t, err)
// an SSH deploy-key has no token to regenerate
sshKey := &DeployKey{RepoID: 1, KeyType: KeyTypeSSH, Name: "ssh"}
require.NoError(t, db.Insert(t.Context(), sshKey))
_, err = RegenerateDeployKeyToken(t.Context(), 1, sshKey.ID)
assert.True(t, IsErrDeployKeyNotExist(err))
}