forgejo/models/actions/forgejo.go
Emmanuel BENOÎT 320ab7ed7f
feat(cli): allow updates to runners' secrets
This commit allows the `forgejo-cli actions register` command to change
an existing runner's secret, as discussed in #4610.

It refactors `RegisterRunner` to extract the code that hashes the token,
moving this code to a method called `UpdateSecret` on `ActionRunner`.
A test for the method has been added.

The `RegisterRunner` function is updated so that:
- it relies on `ActionRunner.UpdateSecret` when creating new runners,
- it checks whether an existing runner's secret still matches the one
  passed on the command line,
- it updates the runner's secret if it wasn't created and it no longer
  matches.

A test has been added for the new behaviour.
2024-07-22 11:55:43 +02:00

85 lines
2 KiB
Go

// SPDX-License-Identifier: MIT
package actions
import (
"context"
"crypto/subtle"
"fmt"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/util"
gouuid "github.com/google/uuid"
)
func RegisterRunner(ctx context.Context, ownerID, repoID int64, token string, labels *[]string, name, version string) (*ActionRunner, error) {
uuid, err := gouuid.FromBytes([]byte(token[:16]))
if err != nil {
return nil, fmt.Errorf("gouuid.FromBytes %v", err)
}
uuidString := uuid.String()
var runner ActionRunner
has, err := db.GetEngine(ctx).Where("uuid=?", uuidString).Get(&runner)
if err != nil {
return nil, fmt.Errorf("GetRunner %v", err)
}
var mustUpdateSecret bool
if has {
//
// The runner exists, check if the rest of the token has changed.
//
mustUpdateSecret = subtle.ConstantTimeCompare(
[]byte(runner.TokenHash),
[]byte(auth_model.HashToken(token, runner.TokenSalt)),
) != 1
} else {
//
// The runner does not exist yet, create it
//
runner = ActionRunner{
UUID: uuidString,
AgentLabels: []string{},
}
if err := runner.UpdateSecret(token); err != nil {
return &runner, fmt.Errorf("can't set new runner's secret: %w", err)
}
if err := CreateRunner(ctx, &runner); err != nil {
return &runner, fmt.Errorf("can't create new runner %w", err)
}
}
//
// Update the existing runner
//
name, _ = util.SplitStringAtByteN(name, 255)
cols := []string{"name", "owner_id", "repo_id", "version"}
runner.Name = name
runner.OwnerID = ownerID
runner.RepoID = repoID
runner.Version = version
if labels != nil {
runner.AgentLabels = *labels
cols = append(cols, "agent_labels")
}
if mustUpdateSecret {
if err := runner.UpdateSecret(token); err != nil {
return &runner, fmt.Errorf("can't change runner's secret: %w", err)
}
cols = append(cols, "token_hash", "token_salt")
}
if err := UpdateRunner(ctx, &runner, cols...); err != nil {
return &runner, fmt.Errorf("can't update the runner %+v %w", runner, err)
}
return &runner, nil
}