code.gitea.io/gitea@v1.21.7/routers/api/actions/runner/interceptor.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package runner 5 6 import ( 7 "context" 8 "crypto/subtle" 9 "errors" 10 "strings" 11 12 actions_model "code.gitea.io/gitea/models/actions" 13 auth_model "code.gitea.io/gitea/models/auth" 14 "code.gitea.io/gitea/modules/log" 15 "code.gitea.io/gitea/modules/timeutil" 16 "code.gitea.io/gitea/modules/util" 17 18 "github.com/bufbuild/connect-go" 19 "google.golang.org/grpc/codes" 20 "google.golang.org/grpc/status" 21 ) 22 23 const ( 24 uuidHeaderKey = "x-runner-uuid" 25 tokenHeaderKey = "x-runner-token" 26 // Deprecated: will be removed after Gitea 1.20 released. 27 versionHeaderKey = "x-runner-version" 28 ) 29 30 var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unaryFunc connect.UnaryFunc) connect.UnaryFunc { 31 return func(ctx context.Context, request connect.AnyRequest) (connect.AnyResponse, error) { 32 methodName := getMethodName(request) 33 if methodName == "Register" { 34 return unaryFunc(ctx, request) 35 } 36 uuid := request.Header().Get(uuidHeaderKey) 37 token := request.Header().Get(tokenHeaderKey) 38 // TODO: version will be removed from request header after Gitea 1.20 released. 39 // And Gitea will not try to read version from reuqest header 40 version := request.Header().Get(versionHeaderKey) 41 42 runner, err := actions_model.GetRunnerByUUID(ctx, uuid) 43 if err != nil { 44 if errors.Is(err, util.ErrNotExist) { 45 return nil, status.Error(codes.Unauthenticated, "unregistered runner") 46 } 47 return nil, status.Error(codes.Internal, err.Error()) 48 } 49 if subtle.ConstantTimeCompare([]byte(runner.TokenHash), []byte(auth_model.HashToken(token, runner.TokenSalt))) != 1 { 50 return nil, status.Error(codes.Unauthenticated, "unregistered runner") 51 } 52 53 cols := []string{"last_online"} 54 55 // TODO: version will be removed from request header after Gitea 1.20 released. 56 // And Gitea will not try to read version from reuqest header 57 version, _ = util.SplitStringAtByteN(version, 64) 58 if !util.IsEmptyString(version) && runner.Version != version { 59 runner.Version = version 60 cols = append(cols, "version") 61 } 62 runner.LastOnline = timeutil.TimeStampNow() 63 if methodName == "UpdateTask" || methodName == "UpdateLog" { 64 runner.LastActive = timeutil.TimeStampNow() 65 cols = append(cols, "last_active") 66 } 67 if err := actions_model.UpdateRunner(ctx, runner, cols...); err != nil { 68 log.Error("can't update runner status: %v", err) 69 } 70 71 ctx = context.WithValue(ctx, runnerCtxKey{}, runner) 72 return unaryFunc(ctx, request) 73 } 74 })) 75 76 func getMethodName(req connect.AnyRequest) string { 77 splits := strings.Split(req.Spec().Procedure, "/") 78 if len(splits) > 0 { 79 return splits[len(splits)-1] 80 } 81 return "" 82 } 83 84 type runnerCtxKey struct{} 85 86 func GetRunner(ctx context.Context) *actions_model.ActionRunner { 87 if v := ctx.Value(runnerCtxKey{}); v != nil { 88 if r, ok := v.(*actions_model.ActionRunner); ok { 89 return r 90 } 91 } 92 return nil 93 }