Rewire the console and visitor resolvers onto the management and visitor services with compliance-portal authorization. Rename the GraphQL and MCP ComplianceExternalURL type to ComplianceCustomLink, expose trust center profile fields, default and custom domains, public URL, and the managed flag, and drop the profile fields from the organization surface. Signed-off-by: Bryan Frimin <bryan@probo.com>
361 lines
12 KiB
Go
361 lines
12 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.93
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"go.gearno.de/kit/log"
|
|
"go.probo.inc/probo/pkg/coredata"
|
|
"go.probo.inc/probo/pkg/iam"
|
|
"go.probo.inc/probo/pkg/iam/scim/bridge/provider/googleworkspace"
|
|
"go.probo.inc/probo/pkg/iam/scim/bridge/provider/microsoft365"
|
|
"go.probo.inc/probo/pkg/page"
|
|
"go.probo.inc/probo/pkg/server/api/authn"
|
|
"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"
|
|
)
|
|
|
|
// 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, profile, err := r.iam.OrganizationService.CreateOrganization(
|
|
ctx,
|
|
identity.ID,
|
|
&iam.CreateOrganizationRequest{
|
|
Name: input.Name,
|
|
LogoFile: logoFile,
|
|
HorizontalLogoFile: horizontalLogoFile,
|
|
},
|
|
)
|
|
if err != nil {
|
|
if errors.Is(err, coredata.ErrResourceAlreadyExists) {
|
|
return nil, gqlutils.Conflict(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot create organization", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return &types.CreateOrganizationPayload{
|
|
Organization: types.NewOrganization(organization),
|
|
Profile: types.NewProfile(profile),
|
|
}, 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,
|
|
}
|
|
|
|
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,
|
|
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"))
|
|
}
|
|
|
|
// Logo is the resolver for the logo field.
|
|
func (r *organizationResolver) Logo(ctx context.Context, obj *types.Organization) (*types.File, error) {
|
|
file, err := r.iam.OrganizationService.LogoFile(ctx, obj.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot load logo file", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if file == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return types.NewFile(file, r.fileManager), nil
|
|
}
|
|
|
|
// HorizontalLogo is the resolver for the horizontalLogo field.
|
|
func (r *organizationResolver) HorizontalLogo(ctx context.Context, obj *types.Organization) (*types.File, error) {
|
|
file, err := r.iam.OrganizationService.HorizontalLogoFile(ctx, obj.ID)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot load horizontal logo file", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
if file == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return types.NewFile(file, r.fileManager), nil
|
|
}
|
|
|
|
// Profiles is the resolver for the profiles field.
|
|
func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProfileOrderBy) (*types.ProfileConnection, error) {
|
|
if _, err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
filter := coredata.NewMembershipProfileFilter(nil).WithMembership()
|
|
|
|
if gqlutils.OnlyTotalCountSelected(ctx) {
|
|
return &types.ProfileConnection{
|
|
Resolver: r,
|
|
ParentID: obj.ID,
|
|
Filters: filter,
|
|
}, nil
|
|
}
|
|
|
|
pageOrderBy := page.OrderBy[coredata.MembershipProfileOrderField]{
|
|
Field: coredata.MembershipProfileOrderFieldFullName,
|
|
Direction: page.OrderDirectionAsc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.MembershipProfileOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
page, err := r.iam.OrganizationService.ListProfiles(ctx, obj.ID, cursor, filter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list profiles", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewProfileConnection(page, r, obj.ID, filter), 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 {
|
|
if _, ok := errors.AsType[*iam.ErrNoSCIMConfigurationFound](err); ok {
|
|
return nil, nil
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get scim configuration", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewSCIMConfiguration(config), nil
|
|
}
|
|
|
|
// ScimBridgeTypes is the resolver for the scimBridgeTypes field.
|
|
func (r *organizationResolver) ScimBridgeTypes(ctx context.Context, obj *types.Organization) ([]*types.SCIMBridgeTypeInfo, error) {
|
|
return []*types.SCIMBridgeTypeInfo{
|
|
{
|
|
Type: coredata.SCIMBridgeTypeGoogleWorkspace,
|
|
Oauth2Scopes: googleworkspace.OAuth2Scopes,
|
|
},
|
|
{
|
|
Type: coredata.SCIMBridgeTypeMicrosoft365,
|
|
Oauth2Scopes: microsoft365.OAuth2Scopes,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// AuditLogEntries is the resolver for the auditLogEntries field.
|
|
func (r *organizationResolver) AuditLogEntries(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditLogEntryOrderBy, filter *types.AuditLogEntryFilter) (*types.AuditLogEntryConnection, error) {
|
|
if _, err := r.authorize(ctx, obj.ID, iam.ActionAuditLogEntryList); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pageOrderBy := page.OrderBy[coredata.AuditLogEntryOrderField]{
|
|
Field: coredata.AuditLogEntryOrderFieldCreatedAt,
|
|
Direction: page.OrderDirectionDesc,
|
|
}
|
|
if orderBy != nil {
|
|
pageOrderBy = page.OrderBy[coredata.AuditLogEntryOrderField]{
|
|
Field: orderBy.Field,
|
|
Direction: orderBy.Direction,
|
|
}
|
|
}
|
|
|
|
c := cursor.NewCursor(first, after, last, before, pageOrderBy)
|
|
|
|
coredataFilter := coredata.NewAuditLogEntryFilter()
|
|
|
|
if filter != nil {
|
|
if filter.Action != nil {
|
|
coredataFilter.WithAction(*filter.Action)
|
|
}
|
|
|
|
if filter.ActorID != nil {
|
|
coredataFilter.WithActorID(*filter.ActorID)
|
|
}
|
|
|
|
if filter.ResourceType != nil {
|
|
coredataFilter.WithResourceType(*filter.ResourceType)
|
|
}
|
|
|
|
if filter.ResourceID != nil {
|
|
coredataFilter.WithResourceID(*filter.ResourceID)
|
|
}
|
|
}
|
|
|
|
p, err := r.iam.OrganizationService.ListAuditLogEntries(ctx, obj.ID, c, coredataFilter)
|
|
if err != nil {
|
|
r.logger.ErrorCtx(ctx, "cannot list audit log entries", log.Error(err))
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewAuditLogEntryConnection(p, r, obj.ID, coredataFilter), nil
|
|
}
|
|
|
|
// Viewer is the resolver for the viewer field.
|
|
func (r *organizationResolver) Viewer(ctx context.Context, obj *types.Organization) (*types.Profile, error) {
|
|
if _, err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
identity := authn.IdentityFromContext(ctx)
|
|
|
|
profile, err := r.iam.OrganizationService.GetProfileForIdentityAndOrganization(ctx, identity.ID, obj.ID)
|
|
if err != nil {
|
|
if _, ok := errors.AsType[*iam.ErrProfileNotFound](err); ok {
|
|
return nil, gqlutils.NotFound(ctx, err)
|
|
}
|
|
|
|
r.logger.ErrorCtx(ctx, "cannot get profile", log.Error(err))
|
|
|
|
return nil, gqlutils.Internal(ctx)
|
|
}
|
|
|
|
return types.NewProfile(profile), nil
|
|
}
|
|
|
|
// Permission is the resolver for the permission field.
|
|
func (r *organizationResolver) Permission(ctx context.Context, obj *types.Organization, action string, attributes map[string]any) (bool, error) {
|
|
return r.permission(ctx, obj, action, attributes)
|
|
}
|
|
|
|
// Organization returns schema.OrganizationResolver implementation.
|
|
func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} }
|
|
|
|
type organizationResolver struct{ *Resolver }
|