~bigbes/core-go

07f694d2fb31e0278aa2c8fb28c7b28adb5aac2a — Drew DeVault 4 years ago 590c9c4
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.
4 files changed, 51 insertions(+), 31 deletions(-)

M auth/middleware.go
M webhooks/config.go
M webhooks/context.go
M webhooks/queue.go
M auth/middleware.go => auth/middleware.go +6 -6
@@ 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

M webhooks/config.go => webhooks/config.go +25 -12
@@ 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")

M webhooks/context.go => webhooks/context.go +5 -2
@@ 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.

M webhooks/queue.go => webhooks/queue.go +15 -11
@@ 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)