From 07f694d2fb31e0278aa2c8fb28c7b28adb5aac2a Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 24 Aug 2021 15:52:29 +0200 Subject: [PATCH] webhooks: expand auth configuration The purpose of this change is to enable internal webhooks to be configured in GQL webhook tables. A webhook subscription now includes the auth method field which is appropriate, which is limited to either OAUTH2 or INTERNAL. In the former case, the previous set of fields will be valid, and in the latter case, the NodeID field will be valid. This will allow us to register webhook subscriptions for internal use. --- auth/middleware.go | 12 ++++++------ webhooks/config.go | 37 +++++++++++++++++++++++++------------ webhooks/context.go | 7 +++++-- webhooks/queue.go | 26 +++++++++++++++----------- 4 files changed, 51 insertions(+), 31 deletions(-) diff --git a/auth/middleware.go b/auth/middleware.go index 4334ea3339e7467fb3f0729b55f807e755015812..0483e6ca61948b9d25f8327a6e32b90f43028534 100644 --- a/auth/middleware.go +++ b/auth/middleware.go @@ -49,11 +49,11 @@ const ( ) const ( - AUTH_OAUTH_LEGACY = iota - AUTH_OAUTH2 = iota - AUTH_COOKIE = iota - AUTH_INTERNAL = iota - AUTH_WEBHOOK = iota + AUTH_OAUTH_LEGACY = "OAUTH_LEGACY" + AUTH_OAUTH2 = "OAUTH2" + AUTH_COOKIE = "COOKIE" + AUTH_INTERNAL = "INTERNAL" + AUTH_WEBHOOK = "WEBHOOK" ) type AuthContext struct { @@ -67,7 +67,7 @@ type AuthContext struct { Location *string Bio *string SuspensionNotice *string - AuthMethod int + AuthMethod string // Only set for meta.sr.ht-api PGPKey *string diff --git a/webhooks/config.go b/webhooks/config.go index a381eb4cad356eebeb2f6776956a696f1edcc69d..f93f4465dd7fc5874f280622215cba7cd1e16ea1 100644 --- a/webhooks/config.go +++ b/webhooks/config.go @@ -9,11 +9,19 @@ import ( "git.sr.ht/~sircmpwn/core-go/auth" ) +// The following invariants apply to AuthConfig: +// 1. AuthMethod will be either OAUTH2 or INTERNAL +// 2. If OAUTH2, TokenHash, Grants, and Expires will be non-nil, and ClientID +// may be non-nil, and NodeID will be nil. +// 3. If INTERNAL, TokenHash, Grants, Expires, and ClientID will be nil, and +// NodeID will be non-nil. type AuthConfig struct { - TokenHash string - Grants string - ClientID *string - Expires time.Time + AuthMethod string + TokenHash *string + Grants *string + ClientID *string + Expires *time.Time + NodeID *string } // Pulls auth details out of the config context and returns a structure of all @@ -25,16 +33,21 @@ func NewAuthConfig(ctx context.Context) (AuthConfig, error) { case auth.AUTH_OAUTH_LEGACY: return AuthConfig{}, fmt.Errorf("Native webhooks are not supported with legacy OAuth") case auth.AUTH_OAUTH2: - ac := AuthConfig{ - TokenHash: hex.EncodeToString(user.TokenHash[:]), - Grants: user.BearerToken.Grants, - Expires: user.BearerToken.Expires.Time(), - } + tokenHash := hex.EncodeToString(user.TokenHash[:]) + grants := user.BearerToken.Grants + expires := user.BearerToken.Expires.Time() + var clientID *string if user.BearerToken.ClientID != "" { - clientID := user.BearerToken.ClientID - ac.ClientID = &clientID + _clientID := user.BearerToken.ClientID + clientID = &_clientID } - return ac, nil + return AuthConfig { + AuthMethod: user.AuthMethod, + TokenHash: &tokenHash, + Grants: &grants, + Expires: &expires, + ClientID: clientID, + }, nil case auth.AUTH_COOKIE: // TODO: Should this work? return AuthConfig{}, fmt.Errorf("Native webhooks are not supported with web authentication") diff --git a/webhooks/context.go b/webhooks/context.go index 082f7d2da8efca92934bd638939e7f1c339c0592..0ebc794484a9e2fa3bd2a4e8e340191b1eb6a19d 100644 --- a/webhooks/context.go +++ b/webhooks/context.go @@ -50,7 +50,10 @@ func Payload(ctx context.Context) (interface{}, error) { func (webhook *WebhookContext) Exec(ctx context.Context, schema graphql.ExecutableSchema) ([]byte, error) { sub := webhook.Subscription - tslice, err := hex.DecodeString(sub.TokenHash) + if sub.AuthMethod != auth.AUTH_OAUTH2 { + panic("TODO") + } + tslice, err := hex.DecodeString(*sub.TokenHash) if err != nil { panic(err) } @@ -58,7 +61,7 @@ func (webhook *WebhookContext) Exec(ctx context.Context, var tokenHash [64]byte copy(tokenHash[:], tslice) ctx, err = auth.WebhookAuth(ctx, webhook.User, - tokenHash, sub.Grants, sub.ClientID, sub.Expires) + 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. diff --git a/webhooks/queue.go b/webhooks/queue.go index fc6b8cf6d9df735f669b62d1b07a6a580627c71d..7c660ee2152f2b0266ef99a967772a7dbe700cb1 100644 --- a/webhooks/queue.go +++ b/webhooks/queue.go @@ -28,13 +28,15 @@ type WebhookQueue struct { } type WebhookSubscription struct { - ID int - URL string - Query string - TokenHash string - Grants string - ClientID *string - Expires time.Time + ID int + URL string + Query string + AuthMethod string + TokenHash *string + Grants *string + ClientID *string + NodeID *string + Expires *time.Time } // Creates a new worker for delivering webhooks. The caller must start the @@ -125,8 +127,9 @@ func (queue *WebhookQueue) fetchSubscriptions(ctx context.Context, ) if rows, err = q. Columns("sub.id", "sub.url", "sub.query", - "sub.token_hash", "sub.grants", "sub.client_id", - "sub.expires"). + "sub.auth_method", + "sub.token_hash", "sub.grants", "sub.client_id", "sub.expires", + "sub.node_id"). Where("? = ANY(sub.events)", event). PlaceholderFormat(sq.Dollar). RunWith(tx). @@ -138,8 +141,9 @@ func (queue *WebhookQueue) fetchSubscriptions(ctx context.Context, for rows.Next() { var sub WebhookSubscription if err := rows.Scan(&sub.ID, &sub.URL, &sub.Query, - &sub.TokenHash, &sub.Grants, &sub.ClientID, - &sub.Expires); err != nil { + &sub.AuthMethod, + &sub.TokenHash, &sub.Grants, &sub.ClientID, &sub.Expires, + &sub.NodeID); err != nil { panic(err) } subs = append(subs, &sub)