From d3f7b91ae41a41d56a78de7eb065f15b1a155851 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 23 Jan 2025 12:32:10 +0100 Subject: [PATCH] webhooks: implement internal webhook users AUTH_INTERNAL requests previously could not register webhooks. This commit adds the necessary changes to allow for this. --- auth/middleware.go | 9 ++++++--- webhooks/config.go | 8 ++++++-- webhooks/context.go | 46 ++++++++++++++++++++++++++++++--------------- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/auth/middleware.go b/auth/middleware.go index ebfb5f814af2d55def697cb42e0368bd9c95f35b..e183059294c9047ef8f86ebf057bb03f2376b5ad 100644 --- a/auth/middleware.go +++ b/auth/middleware.go @@ -77,8 +77,10 @@ type AuthContext struct { // Only filled out if AuthMethod == AUTH_OAUTH2 or AUTH_WEBHOOK BearerToken *BearerToken - Grants Grants TokenHash [64]byte + + // AUTH_OAUTH2, AUTH_WEBHOOK, AUTH_INTERNAL + Grants Grants } func (authctx *AuthContext) Access(scope, kind string) error { @@ -295,6 +297,7 @@ func internalAuth(payload []byte, w http.ResponseWriter, r *http.Request, next h } auth.InternalAuth = internalAuth + auth.Grants, _ = DecodeGrants(r.Context(), "") auth.IPAddress = r.RemoteAddr var route []string @@ -660,8 +663,8 @@ func LegacyOAuth(bearer string, hash [64]byte, w http.ResponseWriter, // webhook to be fired. func WebhookAuth(ctx context.Context, auth *AuthContext, tokenHash [64]byte, rawGrants string, clientID *string, - expires time.Time) (context.Context, error) { - if time.Now().UTC().After(expires) { + expires *time.Time) (context.Context, error) { + if expires != nil && time.Now().UTC().After(*expires) { return nil, fmt.Errorf("The authentication token used to create this webhook has expired") } diff --git a/webhooks/config.go b/webhooks/config.go index e85c3edc76658fca9a3074b7a1b1daa596dc094d..f5490abb032e597eb57069c681d134e46a023fd1 100644 --- a/webhooks/config.go +++ b/webhooks/config.go @@ -7,6 +7,7 @@ import ( "time" "git.sr.ht/~sircmpwn/core-go/auth" + "git.sr.ht/~sircmpwn/core-go/config" sq "github.com/Masterminds/squirrel" ) @@ -53,8 +54,11 @@ func NewAuthConfig(ctx context.Context) (AuthConfig, error) { // TODO: Should this work? return AuthConfig{}, fmt.Errorf("Native webhooks are not supported with web authentication") case auth.AUTH_INTERNAL: - // TODO: Should this work? - panic(fmt.Errorf("Internal webtoken auth is not supported")) + nodeID := config.ServiceName(ctx) + return AuthConfig{ + AuthMethod: user.AuthMethod, + NodeID: &nodeID, + }, nil case auth.AUTH_WEBHOOK: panic(fmt.Errorf("Recursive webhook auth is not supported")) } diff --git a/webhooks/context.go b/webhooks/context.go index 4c645dbcc16be739e568dac0558f15fe785ad246..9867381b9118157316a30b64c971902003d091d3 100644 --- a/webhooks/context.go +++ b/webhooks/context.go @@ -49,23 +49,39 @@ func Payload(ctx context.Context) (interface{}, error) { // the configuration of a secondary authentication and GraphQL context. func (webhook *WebhookContext) Exec(ctx context.Context, schema graphql.ExecutableSchema) ([]byte, error) { + var ( + err error + tokenHash [64]byte + ) sub := webhook.Subscription - if sub.AuthMethod != auth.AUTH_OAUTH2 { - panic(fmt.Errorf("TODO")) - } - tslice, err := hex.DecodeString(*sub.TokenHash) - if err != nil { - panic(err) - } - var tokenHash [64]byte - copy(tokenHash[:], tslice) - ctx, err = auth.WebhookAuth(ctx, webhook.User, - tokenHash, *sub.Grants, sub.ClientID, *sub.Expires) - if err != nil { - // TODO: This codepath can occur when the token has expired, and we may - // want to communicate this to the user. - return nil, err + switch sub.AuthMethod { + case auth.AUTH_OAUTH2: + tslice, err := hex.DecodeString(*sub.TokenHash) + if err != nil { + panic(err) + } + if sub.Expires == nil { + panic(fmt.Errorf("OAuth 2 token has no expiry?")) + } + + copy(tokenHash[:], tslice) + ctx, err = auth.WebhookAuth(ctx, webhook.User, + tokenHash, *sub.Grants, sub.ClientID, sub.Expires) + if err != nil { + // TODO: This codepath can occur when the token has + // expired, and we may want to communicate this to the + // user. + return nil, err + } + case auth.AUTH_INTERNAL: + ctx, err = auth.WebhookAuth(ctx, webhook.User, + tokenHash, "", nil, nil) + if err != nil { + panic(err) + } + default: + panic(fmt.Errorf("Unsupported authentication context for webhook")) } exec := executor.New(schema)