Files
probo/pkg/server/api/connect/v1/v1_resolver.go
2026-02-12 16:21:12 +04:00

2082 lines
70 KiB
Go

package connect_v1
// This file will be automatically regenerated based on the schema, any resolver
// implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.86
import (
"context"
"errors"
"fmt"
"time"
"github.com/99designs/gqlgen/graphql"
"github.com/vektah/gqlparser/v2/gqlerror"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/authz"
"go.probo.inc/probo/pkg/server/api/connect/v1/schema"
"go.probo.inc/probo/pkg/server/api/connect/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
)
// Permission is the resolver for the permission field.
func (r *connectorResolver) Permission(ctx context.Context, obj *types.Connector, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Memberships is the resolver for the memberships field.
func (r *identityResolver) Memberships(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) (*types.MembershipConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipList); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.MembershipConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
pageOrderBy := page.OrderBy[coredata.MembershipOrderField]{
Field: coredata.MembershipOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.MembershipOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.AccountService.ListMemberships(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list memberships", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMembershipConnection(page, r, obj.ID), nil
}
// PendingInvitations is the resolver for the pendingInvitations field.
func (r *identityResolver) PendingInvitations(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.InvitationOrderBy) (*types.InvitationConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionInvitationList); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.InvitationConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
pageOrderBy := page.OrderBy[coredata.InvitationOrderField]{
Field: coredata.InvitationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.AccountService.ListPendingInvitations(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list pending invitations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewInvitationConnection(page, r, obj.ID, nil), nil
}
// Sessions is the resolver for the sessions field.
func (r *identityResolver) Sessions(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SessionOrder) (*types.SessionConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionSessionList); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.SessionConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
pageOrderBy := page.OrderBy[coredata.SessionOrderField]{
Field: coredata.SessionOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.SessionOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.AccountService.ListSessions(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list sessions", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSessionConnection(page, r, obj.ID), nil
}
// PersonalAPIKeys is the resolver for the personalAPIKeys field.
func (r *identityResolver) PersonalAPIKeys(ctx context.Context, obj *types.Identity, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.PersonalAPIKeyConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionPersonalAPIKeyList); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.PersonalAPIKeyConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
pageOrderBy := page.OrderBy[coredata.PersonalAPIKeyOrderField]{
Field: coredata.PersonalAPIKeyOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.AccountService.ListPersonalAPIKeys(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list personal api keys", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewPersonalAPIKeyConnection(page, r, obj.ID), nil
}
// Permission is the resolver for the permission field.
func (r *identityResolver) Permission(ctx context.Context, obj *types.Identity, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Organization is the resolver for the organization field.
func (r *invitationResolver) Organization(ctx context.Context, obj *types.Invitation) (*types.Organization, error) {
if err := r.authorize(ctx, obj.Organization.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Organization{
ID: obj.Organization.ID,
}, nil
}
organization, err := r.iam.OrganizationService.GetOrganizationForInvitation(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get organization for invitation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Permission is the resolver for the permission field.
func (r *invitationResolver) Permission(ctx context.Context, obj *types.Invitation, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *invitationConnectionResolver) TotalCount(ctx context.Context, obj *types.InvitationConnection) (*int, error) {
switch obj.Resolver.(type) {
case *organizationResolver:
if err := r.authorize(ctx, obj.ParentID, iam.ActionInvitationList); err != nil {
return nil, err
}
count, err := r.iam.OrganizationService.CountInvitations(ctx, obj.ParentID, obj.Filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count invitations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
case *identityResolver:
if err := r.authorize(ctx, obj.ParentID, iam.ActionInvitationList); err != nil {
return nil, err
}
count, err := r.iam.AccountService.CountPendingInvitations(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count invitations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Identity is the resolver for the identity field.
func (r *membershipResolver) Identity(ctx context.Context, obj *types.Membership) (*types.Identity, error) {
if err := r.authorize(
ctx,
obj.Identity.ID,
iam.ActionIdentityGet,
authz.WithAttr("organization_id", obj.Organization.ID.String()),
); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Identity{
ID: obj.Identity.ID,
}, nil
}
identity, err := r.iam.AccountService.GetIdentityForMembership(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get identity for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewIdentity(identity), nil
}
// Profile is the resolver for the profile field.
func (r *membershipResolver) Profile(ctx context.Context, obj *types.Membership) (*types.MembershipProfile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
profile, err := r.iam.AccountService.GetProfileForMembership(ctx, obj.ID)
if err != nil {
var errProfileNotFound *iam.ErrProfileNotFound
if errors.As(err, &errProfileNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot get profile for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMembershipProfile(profile), nil
}
// Organization is the resolver for the organization field.
func (r *membershipResolver) Organization(ctx context.Context, obj *types.Membership) (*types.Organization, error) {
if err := r.authorize(ctx, obj.Organization.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Organization{
ID: obj.Organization.ID,
}, nil
}
organization, err := r.iam.OrganizationService.GetOrganizationForMembership(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get organization for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// LastSession is the resolver for the lastSession field.
func (r *membershipResolver) LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
session := authn.SessionFromContext(ctx)
if session == nil {
return nil, nil
}
childSession, err := r.iam.SessionService.GetActiveSessionForMembership(ctx, session.ID, obj.ID)
if err != nil {
var errSessionNotFound *iam.ErrSessionNotFound
if errors.As(err, &errSessionNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get active session for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSession(childSession), nil
}
// Permission is the resolver for the permission field.
func (r *membershipResolver) Permission(ctx context.Context, obj *types.Membership, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *membershipConnectionResolver) TotalCount(ctx context.Context, obj *types.MembershipConnection) (*int, error) {
if err := r.authorize(ctx, obj.ParentID, iam.ActionMembershipList); err != nil {
return nil, err
}
switch obj.Resolver.(type) {
case *identityResolver:
count, err := r.iam.AccountService.CountMemberships(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count memberships", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
case *organizationResolver:
count, err := r.iam.OrganizationService.CountMemberships(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count memberships", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Identity is the resolver for the identity field.
func (r *membershipProfileResolver) Identity(ctx context.Context, obj *types.MembershipProfile) (*types.Identity, error) {
if err := r.authorize(
ctx,
obj.Identity.ID,
iam.ActionIdentityGet,
authz.WithAttr("organization_id", obj.Organization.ID.String()),
); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Identity{
ID: obj.Identity.ID,
}, nil
}
identity, err := r.iam.AccountService.GetIdentity(ctx, obj.Identity.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get identity for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewIdentity(identity), nil
}
// Organization is the resolver for the organization field.
func (r *membershipProfileResolver) Organization(ctx context.Context, obj *types.MembershipProfile) (*types.Organization, error) {
if err := r.authorize(ctx, obj.Organization.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Organization{
ID: obj.Organization.ID,
}, nil
}
organization, err := r.iam.OrganizationService.GetOrganizationForMembership(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get organization for membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Permission is the resolver for the permission field.
func (r *membershipProfileResolver) Permission(ctx context.Context, obj *types.MembershipProfile, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// SignIn is the resolver for the signIn field.
func (r *mutationResolver) SignIn(ctx context.Context, input types.SignInInput) (*types.SignInPayload, error) {
identity, err := r.iam.AuthService.CheckCredentials(ctx, input.Email, input.Password)
if err != nil {
var errInvalidPassword *iam.ErrInvalidPassword
if errors.As(err, &errInvalidPassword) {
return nil, gqlutils.Invalid(ctx, err)
}
var errInvalidCredentials *iam.ErrInvalidCredentials
if errors.As(err, &errInvalidCredentials) {
return nil, &gqlerror.Error{
Message: err.Error(),
Extensions: map[string]any{
"code": "INVALID_CREDENTIALS",
},
}
}
r.logger.ErrorCtx(ctx, "cannot check credentials", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
session := authn.SessionFromContext(ctx)
switch {
case session == nil:
var err error
session, err = r.iam.AuthService.OpenSessionWithPassword(
ctx,
identity.ID,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
case session.IdentityID != identity.ID:
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)
}
session, err = r.iam.AuthService.OpenSessionWithPassword(
ctx,
identity.ID,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
w := gqlutils.HTTPResponseWriterFromContext(ctx)
r.sessionCookie.Set(w, session)
if input.OrganizationID != nil {
var err error
_, _, err = r.iam.SessionService.OpenPasswordChildSessionForOrganization(ctx, session.ID, *input.OrganizationID)
if err != nil {
// Here session middleware already took care of expired/nil root session so we only handle membership related errors
var errMembershipNotFound *iam.ErrMembershipNotFound
var errMembershipInactive *iam.ErrMembershipInactive
if errors.As(err, &errMembershipNotFound) || errors.As(err, &errMembershipInactive) {
return nil, gqlutils.Forbiddenf(ctx, "forbidden")
}
r.logger.ErrorCtx(ctx, "cannot assume organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.SignInPayload{
Identity: types.NewIdentity(identity),
Session: types.NewSession(session),
}, nil
}
// SignUp is the resolver for the signUp field.
func (r *mutationResolver) SignUp(ctx context.Context, input types.SignUpInput) (*types.SignUpPayload, error) {
identity, session, err := r.iam.AuthService.CreateIdentityWithPassword(
ctx,
&iam.CreateIdentityWithPasswordRequest{
Email: input.Email,
Password: input.Password,
FullName: input.FullName,
},
)
if err != nil {
var errIdentityAlreadyExists *iam.ErrIdentityAlreadyExists
if errors.As(err, &errIdentityAlreadyExists) {
return nil, gqlutils.Invalid(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create identity with password", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
w := gqlutils.HTTPResponseWriterFromContext(ctx)
r.sessionCookie.Set(w, session)
return &types.SignUpPayload{
Identity: types.NewIdentity(identity),
}, nil
}
// SignOut is the resolver for the signOut field.
func (r *mutationResolver) SignOut(ctx context.Context) (*types.SignOutPayload, error) {
session := authn.SessionFromContext(ctx)
err := r.iam.SessionService.CloseSession(ctx, session.ID)
if err != nil {
var ErrSessionNotFound *iam.ErrSessionNotFound
if errors.As(err, &ErrSessionNotFound) {
return &types.SignOutPayload{}, nil
}
r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
w := gqlutils.HTTPResponseWriterFromContext(ctx)
r.sessionCookie.Clear(w)
return &types.SignOutPayload{Success: true}, nil
}
// SignUpFromInvitation is the resolver for the signUpFromInvitation field.
func (r *mutationResolver) SignUpFromInvitation(ctx context.Context, input types.SignUpFromInvitationInput) (*types.SignUpFromInvitationPayload, error) {
identity, session, err := r.iam.AuthService.CreateIdentityFromInvitation(
ctx,
&iam.CreateIdentityFromInvitationRequest{
InvitationToken: input.Token,
Password: input.Password,
FullName: input.FullName,
},
)
if err != nil {
var (
errInvalidToken *iam.ErrInvalidToken
errInvitationNotFound *iam.ErrInvitationNotFound
errInvitationAlreadyAccepted *iam.ErrInvitationAlreadyAccepted
errInvitationExpired *iam.ErrInvitationExpired
errIdentityAlreadyExists *iam.ErrIdentityAlreadyExists
isInvalidErr = errors.As(err, &errInvalidToken) ||
errors.As(err, &errInvitationNotFound) ||
errors.As(err, &errInvitationAlreadyAccepted) ||
errors.As(err, &errInvitationExpired)
)
if isInvalidErr {
return nil, gqlutils.Invalid(ctx, err)
}
if errors.As(err, &errIdentityAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create identity from invitation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
w := gqlutils.HTTPResponseWriterFromContext(ctx)
r.sessionCookie.Set(w, session)
return &types.SignUpFromInvitationPayload{
Identity: &types.Identity{
ID: identity.ID,
Email: identity.EmailAddress,
EmailVerified: identity.EmailAddressVerified,
CreatedAt: identity.CreatedAt,
UpdatedAt: identity.UpdatedAt,
},
}, nil
}
// ForgotPassword is the resolver for the forgotPassword field.
func (r *mutationResolver) ForgotPassword(ctx context.Context, input types.ForgotPasswordInput) (*types.ForgotPasswordPayload, error) {
err := r.iam.AuthService.SendPasswordResetInstructionByEmail(
ctx,
input.Email,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot send password reset instruction by email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ForgotPasswordPayload{
Success: true,
}, nil
}
// ResetPassword is the resolver for the resetPassword field.
func (r *mutationResolver) ResetPassword(ctx context.Context, input types.ResetPasswordInput) (*types.ResetPasswordPayload, error) {
err := r.iam.AuthService.ResetPassword(
ctx,
&iam.ResetPasswordRequest{
Token: input.Token,
Password: input.Password,
},
)
if err != nil {
var errInvalidToken *iam.ErrInvalidToken
if errors.As(err, &errInvalidToken) {
return nil, gqlutils.Invalid(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot reset password", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ResetPasswordPayload{
Success: true,
}, nil
}
// VerifyEmail is the resolver for the verifyEmail field.
func (r *mutationResolver) VerifyEmail(ctx context.Context, input types.VerifyEmailInput) (*types.VerifyEmailPayload, error) {
err := r.iam.AccountService.VerifyEmail(ctx, input.Token)
if err != nil {
var (
errInvalidToken *iam.ErrInvalidToken
errIdentityNotFound *iam.ErrIdentityNotFound
errEmailAlreadyVerified *iam.ErrEmailAlreadyVerified
errEmailVerificationMismatch *iam.ErrEmailVerificationMismatch
isInvalidErr = errors.As(err, &errInvalidToken) ||
errors.As(err, &errEmailVerificationMismatch)
)
if isInvalidErr {
return nil, gqlutils.Invalid(ctx, err)
}
if errors.As(err, &errEmailAlreadyVerified) {
return nil, gqlutils.Conflict(ctx, err)
}
if errors.As(err, &errIdentityNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot verify email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.VerifyEmailPayload{
Success: true,
}, nil
}
// ChangePassword is the resolver for the changePassword field.
func (r *mutationResolver) ChangePassword(ctx context.Context, input types.ChangePasswordInput) (*types.ChangePasswordPayload, error) {
identity := authn.IdentityFromContext(ctx)
err := r.iam.AccountService.ChangePassword(
ctx,
identity.ID,
&iam.ChangePasswordRequest{
CurrentPassword: input.CurrentPassword,
NewPassword: input.NewPassword,
},
)
if err != nil {
var (
errInvalidPassword *iam.ErrInvalidPassword
errIdentityNotFound *iam.ErrIdentityNotFound
)
if errors.As(err, &errInvalidPassword) {
return nil, gqlutils.Invalid(ctx, err)
}
if errors.As(err, &errIdentityNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot change password", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ChangePasswordPayload{
Success: true,
}, nil
}
// ChangeEmail is the resolver for the changeEmail field.
func (r *mutationResolver) ChangeEmail(ctx context.Context, input types.ChangeEmailInput) (*types.ChangeEmailPayload, error) {
identity := authn.IdentityFromContext(ctx)
err := r.iam.AccountService.ChangeEmail(
ctx,
identity.ID,
&iam.ChangeEmailRequest{
NewEmail: input.NewEmail,
Password: input.Password,
},
)
if err != nil {
var (
errInvalidPassword *iam.ErrInvalidPassword
errIdentityNotFound *iam.ErrIdentityNotFound
)
if errors.As(err, &errInvalidPassword) {
return nil, gqlutils.Invalid(ctx, err)
}
if errors.As(err, &errIdentityNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot change email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.ChangeEmailPayload{
Success: true,
}, nil
}
// AssumeOrganizationSession is the resolver for the assumeOrganizationSession field.
func (r *mutationResolver) AssumeOrganizationSession(ctx context.Context, input types.AssumeOrganizationSessionInput) (*types.AssumeOrganizationSessionPayload, error) {
rootSession := authn.SessionFromContext(ctx)
childSession, membership, err := r.iam.SessionService.AssumeOrganizationSession(ctx, rootSession.ID, input.OrganizationID, input.Continue)
if err != nil {
var (
errMembershipNotFound *iam.ErrMembershipNotFound
errPasswordRequired *iam.ErrPasswordRequired
errSAMLAuthenticationRequired *iam.ErrSAMLAuthenticationRequired
)
switch {
case errors.As(err, &errMembershipNotFound):
return nil, gqlutils.NotFound(ctx, err)
case errors.As(err, &errPasswordRequired):
return &types.AssumeOrganizationSessionPayload{
Result: types.PasswordRequired{
Reason: types.ReauthenticationReason(errPasswordRequired.Reason),
},
}, nil
case errors.As(err, &errSAMLAuthenticationRequired):
return &types.AssumeOrganizationSessionPayload{
Result: types.SAMLAuthenticationRequired{
Reason: types.ReauthenticationReason(errSAMLAuthenticationRequired.Reason),
RedirectURL: errSAMLAuthenticationRequired.RedirectURL,
},
}, nil
default:
r.logger.ErrorCtx(ctx, "cannot assume organization session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.AssumeOrganizationSessionPayload{
Result: types.OrganizationSessionCreated{
Session: types.NewSession(childSession),
Membership: types.NewMembership(membership),
},
}, nil
}
// RevokeSession is the resolver for the revokeSession field.
func (r *mutationResolver) RevokeSession(ctx context.Context, input types.RevokeSessionInput) (*types.RevokeSessionPayload, error) {
if err := r.authorize(ctx, input.SessionID, iam.ActionSessionRevoke); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
err := r.iam.SessionService.RevokeSession(ctx, identity.ID, input.SessionID)
if err != nil {
var ErrSessionExpired *iam.ErrSessionExpired
if errors.As(err, &ErrSessionExpired) {
return &types.RevokeSessionPayload{Success: true}, nil
}
r.logger.ErrorCtx(ctx, "cannot revoke session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RevokeSessionPayload{Success: true}, nil
}
// RevokeAllSessions is the resolver for the revokeAllSessions field.
func (r *mutationResolver) RevokeAllSessions(ctx context.Context) (*types.RevokeAllSessionsPayload, error) {
if err := r.authorize(ctx, authn.SessionFromContext(ctx).ID, iam.ActionSessionRevokeAll); err != nil {
return nil, err
}
session := authn.SessionFromContext(ctx)
revokedCount, err := r.iam.SessionService.RevokeAllSessions(ctx, session.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot revoke all sessions", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RevokeAllSessionsPayload{RevokedCount: int(revokedCount)}, nil
}
// CreatePersonalAPIKey is the resolver for the createPersonalAPIKey field.
func (r *mutationResolver) CreatePersonalAPIKey(ctx context.Context, input types.CreatePersonalAPIKeyInput) (*types.CreatePersonalAPIKeyPayload, error) {
identity := authn.IdentityFromContext(ctx)
if err := r.authorize(ctx, identity.ID, iam.ActionPersonalAPIKeyCreate); err != nil {
return nil, err
}
userAPIKey, token, err := r.iam.AccountService.CreatePersonalAPIKey(
ctx,
identity.ID,
input.Name,
input.ExpiresAt,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create personal api key", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreatePersonalAPIKeyPayload{
PersonalAPIKeyEdge: types.NewPersonalAPIKeyEdge(userAPIKey, coredata.PersonalAPIKeyOrderFieldCreatedAt),
Token: token,
}, nil
}
// RevokePersonalAPIKey is the resolver for the revokePersonalAPIKey field.
func (r *mutationResolver) RevokePersonalAPIKey(ctx context.Context, input types.RevokePersonalAPIKeyInput) (*types.RevokePersonalAPIKeyPayload, error) {
if err := r.authorize(ctx, input.PersonalAPIKeyID, iam.ActionPersonalAPIKeyDelete); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
err := r.iam.AccountService.DeletePersonalAPIKey(ctx, identity.ID, input.PersonalAPIKeyID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete personal api key", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RevokePersonalAPIKeyPayload{PersonalAPIKeyID: input.PersonalAPIKeyID}, nil
}
// CreateOrganization is the resolver for the createOrganization field.
func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) {
identity := authn.IdentityFromContext(ctx)
// FIXME check email domain and related IDP config
// if ok := r.authorize(ctx, identity.ID, iam.ActionOrganizationCreate); !ok {
// return nil, nil
// }
var (
logoFile *iam.UploadedFile
horizontalLogoFile *iam.UploadedFile
)
if input.LogoFile != nil {
logoFile = &iam.UploadedFile{
Content: input.LogoFile.File,
Filename: input.LogoFile.Filename,
ContentType: input.LogoFile.ContentType,
Size: input.LogoFile.Size,
}
}
if input.HorizontalLogoFile != nil {
horizontalLogoFile = &iam.UploadedFile{
Content: input.HorizontalLogoFile.File,
Filename: input.HorizontalLogoFile.Filename,
ContentType: input.HorizontalLogoFile.ContentType,
Size: input.HorizontalLogoFile.Size,
}
}
organization, membership, err := r.iam.OrganizationService.CreateOrganization(
ctx,
identity.ID,
&iam.CreateOrganizationRequest{
Name: input.Name,
LogoFile: logoFile,
HorizontalLogoFile: horizontalLogoFile,
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateOrganizationPayload{
Organization: types.NewOrganization(organization),
MembershipEdge: types.NewMembershipEdge(membership, coredata.MembershipOrderFieldCreatedAt),
}, nil
}
// UpdateOrganization is the resolver for the updateOrganization field.
func (r *mutationResolver) UpdateOrganization(ctx context.Context, input types.UpdateOrganizationInput) (*types.UpdateOrganizationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionOrganizationUpdate); err != nil {
return nil, err
}
req := &iam.UpdateOrganizationRequest{
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
WebsiteURL: gqlutils.UnwrapOmittable(input.WebsiteURL),
Email: gqlutils.UnwrapOmittable(input.Email),
HeadquarterAddress: gqlutils.UnwrapOmittable(input.HeadquarterAddress),
}
if input.LogoFile != nil {
req.LogoFile = &iam.UploadedFile{
Filename: input.LogoFile.Filename,
ContentType: input.LogoFile.ContentType,
Size: input.LogoFile.Size,
Content: input.LogoFile.File,
}
}
if input.HorizontalLogoFile != nil {
req.HorizontalLogoFile = &iam.UploadedFile{
Filename: input.HorizontalLogoFile.Filename,
ContentType: input.HorizontalLogoFile.ContentType,
Size: input.HorizontalLogoFile.Size,
Content: input.HorizontalLogoFile.File,
}
}
organization, err := r.iam.OrganizationService.UpdateOrganization(
ctx,
input.OrganizationID,
req,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateOrganizationPayload{
Organization: &types.Organization{
ID: organization.ID,
Name: organization.Name,
Description: organization.Description,
WebsiteURL: organization.WebsiteURL,
Email: organization.Email,
HeadquarterAddress: organization.HeadquarterAddress,
CreatedAt: organization.CreatedAt,
UpdatedAt: organization.UpdatedAt,
},
}, nil
}
// DeleteOrganization is the resolver for the deleteOrganization field.
func (r *mutationResolver) DeleteOrganization(ctx context.Context, input types.DeleteOrganizationInput) (*types.DeleteOrganizationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionOrganizationDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.DeleteOrganization(ctx, input.OrganizationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteOrganizationPayload{DeletedOrganizationID: input.OrganizationID}, nil
}
// DeleteOrganizationHorizontalLogo is the resolver for the deleteOrganizationHorizontalLogo field.
func (r *mutationResolver) DeleteOrganizationHorizontalLogo(ctx context.Context, input types.DeleteOrganizationHorizontalLogoInput) (*types.DeleteOrganizationHorizontalLogoPayload, error) {
panic(fmt.Errorf("not implemented: DeleteOrganizationHorizontalLogo - deleteOrganizationHorizontalLogo"))
}
// InviteMember is the resolver for the inviteMember field.
func (r *mutationResolver) InviteMember(ctx context.Context, input types.InviteMemberInput) (*types.InviteMemberPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionInvitationCreate); err != nil {
return nil, err
}
invitation, err := r.iam.OrganizationService.InviteMember(
ctx,
input.OrganizationID,
&iam.CreateInvitationRequest{
Email: input.Email,
FullName: input.FullName,
Role: input.Role,
},
)
if err != nil {
var errOrganizationNotFound *iam.ErrOrganizationNotFound
var errMembershipAlreadyExists *iam.ErrMembershipAlreadyExists
if errors.As(err, &errOrganizationNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.As(err, &errMembershipAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot add member to organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.InviteMemberPayload{
InvitationEdge: types.NewInvitationEdge(invitation, coredata.InvitationOrderFieldCreatedAt),
}, nil
}
// DeleteInvitation is the resolver for the deleteInvitation field.
func (r *mutationResolver) DeleteInvitation(ctx context.Context, input types.DeleteInvitationInput) (*types.DeleteInvitationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionInvitationDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.DeleteInvitation(ctx, input.OrganizationID, input.InvitationID)
if err != nil {
var errInvitationNotFound *iam.ErrInvitationNotFound
var errInvitationNotDeleted *iam.ErrInvitationNotDeleted
if errors.As(err, &errInvitationNotFound) {
return nil, gqlutils.NotFound(ctx, err)
}
if errors.As(err, &errInvitationNotDeleted) {
return nil, gqlutils.Invalid(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot delete invitation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteInvitationPayload{DeletedInvitationID: input.InvitationID}, nil
}
// UpdateProfile is the resolver for the updateProfile field.
func (r *mutationResolver) UpdateProfile(ctx context.Context, input types.UpdateProfileInput) (*types.UpdateProfilePayload, error) {
if err := r.authorize(ctx, input.ID, iam.ActionMembershipProfileUpdate); err != nil {
return nil, err
}
profile, err := r.iam.OrganizationService.UpdateProfile(
ctx,
&iam.UpdateProfileRequest{
ID: input.ID,
FullName: input.FullName,
AdditionalEmailAddresses: input.AdditionalEmailAddresses,
Kind: input.Kind,
Position: gqlutils.UnwrapOmittable(input.Position),
ContractStartDate: gqlutils.UnwrapOmittable(input.ContractStartDate),
ContractEndDate: gqlutils.UnwrapOmittable(input.ContractEndDate),
},
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateProfilePayload{
Profile: types.NewMembershipProfile(profile),
}, nil
}
// UpdateMembership is the resolver for the updateMembership field.
func (r *mutationResolver) UpdateMembership(ctx context.Context, input types.UpdateMembershipInput) (*types.UpdateMembershipPayload, error) {
if err := r.authorize(ctx, input.MembershipID, iam.ActionMembershipUpdate); err != nil {
return nil, err
}
if input.Role == coredata.MembershipRoleOwner {
if err := r.authorize(ctx, input.MembershipID, iam.ActionMembershipRoleSetOwner); err != nil {
return nil, err
}
}
membership, err := r.iam.OrganizationService.UpdateMempership(ctx, input.OrganizationID, input.MembershipID, input.Role)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update membership", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateMembershipPayload{
Membership: types.NewMembership(membership),
}, nil
}
// RemoveMember is the resolver for the removeMember field.
func (r *mutationResolver) RemoveMember(ctx context.Context, input types.RemoveMemberInput) (*types.RemoveMemberPayload, error) {
if err := r.authorize(ctx, input.MembershipID, iam.ActionMembershipDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.RemoveMember(ctx, input.OrganizationID, input.MembershipID)
if err != nil {
var errManagedBySCIM *iam.ErrMembershipManagedBySCIM
var errLastActiveOwner *iam.ErrLastActiveOwner
if errors.As(err, &errManagedBySCIM) {
return nil, gqlutils.Conflict(ctx, err)
}
if errors.As(err, &errLastActiveOwner) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot remove member from organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RemoveMemberPayload{DeletedMembershipID: input.MembershipID}, nil
}
// AcceptInvitation is the resolver for the acceptInvitation field.
func (r *mutationResolver) AcceptInvitation(ctx context.Context, input types.AcceptInvitationInput) (*types.AcceptInvitationPayload, error) {
if err := r.authorize(ctx, input.InvitationID, iam.ActionInvitationAccept); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
invitation, membership, err := r.iam.AccountService.AcceptInvitation(ctx, identity.ID, input.InvitationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot accept invitation", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.AcceptInvitationPayload{
MembershipEdge: types.NewMembershipEdge(membership, coredata.MembershipOrderFieldCreatedAt),
Invitation: types.NewInvitation(invitation),
}, nil
}
// CreateSAMLConfiguration is the resolver for the createSAMLConfiguration field.
func (r *mutationResolver) CreateSAMLConfiguration(ctx context.Context, input types.CreateSAMLConfigurationInput) (*types.CreateSAMLConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSAMLConfigurationCreate); err != nil {
return nil, err
}
req := &iam.CreateSAMLConfigurationRequest{
EmailDomain: input.EmailDomain,
IdPEntityID: input.IdpEntityID,
IdPSsoURL: input.IdpSsoURL,
IdPCertificate: input.IdpCertificate,
AutoSignupEnabled: input.AutoSignupEnabled,
}
if input.AttributeMappings != nil {
req.AttributeEmail = input.AttributeMappings.Email
req.AttributeFirstname = input.AttributeMappings.FirstName
req.AttributeLastname = input.AttributeMappings.LastName
req.AttributeRole = input.AttributeMappings.Role
}
samlConfiguration, err := r.iam.OrganizationService.CreateSAMLConfiguration(
ctx,
input.OrganizationID,
req,
)
if err != nil {
var errSAMLConfigurationEmailDomainAlreadyExists *iam.ErrSAMLConfigurationEmailDomainAlreadyExists
if errors.As(err, &errSAMLConfigurationEmailDomainAlreadyExists) {
return nil, gqlutils.Conflict(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot create saml configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.CreateSAMLConfigurationPayload{
SamlConfigurationEdge: types.NewSAMLConfigurationEdge(
samlConfiguration,
coredata.SAMLConfigurationOrderFieldCreatedAt,
),
}, nil
}
// UpdateSAMLConfiguration is the resolver for the updateSAMLConfiguration field.
func (r *mutationResolver) UpdateSAMLConfiguration(ctx context.Context, input types.UpdateSAMLConfigurationInput) (*types.UpdateSAMLConfigurationPayload, error) {
if err := r.authorize(ctx, input.SamlConfigurationID, iam.ActionSAMLConfigurationUpdate); err != nil {
return nil, err
}
req := &iam.UpdateSAMLConfigurationRequest{
IdPEntityID: input.IdpEntityID,
IdPSsoURL: input.IdpSsoURL,
IdPCertificate: input.IdpCertificate,
AutoSignupEnabled: input.AutoSignupEnabled,
EnforcementPolicy: &input.EnforcementPolicy,
}
if input.AttributeMappings != nil {
req.AttributeEmail = input.AttributeMappings.Email
req.AttributeFirstname = input.AttributeMappings.FirstName
req.AttributeLastname = input.AttributeMappings.LastName
req.AttributeRole = input.AttributeMappings.Role
}
samlConfiguration, err := r.iam.OrganizationService.UpdateSAMLConfiguration(
ctx,
input.OrganizationID,
input.SamlConfigurationID,
req,
)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update saml configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateSAMLConfigurationPayload{
SamlConfiguration: types.NewSAMLConfiguration(samlConfiguration),
}, nil
}
// DeleteSAMLConfiguration is the resolver for the deleteSAMLConfiguration field.
func (r *mutationResolver) DeleteSAMLConfiguration(ctx context.Context, input types.DeleteSAMLConfigurationInput) (*types.DeleteSAMLConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSAMLConfigurationDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.DeleteSAMLConfiguration(ctx, input.OrganizationID, input.SamlConfigurationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete saml configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteSAMLConfigurationPayload{DeletedSamlConfigurationID: input.SamlConfigurationID}, nil
}
// CreateSCIMConfiguration is the resolver for the createSCIMConfiguration field.
func (r *mutationResolver) CreateSCIMConfiguration(ctx context.Context, input types.CreateSCIMConfigurationInput) (*types.CreateSCIMConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSCIMConfigurationCreate); err != nil {
return nil, err
}
config, token, err := r.iam.OrganizationService.CreateSCIMConfiguration(ctx, input.OrganizationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
var bridge *types.SCIMBridge
if input.ConnectorID != nil {
scimBridge, err := r.iam.OrganizationService.CreateSCIMBridge(ctx, input.OrganizationID, config.ID, *input.ConnectorID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot create scim bridge", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
bridge = types.NewSCIMBridge(scimBridge)
}
payload := &types.CreateSCIMConfigurationPayload{
ScimConfiguration: types.NewSCIMConfiguration(config),
ScimBridge: bridge,
Token: token,
}
return payload, nil
}
// DeleteSCIMConfiguration is the resolver for the deleteSCIMConfiguration field.
func (r *mutationResolver) DeleteSCIMConfiguration(ctx context.Context, input types.DeleteSCIMConfigurationInput) (*types.DeleteSCIMConfigurationPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, iam.ActionSCIMConfigurationDelete); err != nil {
return nil, err
}
err := r.iam.OrganizationService.DeleteSCIMConfiguration(ctx, input.OrganizationID, input.ScimConfigurationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.DeleteSCIMConfigurationPayload{DeletedScimConfigurationID: input.ScimConfigurationID}, nil
}
// RegenerateSCIMToken is the resolver for the regenerateSCIMToken field.
func (r *mutationResolver) RegenerateSCIMToken(ctx context.Context, input types.RegenerateSCIMTokenInput) (*types.RegenerateSCIMTokenPayload, error) {
if err := r.authorize(ctx, input.ScimConfigurationID, iam.ActionSCIMConfigurationUpdate); err != nil {
return nil, err
}
config, token, err := r.iam.OrganizationService.RegenerateSCIMToken(ctx, input.OrganizationID, input.ScimConfigurationID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot regenerate scim token", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.RegenerateSCIMTokenPayload{
ScimConfiguration: types.NewSCIMConfiguration(config),
Token: token,
}, nil
}
// UpdateSCIMBridge is the resolver for the updateSCIMBridge field.
func (r *mutationResolver) UpdateSCIMBridge(ctx context.Context, input types.UpdateSCIMBridgeInput) (*types.UpdateSCIMBridgePayload, error) {
if err := r.authorize(ctx, input.ScimBridgeID, iam.ActionSCIMBridgeUpdate); err != nil {
return nil, err
}
bridge, err := r.iam.OrganizationService.UpdateSCIMBridge(ctx, input.OrganizationID, input.ScimBridgeID, input.ExcludedUserNames)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot update scim bridge excluded user names", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.UpdateSCIMBridgePayload{
ScimBridge: types.NewSCIMBridge(bridge),
}, nil
}
// LogoURL is the resolver for the logoUrl field.
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionOrganizationGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
presignedURL, err := r.iam.OrganizationService.GenerateLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate logo URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return presignedURL, nil
}
// HorizontalLogoURL is the resolver for the horizontalLogoUrl field.
func (r *organizationResolver) HorizontalLogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionOrganizationGet); err != nil {
return nil, err
}
presignedURL, err := r.iam.OrganizationService.GenerateHorizontalLogoURL(ctx, obj.ID, 1*time.Hour)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot generate horizontal logo URL", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return presignedURL, nil
}
// Members is the resolver for the members field.
func (r *organizationResolver) Members(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MembershipOrderBy) (*types.MembershipConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipList); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.MembershipConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
pageOrderBy := page.OrderBy[coredata.MembershipOrderField]{
Field: coredata.MembershipOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy.Field = coredata.MembershipOrderField(orderBy.Field)
pageOrderBy.Direction = page.OrderDirection(orderBy.Direction)
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.OrganizationService.ListMembers(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list memberships", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMembershipConnection(page, r, obj.ID), nil
}
// Invitations is the resolver for the invitations field.
func (r *organizationResolver) Invitations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, status *coredata.InvitationStatus, orderBy *types.InvitationOrderBy) (*types.InvitationConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionInvitationList); err != nil {
return nil, err
}
filters := coredata.NewInvitationFilter(nil)
if status != nil {
filters = coredata.NewInvitationFilter([]coredata.InvitationStatus{*status})
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.InvitationConnection{
Resolver: r,
ParentID: obj.ID,
Filters: filters,
}, nil
}
pageOrderBy := page.OrderBy[coredata.InvitationOrderField]{
Field: coredata.InvitationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.InvitationOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.OrganizationService.ListInvitations(ctx, obj.ID, cursor, filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list invitations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewInvitationConnection(page, r, obj.ID, filters), nil
}
// SamlConfigurations is the resolver for the samlConfigurations field.
func (r *organizationResolver) SamlConfigurations(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.SAMLConfigurationConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionSAMLConfigurationList); err != nil {
return nil, err
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.SAMLConfigurationConnection{
Resolver: r,
ParentID: obj.ID,
}, nil
}
pageOrderBy := page.OrderBy[coredata.SAMLConfigurationOrderField]{
Field: coredata.SAMLConfigurationOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.OrganizationService.ListSAMLConfigurations(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list saml configurations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSAMLConfigurationConnection(page, r, obj.ID), nil
}
// ScimConfiguration is the resolver for the scimConfiguration field.
func (r *organizationResolver) ScimConfiguration(ctx context.Context, obj *types.Organization) (*types.SCIMConfiguration, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
config, err := r.iam.OrganizationService.GetSCIMConfiguration(ctx, obj.ID)
if err != nil {
var notFound *iam.ErrNoSCIMConfigurationFound
if errors.As(err, &notFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSCIMConfiguration(config), nil
}
// ViewerMembership is the resolver for the viewerMembership field.
func (r *organizationResolver) ViewerMembership(ctx context.Context, obj *types.Organization) (*types.Membership, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipGet, authz.WithSkipAssumptionCheck()); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
membership, err := r.iam.AccountService.GetMembershipForOrganization(ctx, identity.ID, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get membership for organization", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMembership(membership), nil
}
// Permission is the resolver for the permission field.
func (r *organizationResolver) Permission(ctx context.Context, obj *types.Organization, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Token is the resolver for the token field.
func (r *personalAPIKeyResolver) Token(ctx context.Context, obj *types.PersonalAPIKey) (*string, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionPersonalAPIKeyGet); err != nil {
return nil, err
}
identity := authn.IdentityFromContext(ctx)
token, err := r.iam.AccountService.RevealPersonalAPIKeyToken(ctx, identity.ID, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot reveal personal api key token", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &token, nil
}
// Permission is the resolver for the permission field.
func (r *personalAPIKeyResolver) Permission(ctx context.Context, obj *types.PersonalAPIKey, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *personalAPIKeyConnectionResolver) TotalCount(ctx context.Context, obj *types.PersonalAPIKeyConnection) (*int, error) {
switch obj.Resolver.(type) {
case *identityResolver:
if err := r.authorize(ctx, obj.ParentID, iam.ActionPersonalAPIKeyList); err != nil {
return nil, err
}
count, err := r.iam.AccountService.CountPersonalAPIKeys(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count personal api keys", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Node is the resolver for the node field.
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
var (
loadNode func(ctx context.Context, id gid.GID) (types.Node, error)
action string
)
switch id.EntityType() {
case coredata.OrganizationEntityType:
action = iam.ActionOrganizationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
organization, err := r.iam.OrganizationService.GetOrganization(ctx, id)
if err != nil {
return nil, err
}
return types.NewOrganization(organization), nil
}
case coredata.IdentityEntityType:
action = iam.ActionIdentityGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
identity, err := r.iam.AccountService.GetIdentity(ctx, id)
if err != nil {
return nil, err
}
return types.NewIdentity(identity), nil
}
case coredata.SessionEntityType:
action = iam.ActionSessionGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
session, err := r.iam.GetSession(ctx, id)
if err != nil {
return nil, err
}
return types.NewSession(session), nil
}
case coredata.MembershipProfileEntityType:
action = iam.ActionMembershipGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
profile, err := r.iam.OrganizationService.GetProfile(ctx, id)
if err != nil {
return nil, err
}
return types.NewMembershipProfile(profile), nil
}
case coredata.MembershipEntityType:
action = iam.ActionMembershipGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
membership, err := r.iam.GetMembership(ctx, id)
if err != nil {
return nil, err
}
return types.NewMembership(membership), nil
}
case coredata.InvitationEntityType:
action = iam.ActionInvitationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
invitation, err := r.iam.GetInvitation(ctx, id)
if err != nil {
return nil, err
}
return types.NewInvitation(invitation), nil
}
case coredata.SAMLConfigurationEntityType:
action = iam.ActionSAMLConfigurationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
samlConfiguration, err := r.iam.GetSAMLconfiguration(ctx, id)
if err != nil {
return nil, err
}
return types.NewSAMLConfiguration(samlConfiguration), nil
}
case coredata.PersonalAPIKeyEntityType:
action = iam.ActionPersonalAPIKeyGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
personalAPIKey, err := r.iam.GetPersonalAPIKey(ctx, id)
if err != nil {
return nil, err
}
return types.NewPersonalAPIKey(personalAPIKey), nil
}
case coredata.SCIMConfigurationEntityType:
action = iam.ActionSCIMConfigurationGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
scimConfiguration, err := r.iam.GetSCIMConfiguration(ctx, id)
if err != nil {
return nil, err
}
return types.NewSCIMConfiguration(scimConfiguration), nil
}
case coredata.SCIMEventEntityType:
action = iam.ActionSCIMEventGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
scimEvent, err := r.iam.GetSCIMEvent(ctx, id)
if err != nil {
return nil, err
}
return types.NewSCIMEvent(scimEvent), nil
}
default:
return nil, fmt.Errorf("unsupported entity type: %d", id.EntityType())
}
if err := r.authorize(ctx, id, action); err != nil {
return nil, err
}
node, err := loadNode(ctx, id)
if err != nil {
var (
errOrganizationNotFound *iam.ErrOrganizationNotFound
errIdentityNotFound *iam.ErrIdentityNotFound
errSessionNotFound *iam.ErrSessionNotFound
errMembershipNotFound *iam.ErrMembershipNotFound
errInvitationNotFound *iam.ErrInvitationNotFound
isNotFoundErr = errors.As(err, &errOrganizationNotFound) ||
errors.As(err, &errIdentityNotFound) ||
errors.As(err, &errSessionNotFound) ||
errors.As(err, &errMembershipNotFound) ||
errors.As(err, &errInvitationNotFound)
)
if isNotFoundErr {
return nil, gqlutils.NotFound(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot load node", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return node, nil
}
// Viewer is the resolver for the viewer field.
func (r *queryResolver) Viewer(ctx context.Context) (*types.Identity, error) {
identity := authn.IdentityFromContext(ctx)
return &types.Identity{
ID: identity.ID,
Email: identity.EmailAddress,
EmailVerified: identity.EmailAddressVerified,
FullName: identity.FullName,
CreatedAt: identity.CreatedAt,
UpdatedAt: identity.UpdatedAt,
}, nil
}
// SsoLoginURL is the resolver for the ssoLoginURL field.
func (r *queryResolver) SsoLoginURL(ctx context.Context, email mail.Addr) (*string, error) {
count, err := r.iam.AccountService.CountSAMLConfigurationsForEmail(ctx, email)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count SAML configurations for email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if count != 1 {
if count == 0 {
return nil, graphql.ErrorOnPath(
ctx,
fmt.Errorf("no SAML configuration for email"),
)
}
return nil, graphql.ErrorOnPath(
ctx,
fmt.Errorf("multiple SSO configurations found for this domain. Please use your organization-specific SSO login URL"),
)
}
samlConfigs, err := r.iam.AccountService.ListSAMLConfigurationsForEmail(ctx, email)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list SAML configurations for email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
samlConfig := samlConfigs[0]
loginURL := r.baseURL.WithPath("/api/connect/v1/saml/2.0/" + samlConfig.ID.String()).MustString()
return &loginURL, nil
}
// TestLoginURL is the resolver for the testLoginUrl field.
func (r *sAMLConfigurationResolver) TestLoginURL(ctx context.Context, obj *types.SAMLConfiguration) (string, error) {
return r.baseURL.WithPath("/api/connect/v1/saml/2.0/" + obj.ID.String()).MustString(), nil
}
// Permission is the resolver for the permission field.
func (r *sAMLConfigurationResolver) Permission(ctx context.Context, obj *types.SAMLConfiguration, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *sAMLConfigurationConnectionResolver) TotalCount(ctx context.Context, obj *types.SAMLConfigurationConnection) (*int, error) {
switch obj.Resolver.(type) {
case *organizationResolver:
count, err := r.iam.OrganizationService.CountSAMLConfigurations(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count saml configurations", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// ScimConfiguration is the resolver for the scimConfiguration field.
func (r *sCIMBridgeResolver) ScimConfiguration(ctx context.Context, obj *types.SCIMBridge) (*types.SCIMConfiguration, error) {
if err := r.authorize(ctx, obj.ScimConfiguration.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.SCIMConfiguration{
ID: obj.ScimConfiguration.ID,
}, nil
}
scimConfiguration, err := r.iam.GetSCIMConfiguration(ctx, obj.ScimConfiguration.ID)
if err != nil {
var errNoSCIMConfigurationFound *iam.ErrNoSCIMConfigurationFound
if errors.As(err, &errNoSCIMConfigurationFound) {
return nil, nil
}
return nil, err
}
return types.NewSCIMConfiguration(scimConfiguration), nil
}
// Connector is the resolver for the connector field.
func (r *sCIMBridgeResolver) Connector(ctx context.Context, obj *types.SCIMBridge) (*types.Connector, error) {
if obj.Connector == nil {
return nil, nil
}
// Authorize based on the SCIM configuration (connector accessed via bridge is a sub-resource)
if err := r.authorize(ctx, obj.ScimConfiguration.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Connector{
ID: obj.Connector.ID,
}, nil
}
// Use metadata-only loading since we don't need the encrypted connection data
connector, err := r.iam.OrganizationService.GetConnectorMetadataByID(ctx, obj.Connector.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get connector", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewConnector(connector), nil
}
// Permission is the resolver for the permission field.
func (r *sCIMBridgeResolver) Permission(ctx context.Context, obj *types.SCIMBridge, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// EndpointURL is the resolver for the endpointUrl field.
func (r *sCIMConfigurationResolver) EndpointURL(ctx context.Context, obj *types.SCIMConfiguration) (string, error) {
return r.baseURL.WithPath("/api/connect/v1/scim/2.0").MustString(), nil
}
// Organization is the resolver for the organization field.
func (r *sCIMConfigurationResolver) Organization(ctx context.Context, obj *types.SCIMConfiguration) (*types.Organization, error) {
if err := r.authorize(ctx, obj.Organization.ID, iam.ActionOrganizationGet); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Organization{
ID: obj.Organization.ID,
}, nil
}
organization, err := r.iam.OrganizationService.GetOrganization(ctx, obj.Organization.ID)
if err != nil {
var errOrganizationNotFound *iam.ErrOrganizationNotFound
if errors.As(err, &errOrganizationNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get organization for scim configuration", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewOrganization(organization), nil
}
// Bridge is the resolver for the bridge field.
func (r *sCIMConfigurationResolver) Bridge(ctx context.Context, obj *types.SCIMConfiguration) (*types.SCIMBridge, error) {
if obj.Bridge == nil {
return nil, nil
}
if err := r.authorize(ctx, obj.ID, iam.ActionSCIMConfigurationGet); err != nil {
return nil, err
}
bridge, err := r.iam.OrganizationService.GetSCIMBridgeByID(ctx, obj.Bridge.ID)
if err != nil {
var errSCIMBridgeNotFound *iam.ErrSCIMBridgeNotFound
if errors.As(err, &errSCIMBridgeNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get scim bridge", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSCIMBridge(bridge), nil
}
// Events is the resolver for the events field.
func (r *sCIMConfigurationResolver) Events(ctx context.Context, obj *types.SCIMConfiguration, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SCIMEventOrderBy) (*types.SCIMEventConnection, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionSCIMEventList); err != nil {
return nil, err
}
pageOrderBy := page.OrderBy[coredata.SCIMEventOrderField]{
Field: coredata.SCIMEventOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy.Field = coredata.SCIMEventOrderField(orderBy.Field)
pageOrderBy.Direction = page.OrderDirection(orderBy.Direction)
}
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
events, err := r.iam.OrganizationService.ListSCIMEventsByConfigID(ctx, obj.ID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list scim events", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewSCIMEventConnection(events, r, obj.ID), nil
}
// Permission is the resolver for the permission field.
func (r *sCIMConfigurationResolver) Permission(ctx context.Context, obj *types.SCIMConfiguration, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// Membership is the resolver for the membership field.
func (r *sCIMEventResolver) Membership(ctx context.Context, obj *types.SCIMEvent) (*types.Membership, error) {
if obj.Membership == nil {
return nil, nil
}
if err := r.authorize(ctx, obj.Membership.ID, iam.ActionMembershipGet); err != nil {
return nil, err
}
if gqlutils.OnlyIDSelected(ctx) {
return &types.Membership{
ID: obj.Membership.ID,
}, nil
}
membership, err := r.iam.GetMembership(ctx, obj.Membership.ID)
if err != nil {
var errMembershipNotFound *iam.ErrMembershipNotFound
if errors.As(err, &errMembershipNotFound) {
return nil, nil
}
r.logger.ErrorCtx(ctx, "cannot get membership for scim event", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMembership(membership), nil
}
// Permission is the resolver for the permission field.
func (r *sCIMEventResolver) Permission(ctx context.Context, obj *types.SCIMEvent, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *sCIMEventConnectionResolver) TotalCount(ctx context.Context, obj *types.SCIMEventConnection) (*int, error) {
if err := r.authorize(ctx, obj.ParentID, iam.ActionSCIMEventList); err != nil {
return nil, err
}
switch obj.Resolver.(type) {
case *sCIMConfigurationResolver:
count, err := r.iam.OrganizationService.CountSCIMEvents(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count scim events", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Identity is the resolver for the identity field.
func (r *sessionResolver) Identity(ctx context.Context, obj *types.Session) (*types.Identity, error) {
if gqlutils.OnlyIDSelected(ctx) {
return &types.Identity{
ID: obj.Identity.ID,
}, nil
}
identity, err := r.iam.AccountService.GetIdentity(ctx, obj.Identity.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get identity for session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewIdentity(identity), nil
}
// Permission is the resolver for the permission field.
func (r *sessionResolver) Permission(ctx context.Context, obj *types.Session, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action)
}
// TotalCount is the resolver for the totalCount field.
func (r *sessionConnectionResolver) TotalCount(ctx context.Context, obj *types.SessionConnection) (*int, error) {
switch obj.Resolver.(type) {
case *identityResolver:
count, err := r.iam.AccountService.CountSessions(ctx, obj.ParentID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot count sessions", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &count, nil
}
r.logger.ErrorCtx(ctx, "unsupported resolver", log.Any("resolver", obj.Resolver))
return nil, gqlutils.Internal(ctx)
}
// Connector returns schema.ConnectorResolver implementation.
func (r *Resolver) Connector() schema.ConnectorResolver { return &connectorResolver{r} }
// Identity returns schema.IdentityResolver implementation.
func (r *Resolver) Identity() schema.IdentityResolver { return &identityResolver{r} }
// Invitation returns schema.InvitationResolver implementation.
func (r *Resolver) Invitation() schema.InvitationResolver { return &invitationResolver{r} }
// InvitationConnection returns schema.InvitationConnectionResolver implementation.
func (r *Resolver) InvitationConnection() schema.InvitationConnectionResolver {
return &invitationConnectionResolver{r}
}
// Membership returns schema.MembershipResolver implementation.
func (r *Resolver) Membership() schema.MembershipResolver { return &membershipResolver{r} }
// MembershipConnection returns schema.MembershipConnectionResolver implementation.
func (r *Resolver) MembershipConnection() schema.MembershipConnectionResolver {
return &membershipConnectionResolver{r}
}
// MembershipProfile returns schema.MembershipProfileResolver implementation.
func (r *Resolver) MembershipProfile() schema.MembershipProfileResolver {
return &membershipProfileResolver{r}
}
// Mutation returns schema.MutationResolver implementation.
func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver{r} }
// Organization returns schema.OrganizationResolver implementation.
func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} }
// PersonalAPIKey returns schema.PersonalAPIKeyResolver implementation.
func (r *Resolver) PersonalAPIKey() schema.PersonalAPIKeyResolver { return &personalAPIKeyResolver{r} }
// PersonalAPIKeyConnection returns schema.PersonalAPIKeyConnectionResolver implementation.
func (r *Resolver) PersonalAPIKeyConnection() schema.PersonalAPIKeyConnectionResolver {
return &personalAPIKeyConnectionResolver{r}
}
// Query returns schema.QueryResolver implementation.
func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
// SAMLConfiguration returns schema.SAMLConfigurationResolver implementation.
func (r *Resolver) SAMLConfiguration() schema.SAMLConfigurationResolver {
return &sAMLConfigurationResolver{r}
}
// SAMLConfigurationConnection returns schema.SAMLConfigurationConnectionResolver implementation.
func (r *Resolver) SAMLConfigurationConnection() schema.SAMLConfigurationConnectionResolver {
return &sAMLConfigurationConnectionResolver{r}
}
// SCIMBridge returns schema.SCIMBridgeResolver implementation.
func (r *Resolver) SCIMBridge() schema.SCIMBridgeResolver { return &sCIMBridgeResolver{r} }
// SCIMConfiguration returns schema.SCIMConfigurationResolver implementation.
func (r *Resolver) SCIMConfiguration() schema.SCIMConfigurationResolver {
return &sCIMConfigurationResolver{r}
}
// SCIMEvent returns schema.SCIMEventResolver implementation.
func (r *Resolver) SCIMEvent() schema.SCIMEventResolver { return &sCIMEventResolver{r} }
// SCIMEventConnection returns schema.SCIMEventConnectionResolver implementation.
func (r *Resolver) SCIMEventConnection() schema.SCIMEventConnectionResolver {
return &sCIMEventConnectionResolver{r}
}
// Session returns schema.SessionResolver implementation.
func (r *Resolver) Session() schema.SessionResolver { return &sessionResolver{r} }
// SessionConnection returns schema.SessionConnectionResolver implementation.
func (r *Resolver) SessionConnection() schema.SessionConnectionResolver {
return &sessionConnectionResolver{r}
}
type connectorResolver struct{ *Resolver }
type identityResolver struct{ *Resolver }
type invitationResolver struct{ *Resolver }
type invitationConnectionResolver struct{ *Resolver }
type membershipResolver struct{ *Resolver }
type membershipConnectionResolver struct{ *Resolver }
type membershipProfileResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
type personalAPIKeyResolver struct{ *Resolver }
type personalAPIKeyConnectionResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type sAMLConfigurationResolver struct{ *Resolver }
type sAMLConfigurationConnectionResolver struct{ *Resolver }
type sCIMBridgeResolver struct{ *Resolver }
type sCIMConfigurationResolver struct{ *Resolver }
type sCIMEventResolver struct{ *Resolver }
type sCIMEventConnectionResolver struct{ *Resolver }
type sessionResolver struct{ *Resolver }
type sessionConnectionResolver struct{ *Resolver }