// Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package runner import ( "context" "crypto/subtle" "errors" "strings" actions_model "code.gitea.io/gitea/models/actions" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" "github.com/bufbuild/connect-go" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) const ( uuidHeaderKey = "x-runner-uuid" tokenHeaderKey = "x-runner-token" ) var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unaryFunc connect.UnaryFunc) connect.UnaryFunc { return func(ctx context.Context, request connect.AnyRequest) (connect.AnyResponse, error) { methodName := getMethodName(request) if methodName == "Register" { return unaryFunc(ctx, request) } uuid := request.Header().Get(uuidHeaderKey) token := request.Header().Get(tokenHeaderKey) runner, err := actions_model.GetRunnerByUUID(ctx, uuid) if err != nil { if errors.Is(err, util.ErrNotExist) { return nil, status.Error(codes.Unauthenticated, "unregistered runner") } return nil, status.Error(codes.Internal, err.Error()) } if subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))) != 1 { return nil, status.Error(codes.Unauthenticated, "unregistered runner") } cols := []string{"last_online"} runner.LastOnline = timeutil.TimeStampNow() if methodName == "UpdateTask" || methodName == "UpdateLog" { runner.LastActive = timeutil.TimeStampNow() cols = append(cols, "last_active") } if err := actions_model.UpdateRunner(ctx, runner, cols...); err != nil { log.Error("can't update runner status: %v", err) } ctx = context.WithValue(ctx, runnerCtxKey{}, runner) return unaryFunc(ctx, request) } })) func getMethodName(req connect.AnyRequest) string { splits := strings.Split(req.Spec().Procedure, "/") if len(splits) > 0 { return splits[len(splits)-1] } return "" } type runnerCtxKey struct{} func GetRunner(ctx context.Context) *actions_model.ActionRunner { if v := ctx.Value(runnerCtxKey{}); v != nil { if r, ok := v.(*actions_model.ActionRunner); ok { return r } } return nil }