Respond with unauthenticated on compliance page missing membership
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -673,6 +673,15 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s AuthService) GetMagincLinkEmail(ctx context.Context, tokenString string) (mail.Addr, error) {
|
||||||
|
payload, err := statelesstoken.ValidateToken[MagicLinkData](s.tokenSecret, TokenTypeMagicLink, tokenString)
|
||||||
|
if err != nil {
|
||||||
|
return mail.Nil, NewInvalidTokenError()
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload.Data.Email, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s AuthService) OpenSessionWithMagicLink(ctx context.Context, tokenString string) (*coredata.Identity, *coredata.Session, *string, error) {
|
func (s AuthService) OpenSessionWithMagicLink(ctx context.Context, tokenString string) (*coredata.Identity, *coredata.Session, *string, error) {
|
||||||
var (
|
var (
|
||||||
now = time.Now()
|
now = time.Now()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func NewGraphQLHandler(iamSvc *iam.Service, trustSvc *trust.Service, esignSvc *e
|
|||||||
sessionCookie: authn.NewCookie(&cookieConfig),
|
sessionCookie: authn.NewCookie(&cookieConfig),
|
||||||
},
|
},
|
||||||
Directives: schema.DirectiveRoot{
|
Directives: schema.DirectiveRoot{
|
||||||
Nda: newNDADirectiveFunc(logger, trustSvc, esignSvc),
|
Nda: newNDADirective(logger, esignSvc),
|
||||||
Session: session.Directive,
|
Session: session.Directive,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ package trust_v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql"
|
"github.com/99designs/gqlgen/graphql"
|
||||||
"go.gearno.de/kit/log"
|
"go.gearno.de/kit/log"
|
||||||
@@ -25,12 +24,10 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/server/api/authn"
|
"go.probo.inc/probo/pkg/server/api/authn"
|
||||||
"go.probo.inc/probo/pkg/server/api/compliancepage"
|
"go.probo.inc/probo/pkg/server/api/compliancepage"
|
||||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||||
"go.probo.inc/probo/pkg/trust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newNDADirectiveFunc(
|
func newNDADirective(
|
||||||
logger *log.Logger,
|
logger *log.Logger,
|
||||||
trustSvc *trust.Service,
|
|
||||||
esignSvc *esign.Service,
|
esignSvc *esign.Service,
|
||||||
) func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
|
) func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
|
||||||
return func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
|
return func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
|
||||||
@@ -41,24 +38,7 @@ func newNDADirectiveFunc(
|
|||||||
|
|
||||||
membership := compliancepage.ComplianceMembershipFromContext(ctx)
|
membership := compliancepage.ComplianceMembershipFromContext(ctx)
|
||||||
if membership == nil {
|
if membership == nil {
|
||||||
// Connected user should always have a membership
|
return nil, gqlutils.Unauthenticatedf(ctx, "authentication needed")
|
||||||
logger.ErrorCtx(ctx, "cannot get membership from context")
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
compliancePage := compliancepage.CompliancePageFromContext(ctx)
|
|
||||||
if compliancePage == nil {
|
|
||||||
logger.ErrorCtx(ctx, "cannot get compliance page from context")
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := trustSvc.GetNDAFile(ctx, compliancePage.ID); err != nil {
|
|
||||||
if errors.Is(err, trust.ErrNDAFileNotFound) {
|
|
||||||
return next(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.ErrorCtx(ctx, "cannot get NDA file", log.Error(err))
|
|
||||||
return nil, gqlutils.Internal(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if membership.ElectronicSignatureID == nil {
|
if membership.ElectronicSignatureID == nil {
|
||||||
|
|||||||
@@ -824,9 +824,9 @@ type Query {
|
|||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
|
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
|
||||||
@session(required: NONE)
|
@session(required: OPTIONAL)
|
||||||
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
|
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
|
||||||
@session(required: NONE)
|
@session(required: OPTIONAL)
|
||||||
|
|
||||||
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda
|
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda
|
||||||
|
|
||||||
|
|||||||
@@ -2246,9 +2246,9 @@ type Query {
|
|||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
|
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
|
||||||
@session(required: NONE)
|
@session(required: OPTIONAL)
|
||||||
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
|
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
|
||||||
@session(required: NONE)
|
@session(required: OPTIONAL)
|
||||||
|
|
||||||
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda
|
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda
|
||||||
|
|
||||||
@@ -4278,7 +4278,7 @@ func (ec *executionContext) _Mutation_sendMagicLink(ctx context.Context, field g
|
|||||||
directive0 := next
|
directive0 := next
|
||||||
|
|
||||||
directive1 := func(ctx context.Context) (any, error) {
|
directive1 := func(ctx context.Context) (any, error) {
|
||||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "OPTIONAL")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var zeroVal *types.SendMagicLinkPayload
|
var zeroVal *types.SendMagicLinkPayload
|
||||||
return zeroVal, err
|
return zeroVal, err
|
||||||
@@ -4341,7 +4341,7 @@ func (ec *executionContext) _Mutation_verifyMagicLink(ctx context.Context, field
|
|||||||
directive0 := next
|
directive0 := next
|
||||||
|
|
||||||
directive1 := func(ctx context.Context) (any, error) {
|
directive1 := func(ctx context.Context) (any, error) {
|
||||||
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
|
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "OPTIONAL")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var zeroVal *types.VerifyMagicLinkPayload
|
var zeroVal *types.VerifyMagicLinkPayload
|
||||||
return zeroVal, err
|
return zeroVal, err
|
||||||
|
|||||||
@@ -210,17 +210,54 @@ func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMa
|
|||||||
|
|
||||||
// VerifyMagicLink is the resolver for the verifyMagicLink field.
|
// VerifyMagicLink is the resolver for the verifyMagicLink field.
|
||||||
func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) {
|
func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) {
|
||||||
identity, session, continueURL, err := r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
session := authn.SessionFromContext(ctx)
|
||||||
|
identity := authn.IdentityFromContext(ctx)
|
||||||
|
|
||||||
|
email, err := r.iam.AuthService.GetMagincLinkEmail(ctx, input.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var errInvalidToken *iam.ErrInvalidToken
|
var errInvalidToken *iam.ErrInvalidToken
|
||||||
if errors.As(err, &errInvalidToken) {
|
if errors.As(err, &errInvalidToken) {
|
||||||
return nil, gqlutils.Invalid(ctx, err)
|
return nil, gqlutils.Invalid(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
|
r.logger.ErrorCtx(ctx, "cannot get magic link email", log.Error(err))
|
||||||
return nil, gqlutils.Internal(ctx)
|
return nil, gqlutils.Internal(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var continueURL *string
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case session == nil:
|
||||||
|
var err error
|
||||||
|
identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
||||||
|
if err != nil {
|
||||||
|
var errInvalidToken *iam.ErrInvalidToken
|
||||||
|
if errors.As(err, &errInvalidToken) {
|
||||||
|
return nil, gqlutils.Invalid(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
|
||||||
|
return nil, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
case identity.EmailAddress != email:
|
||||||
|
if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil {
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err))
|
||||||
|
return nil, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
|
||||||
|
if err != nil {
|
||||||
|
var errInvalidToken *iam.ErrInvalidToken
|
||||||
|
if errors.As(err, &errInvalidToken) {
|
||||||
|
return nil, gqlutils.Invalid(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
|
||||||
|
return nil, gqlutils.Internal(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
trustCenter := compliancepage.CompliancePageFromContext(ctx)
|
||||||
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
|
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user