You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

interceptor.go 2.8KB

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