Implement organization assumption check in authorization layer
Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
@@ -20,11 +20,14 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"go.gearno.de/kit/httpserver"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/securetoken"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -56,7 +59,15 @@ func NewAPIKeyMiddleware(svc *iam.Service, tokenSecret string) func(next http.Ha
|
||||
|
||||
session := SessionFromContext(ctx)
|
||||
if keyID != gid.Nil && session != nil {
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errors.New("api key authentication cannot be used with session authentication"))
|
||||
httpserver.RenderJSON(
|
||||
w,
|
||||
http.StatusUnauthorized,
|
||||
&graphql.Response{
|
||||
Errors: gqlerror.List{
|
||||
gqlutils.Conflictf(ctx, "API key authentication cannot be used with session authentication"),
|
||||
},
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
83
pkg/server/api/connect/v1/authorization.go
Normal file
83
pkg/server/api/connect/v1/authorization.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package connect_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
type (
|
||||
AuthorizeFuncOption func(*iam.AuthorizeParams)
|
||||
AuthorizeFunc func(context.Context, gid.GID, string, ...AuthorizeFuncOption) error
|
||||
)
|
||||
|
||||
func WithAttr(key, value string) AuthorizeFuncOption {
|
||||
return func(params *iam.AuthorizeParams) {
|
||||
params.ResourceAttributes[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
func WithSession(sessionID *gid.GID) AuthorizeFuncOption {
|
||||
return func(params *iam.AuthorizeParams) {
|
||||
params.Session = sessionID
|
||||
}
|
||||
}
|
||||
|
||||
func NewAuthorizeFunc(
|
||||
svc *iam.Service,
|
||||
logger *log.Logger,
|
||||
) AuthorizeFunc {
|
||||
return func(
|
||||
ctx context.Context,
|
||||
objectID gid.GID,
|
||||
action string,
|
||||
options ...AuthorizeFuncOption,
|
||||
) error {
|
||||
identity := IdentityFromContext(ctx)
|
||||
session := SessionFromContext(ctx)
|
||||
|
||||
params := iam.AuthorizeParams{
|
||||
Principal: identity.ID,
|
||||
Resource: objectID,
|
||||
Action: action,
|
||||
ResourceAttributes: make(map[string]string),
|
||||
}
|
||||
if session != nil {
|
||||
params.Session = &session.ID
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(¶ms)
|
||||
}
|
||||
|
||||
if err := svc.Authorizer.Authorize(ctx, params); err != nil {
|
||||
var errInsufficientPermissions *iam.ErrInsufficientPermissions
|
||||
if errors.As(err, &errInsufficientPermissions) {
|
||||
return gqlutils.Forbidden(ctx, err)
|
||||
}
|
||||
|
||||
logger.ErrorCtx(ctx, "cannot authorize", log.Error(err))
|
||||
return gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
@@ -29,29 +28,6 @@ import (
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrForbidden = &gqlerror.Error{
|
||||
Message: "You are not authorized to access this resource",
|
||||
Extensions: map[string]any{
|
||||
"code": "FORBIDDEN",
|
||||
},
|
||||
}
|
||||
|
||||
ErrUnauthenticated = &gqlerror.Error{
|
||||
Message: "You must be authenticated to access this resouce",
|
||||
Extensions: map[string]any{
|
||||
"code": "UNAUTHENTICATED",
|
||||
},
|
||||
}
|
||||
|
||||
ErrAlreadyAuthenticated = &gqlerror.Error{
|
||||
Message: "authentication not allowed for this resource/action",
|
||||
Extensions: map[string]any{
|
||||
"code": "ALREADY_AUTHENTICATED",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func SessionDirective(ctx context.Context, obj any, next graphql.Resolver, required types.SessionRequirement) (any, error) {
|
||||
session := SessionFromContext(ctx)
|
||||
apiKey := APIKeyFromContext(ctx)
|
||||
@@ -60,50 +36,34 @@ func SessionDirective(ctx context.Context, obj any, next graphql.Resolver, requi
|
||||
case types.SessionRequirementOptional:
|
||||
case types.SessionRequirementPresent:
|
||||
if session == nil && apiKey == nil {
|
||||
return nil, ErrUnauthenticated
|
||||
return nil, gqlutils.Unauthenticatedf(
|
||||
ctx,
|
||||
"authentication is required to access this resouce",
|
||||
)
|
||||
}
|
||||
case types.SessionRequirementNone:
|
||||
if session != nil && apiKey != nil {
|
||||
return nil, ErrAlreadyAuthenticated
|
||||
return nil, gqlutils.Invalidf(
|
||||
ctx,
|
||||
"authentication not allowed for this resource/action",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
func IsViewerDirective(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
|
||||
identity := IdentityFromContext(ctx)
|
||||
|
||||
switch node := obj.(type) {
|
||||
case *types.Identity:
|
||||
if identity.ID != node.ID {
|
||||
return nil, ErrForbidden
|
||||
}
|
||||
case *types.Membership:
|
||||
if identity.ID != node.Identity.ID {
|
||||
return nil, ErrForbidden
|
||||
}
|
||||
case *types.Session:
|
||||
if identity.ID != node.Identity.ID {
|
||||
return nil, ErrForbidden
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
func NewGraphQLHandler(svc *iam.Service, logger *log.Logger, baseURL *baseurl.BaseURL, cookieConfig securecookie.Config) http.Handler {
|
||||
config := schema.Config{
|
||||
Resolvers: &Resolver{
|
||||
authorize: NewAuthorizeFunc(svc, logger),
|
||||
logger: logger,
|
||||
iam: svc,
|
||||
baseURL: baseURL,
|
||||
cookieConfig: cookieConfig,
|
||||
},
|
||||
Directives: schema.DirectiveRoot{
|
||||
Session: SessionDirective,
|
||||
IsViewer: IsViewerDirective,
|
||||
Session: SessionDirective,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
52
pkg/server/api/connect/v1/identity_presence_middleware.go
Normal file
52
pkg/server/api/connect/v1/identity_presence_middleware.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package connect_v1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"go.gearno.de/kit/httpserver"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
func NewIdentityPresenceMiddleware() func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
identity := IdentityFromContext(r.Context())
|
||||
|
||||
if identity == nil {
|
||||
httpserver.RenderJSON(
|
||||
w,
|
||||
http.StatusUnauthorized,
|
||||
&graphql.Response{
|
||||
Errors: gqlerror.List{
|
||||
gqlutils.Unauthenticatedf(
|
||||
r.Context(),
|
||||
"authentication is required to access this resouce",
|
||||
),
|
||||
},
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -18,23 +18,20 @@ package connect_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
"go.probo.inc/probo/pkg/server/api/connect/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
type (
|
||||
Resolver struct {
|
||||
authorize AuthorizeFunc
|
||||
logger *log.Logger
|
||||
iam *iam.Service
|
||||
baseURL *baseurl.BaseURL
|
||||
@@ -81,54 +78,5 @@ func NewMux(logger *log.Logger, svc *iam.Service, cookieConfig securecookie.Conf
|
||||
}
|
||||
|
||||
func (r *Resolver) Permission(ctx context.Context, obj types.Node, action string) (bool, error) {
|
||||
identity := IdentityFromContext(ctx)
|
||||
|
||||
err := r.iam.Authorizer.Authorize(
|
||||
ctx,
|
||||
iam.AuthorizeParams{
|
||||
Principal: identity.ID,
|
||||
Resource: obj.GetID(),
|
||||
Action: action,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
var errInsufficientPermissions *iam.ErrInsufficientPermissions
|
||||
if errors.As(err, &errInsufficientPermissions) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot authorize", log.Error(err))
|
||||
return false, gqlutils.InternalServerError(ctx)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) Authorize(ctx context.Context, objectID gid.GID, action string, attrs map[string]string) bool {
|
||||
identity := IdentityFromContext(ctx)
|
||||
|
||||
err := r.iam.Authorizer.Authorize(
|
||||
ctx,
|
||||
iam.AuthorizeParams{
|
||||
Principal: identity.ID,
|
||||
Resource: objectID,
|
||||
Action: action,
|
||||
ResourceAttributes: attrs,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
var errInsufficientPermissions *iam.ErrInsufficientPermissions
|
||||
if errors.As(err, &errInsufficientPermissions) {
|
||||
graphql.AddError(ctx, err)
|
||||
return false
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot authorize", log.Error(err))
|
||||
graphql.AddError(ctx, gqlutils.InternalServerError(ctx))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return r.authorize(ctx, obj.GetID(), action) == nil, nil
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func (h *SAMLHandler) MetadataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set("Content-Type", "application/samlmetadata+xml")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(metadataXML)
|
||||
_, _ = w.Write(metadataXML)
|
||||
}
|
||||
|
||||
func (h *SAMLHandler) ConsumeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -97,7 +97,11 @@ func (h *SAMLHandler) ConsumeHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
securecookie.Set(w, h.cookieConfig, rootSession.ID.String())
|
||||
if err := securecookie.Set(w, h.cookieConfig, rootSession.ID.String()); err != nil {
|
||||
h.logger.ErrorCtx(ctx, "cannot set cookie", log.Error(err))
|
||||
h.renderInternalServerError(w, r)
|
||||
return
|
||||
}
|
||||
redirectURL := h.baseURL.WithPath("/organizations/" + membership.OrganizationID.String()).MustString()
|
||||
http.Redirect(w, r, redirectURL, http.StatusFound)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ directive @goEnum(value: String) on ENUM_VALUE
|
||||
|
||||
directive @session(required: SessionRequirement!) on FIELD_DEFINITION
|
||||
|
||||
directive @isViewer on FIELD_DEFINITION
|
||||
|
||||
scalar CursorKey
|
||||
scalar Datetime
|
||||
scalar Upload
|
||||
@@ -150,7 +148,7 @@ type Identity implements Node {
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: MembershipOrder
|
||||
): MembershipConnection @goField(forceResolver: true) @isViewer
|
||||
): MembershipConnection @goField(forceResolver: true)
|
||||
|
||||
pendingInvitations(
|
||||
first: Int
|
||||
@@ -158,7 +156,7 @@ type Identity implements Node {
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: InvitationOrder
|
||||
): InvitationConnection @goField(forceResolver: true) @isViewer
|
||||
): InvitationConnection @goField(forceResolver: true)
|
||||
|
||||
sessions(
|
||||
first: Int
|
||||
@@ -166,14 +164,14 @@ type Identity implements Node {
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: SessionOrder
|
||||
): SessionConnection @goField(forceResolver: true) @isViewer
|
||||
): SessionConnection @goField(forceResolver: true)
|
||||
|
||||
personalAPIKeys(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): PersonalAPIKeyConnection @goField(forceResolver: true) @isViewer
|
||||
): PersonalAPIKeyConnection @goField(forceResolver: true)
|
||||
|
||||
permission(action: String!): Boolean!
|
||||
@goField(forceResolver: true)
|
||||
@@ -272,7 +270,7 @@ type Membership implements Node {
|
||||
source: MembershipSource!
|
||||
state: MembershipState!
|
||||
|
||||
lastSession: Session @goField(forceResolver: true) @isViewer
|
||||
lastSession: Session @goField(forceResolver: true)
|
||||
|
||||
permission(action: String!): Boolean!
|
||||
@goField(forceResolver: true)
|
||||
@@ -297,7 +295,7 @@ type Invitation implements Node {
|
||||
|
||||
type Session implements Node {
|
||||
id: ID!
|
||||
identity: Identity @goField(forceResolver: true) @isViewer
|
||||
identity: Identity @goField(forceResolver: true)
|
||||
ipAddress: String!
|
||||
userAgent: String!
|
||||
updatedAt: Datetime!
|
||||
|
||||
@@ -67,8 +67,7 @@ type ResolverRoot interface {
|
||||
}
|
||||
|
||||
type DirectiveRoot struct {
|
||||
IsViewer func(ctx context.Context, obj any, next graphql.Resolver) (res any, err error)
|
||||
Session func(ctx context.Context, obj any, next graphql.Resolver, required types.SessionRequirement) (res any, err error)
|
||||
Session func(ctx context.Context, obj any, next graphql.Resolver, required types.SessionRequirement) (res any, err error)
|
||||
}
|
||||
|
||||
type ComplexityRoot struct {
|
||||
@@ -2281,8 +2280,6 @@ directive @goEnum(value: String) on ENUM_VALUE
|
||||
|
||||
directive @session(required: SessionRequirement!) on FIELD_DEFINITION
|
||||
|
||||
directive @isViewer on FIELD_DEFINITION
|
||||
|
||||
scalar CursorKey
|
||||
scalar Datetime
|
||||
scalar Upload
|
||||
@@ -2418,7 +2415,7 @@ type Identity implements Node {
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: MembershipOrder
|
||||
): MembershipConnection @goField(forceResolver: true) @isViewer
|
||||
): MembershipConnection @goField(forceResolver: true)
|
||||
|
||||
pendingInvitations(
|
||||
first: Int
|
||||
@@ -2426,7 +2423,7 @@ type Identity implements Node {
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: InvitationOrder
|
||||
): InvitationConnection @goField(forceResolver: true) @isViewer
|
||||
): InvitationConnection @goField(forceResolver: true)
|
||||
|
||||
sessions(
|
||||
first: Int
|
||||
@@ -2434,14 +2431,14 @@ type Identity implements Node {
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: SessionOrder
|
||||
): SessionConnection @goField(forceResolver: true) @isViewer
|
||||
): SessionConnection @goField(forceResolver: true)
|
||||
|
||||
personalAPIKeys(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
): PersonalAPIKeyConnection @goField(forceResolver: true) @isViewer
|
||||
): PersonalAPIKeyConnection @goField(forceResolver: true)
|
||||
|
||||
permission(action: String!): Boolean!
|
||||
@goField(forceResolver: true)
|
||||
@@ -2540,7 +2537,7 @@ type Membership implements Node {
|
||||
source: MembershipSource!
|
||||
state: MembershipState!
|
||||
|
||||
lastSession: Session @goField(forceResolver: true) @isViewer
|
||||
lastSession: Session @goField(forceResolver: true)
|
||||
|
||||
permission(action: String!): Boolean!
|
||||
@goField(forceResolver: true)
|
||||
@@ -2565,7 +2562,7 @@ type Invitation implements Node {
|
||||
|
||||
type Session implements Node {
|
||||
id: ID!
|
||||
identity: Identity @goField(forceResolver: true) @isViewer
|
||||
identity: Identity @goField(forceResolver: true)
|
||||
ipAddress: String!
|
||||
userAgent: String!
|
||||
updatedAt: Datetime!
|
||||
@@ -4683,20 +4680,7 @@ func (ec *executionContext) _Identity_memberships(ctx context.Context, field gra
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Identity().Memberships(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.MembershipOrderBy))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
if ec.directives.IsViewer == nil {
|
||||
var zeroVal *types.MembershipConnection
|
||||
return zeroVal, errors.New("directive isViewer is not implemented")
|
||||
}
|
||||
return ec.directives.IsViewer(ctx, obj, directive0)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalOMembershipConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐMembershipConnection,
|
||||
true,
|
||||
false,
|
||||
@@ -4745,20 +4729,7 @@ func (ec *executionContext) _Identity_pendingInvitations(ctx context.Context, fi
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Identity().PendingInvitations(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.InvitationOrderBy))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
if ec.directives.IsViewer == nil {
|
||||
var zeroVal *types.InvitationConnection
|
||||
return zeroVal, errors.New("directive isViewer is not implemented")
|
||||
}
|
||||
return ec.directives.IsViewer(ctx, obj, directive0)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalOInvitationConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐInvitationConnection,
|
||||
true,
|
||||
false,
|
||||
@@ -4807,20 +4778,7 @@ func (ec *executionContext) _Identity_sessions(ctx context.Context, field graphq
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Identity().Sessions(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.SessionOrder))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
if ec.directives.IsViewer == nil {
|
||||
var zeroVal *types.SessionConnection
|
||||
return zeroVal, errors.New("directive isViewer is not implemented")
|
||||
}
|
||||
return ec.directives.IsViewer(ctx, obj, directive0)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalOSessionConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSessionConnection,
|
||||
true,
|
||||
false,
|
||||
@@ -4869,20 +4827,7 @@ func (ec *executionContext) _Identity_personalAPIKeys(ctx context.Context, field
|
||||
fc := graphql.GetFieldContext(ctx)
|
||||
return ec.resolvers.Identity().PersonalAPIKeys(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey))
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
if ec.directives.IsViewer == nil {
|
||||
var zeroVal *types.PersonalAPIKeyConnection
|
||||
return zeroVal, errors.New("directive isViewer is not implemented")
|
||||
}
|
||||
return ec.directives.IsViewer(ctx, obj, directive0)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalOPersonalAPIKeyConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPersonalAPIKeyConnection,
|
||||
true,
|
||||
false,
|
||||
@@ -5863,20 +5808,7 @@ func (ec *executionContext) _Membership_lastSession(ctx context.Context, field g
|
||||
func(ctx context.Context) (any, error) {
|
||||
return ec.resolvers.Membership().LastSession(ctx, obj)
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
if ec.directives.IsViewer == nil {
|
||||
var zeroVal *types.Session
|
||||
return zeroVal, errors.New("directive isViewer is not implemented")
|
||||
}
|
||||
return ec.directives.IsViewer(ctx, obj, directive0)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalOSession2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐSession,
|
||||
true,
|
||||
false,
|
||||
@@ -11579,20 +11511,7 @@ func (ec *executionContext) _Session_identity(ctx context.Context, field graphql
|
||||
func(ctx context.Context) (any, error) {
|
||||
return ec.resolvers.Session().Identity(ctx, obj)
|
||||
},
|
||||
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
|
||||
directive0 := next
|
||||
|
||||
directive1 := func(ctx context.Context) (any, error) {
|
||||
if ec.directives.IsViewer == nil {
|
||||
var zeroVal *types.Identity
|
||||
return zeroVal, errors.New("directive isViewer is not implemented")
|
||||
}
|
||||
return ec.directives.IsViewer(ctx, obj, directive0)
|
||||
}
|
||||
|
||||
next = directive1
|
||||
return next
|
||||
},
|
||||
nil,
|
||||
ec.marshalOIdentity2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐIdentity,
|
||||
true,
|
||||
false,
|
||||
|
||||
@@ -21,11 +21,14 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"go.gearno.de/kit/httpserver"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -64,7 +67,15 @@ func NewSessionMiddleware(svc *iam.Service, cookieConfig securecookie.Config) fu
|
||||
|
||||
apiKey := APIKeyFromContext(ctx)
|
||||
if sessionID != gid.Nil && apiKey != nil {
|
||||
httpserver.RenderError(w, http.StatusBadRequest, errors.New("session authentication cannot be used with API key authentication"))
|
||||
httpserver.RenderJSON(
|
||||
w,
|
||||
http.StatusUnauthorized,
|
||||
&graphql.Response{
|
||||
Errors: gqlerror.List{
|
||||
gqlutils.Conflictf(ctx, "session authentication cannot be used with API key authentication"),
|
||||
},
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user