Files
probo/pkg/server/api/complianceportal/v1/auth_resolvers.go
Bryan Frimin 43ce3a7c53 Harden compliance portal auth and TLS
Align console references and OAuth branding with the
compliance-page model, and fix certificate cache eviction,
portal OAuth handlers, and magic-link edge cases left after
the trust-center rename.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-07-21 15:44:22 +02:00

120 lines
4.1 KiB
Go

package complianceportal_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"
"go.gearno.de/kit/log"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/iam"
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/complianceportal"
"go.probo.inc/probo/pkg/server/api/complianceportal/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/validator"
)
// UpdateFullName is the resolver for the updateFullName field.
func (r *mutationResolver) UpdateFullName(ctx context.Context, input types.UpdateFullNameInput) (*types.UpdateFullNamePayload, error) {
identity := authn.IdentityFromContext(ctx)
if identity == nil {
return nil, gqlutils.Unauthenticatedf(ctx, "authentication is required to request access")
}
compliancePage := complianceportal.CompliancePageFromContext(ctx)
profile, err := r.iam.OrganizationService.GetProfileForIdentityAndOrganization(ctx, identity.ID, compliancePage.OrganizationID)
if err != nil {
// External trust-center visitors have no organization profile; only
// their identity needs updating.
if _, ok := errors.AsType[*iam.ErrProfileNotFound](err); !ok {
r.logger.ErrorCtx(ctx, "cannot get profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
profile = nil
}
// The identity and profile full names are validated by different rules.
// Validate the profile update up front so it cannot fail after the
// identity has already been mutated, keeping the two in sync.
var updateUserRequest *iam.UpdateUserRequest
if profile != nil && profile.Source == coredata.ProfileSourceManual {
updateUserRequest = &iam.UpdateUserRequest{
ID: profile.ID,
FullName: input.FullName,
AdditionalEmailAddresses: profile.AdditionalEmailAddresses,
Kind: profile.Kind,
Position: profile.Position,
ContractStartDate: &profile.ContractStartDate,
ContractEndDate: &profile.ContractEndDate,
}
if err := updateUserRequest.Validate(); err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot validate profile update", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
if _, err := r.iam.AccountService.UpdateIdentity(
ctx,
identity.ID,
&iam.UpdateIdentityRequest{
FullName: input.FullName,
},
); err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update identity", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if updateUserRequest != nil {
if _, err := r.iam.OrganizationService.UpdateUser(ctx, updateUserRequest); err != nil {
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
}
r.logger.ErrorCtx(ctx, "cannot update profile", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
return &types.UpdateFullNamePayload{Success: true}, 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 {
_, notFound := errors.AsType[*iam.ErrSessionNotFound](err)
_, expired := errors.AsType[*iam.ErrSessionExpired](err)
if !notFound && !expired {
r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
// Already closed or missing — still clear the cookie so the browser
// drops the stale session on concurrent / retried logout.
}
w := gqlutils.HTTPResponseWriterFromContext(ctx)
r.sessionCookie.Clear(w)
return &types.SignOutPayload{Success: true}, nil
}