mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-28 21:30:06 +00:00
646ea0f253
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>
77 lines
2.3 KiB
Go
77 lines
2.3 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package deploykey
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"gitea.dev/models/db"
|
|
"gitea.dev/models/perm"
|
|
"gitea.dev/modules/base"
|
|
"gitea.dev/modules/util"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
const (
|
|
DeployTokenPrefix = "gdt_" // lets a secret scanner recognize a leaked token
|
|
deployTokenLength = 43 // 256 bits of entropy over the 62 alphanumerical characters
|
|
)
|
|
|
|
func (key *DeployKey) generateToken() {
|
|
key.Token = DeployTokenPrefix + util.CryptoRandomString(deployTokenLength)
|
|
key.TokenHash = base.EncodeSha256(key.Token)
|
|
key.Fingerprint = key.Token[:len(DeployTokenPrefix)+2] + "********" + key.Token[len(key.Token)-2:]
|
|
}
|
|
|
|
// AddDeployKeyToken adds a token that authenticates git HTTP requests for one repository.
|
|
// The plaintext token is only readable on the returned key.
|
|
func AddDeployKeyToken(ctx context.Context, repoID int64, name string, accessMode perm.AccessMode) (*DeployKey, error) {
|
|
key := &DeployKey{
|
|
RepoID: repoID,
|
|
KeyType: KeyTypeToken,
|
|
Name: name,
|
|
Mode: accessMode,
|
|
}
|
|
key.generateToken()
|
|
|
|
return db.WithTx2(ctx, func(ctx context.Context) (*DeployKey, error) {
|
|
if err := checkDeployKeyName(ctx, repoID, name); err != nil {
|
|
return nil, err
|
|
}
|
|
return key, db.Insert(ctx, key)
|
|
})
|
|
}
|
|
|
|
// RegenerateDeployKeyToken replaces the token value of an existing deploy token, keeping its name and access mode.
|
|
func RegenerateDeployKeyToken(ctx context.Context, repoID, keyID int64) (*DeployKey, error) {
|
|
key, err := GetDeployKeyByID(ctx, repoID, keyID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if key.KeyType != KeyTypeToken {
|
|
return nil, ErrDeployKeyNotExist{keyID, 0, repoID}
|
|
}
|
|
|
|
key.generateToken()
|
|
_, err = db.GetEngine(ctx).ID(key.ID).Cols("token_hash", "fingerprint").NoAutoTime().Update(key)
|
|
return key, err
|
|
}
|
|
|
|
// VerifyDeployKeyToken returns the deploy-key which the given plaintext token authenticates.
|
|
func VerifyDeployKeyToken(ctx context.Context, token string) (*DeployKey, error) {
|
|
if !strings.HasPrefix(token, DeployTokenPrefix) { // spares a query for every password of a normal user
|
|
return nil, ErrDeployKeyNotExist{}
|
|
}
|
|
|
|
key, exist, err := db.Get[DeployKey](ctx, builder.Eq{"token_hash": base.EncodeSha256(token), "key_type": KeyTypeToken})
|
|
if err != nil {
|
|
return nil, err
|
|
} else if !exist {
|
|
return nil, ErrDeployKeyNotExist{}
|
|
}
|
|
return key, nil
|
|
}
|