Add resourcealias application service
Introduce a standalone resourcealias package with its own service, IAM policies, and OAuth2 scopes so alias management no longer lives inside the trust center services. Remove the trust-center-specific alias services from probo and trust, and wire the new service into probod, the server, and the API layer. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
@@ -75,10 +75,6 @@ const (
|
|||||||
ActionComplianceExternalURLUpdate = "core:compliance-external-url:update"
|
ActionComplianceExternalURLUpdate = "core:compliance-external-url:update"
|
||||||
ActionComplianceExternalURLDelete = "core:compliance-external-url:delete"
|
ActionComplianceExternalURLDelete = "core:compliance-external-url:delete"
|
||||||
|
|
||||||
// TrustCenterAlias actions
|
|
||||||
ActionTrustCenterAliasSet = "core:trust-center-alias:set"
|
|
||||||
ActionTrustCenterAliasRemove = "core:trust-center-alias:remove"
|
|
||||||
|
|
||||||
// TrustCenterFile actions
|
// TrustCenterFile actions
|
||||||
ActionTrustCenterFileGet = "core:trust-center-file:get"
|
ActionTrustCenterFileGet = "core:trust-center-file:get"
|
||||||
ActionTrustCenterFileList = "core:trust-center-file:list"
|
ActionTrustCenterFileList = "core:trust-center-file:list"
|
||||||
|
|||||||
@@ -149,8 +149,6 @@ var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
|
|||||||
ActionComplianceExternalURLDelete,
|
ActionComplianceExternalURLDelete,
|
||||||
ActionCustomDomainCreate,
|
ActionCustomDomainCreate,
|
||||||
ActionCustomDomainDelete,
|
ActionCustomDomainDelete,
|
||||||
ActionTrustCenterAliasSet,
|
|
||||||
ActionTrustCenterAliasRemove,
|
|
||||||
},
|
},
|
||||||
ScopeV1ConnectorRead: {
|
ScopeV1ConnectorRead: {
|
||||||
ActionConnectorList,
|
ActionConnectorList,
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ type (
|
|||||||
TrustCenters *TrustCenterService
|
TrustCenters *TrustCenterService
|
||||||
TrustCenterAccesses *TrustCenterAccessService
|
TrustCenterAccesses *TrustCenterAccessService
|
||||||
TrustCenterReferences *TrustCenterReferenceService
|
TrustCenterReferences *TrustCenterReferenceService
|
||||||
TrustCenterAliases *TrustCenterAliasService
|
|
||||||
TrustCenterFiles *TrustCenterFileService
|
TrustCenterFiles *TrustCenterFileService
|
||||||
ComplianceFrameworks *ComplianceFrameworkService
|
ComplianceFrameworks *ComplianceFrameworkService
|
||||||
ComplianceExternalURLs *ComplianceExternalURLService
|
ComplianceExternalURLs *ComplianceExternalURLService
|
||||||
@@ -229,7 +228,6 @@ func NewService(
|
|||||||
svc.TrustCenters = &TrustCenterService{svc: svc}
|
svc.TrustCenters = &TrustCenterService{svc: svc}
|
||||||
svc.TrustCenterAccesses = &TrustCenterAccessService{svc: svc}
|
svc.TrustCenterAccesses = &TrustCenterAccessService{svc: svc}
|
||||||
svc.TrustCenterReferences = &TrustCenterReferenceService{svc: svc}
|
svc.TrustCenterReferences = &TrustCenterReferenceService{svc: svc}
|
||||||
svc.TrustCenterAliases = &TrustCenterAliasService{svc: svc}
|
|
||||||
svc.ComplianceFrameworks = &ComplianceFrameworkService{svc: svc}
|
svc.ComplianceFrameworks = &ComplianceFrameworkService{svc: svc}
|
||||||
svc.ComplianceExternalURLs = &ComplianceExternalURLService{svc: svc}
|
svc.ComplianceExternalURLs = &ComplianceExternalURLService{svc: svc}
|
||||||
svc.TrustCenterFiles = &TrustCenterFileService{
|
svc.TrustCenterFiles = &TrustCenterFileService{
|
||||||
|
|||||||
@@ -1,211 +0,0 @@
|
|||||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.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 probo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"go.gearno.de/kit/pg"
|
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
|
||||||
"go.probo.inc/probo/pkg/gid"
|
|
||||||
"go.probo.inc/probo/pkg/validator"
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
TrustCenterAliasService struct {
|
|
||||||
svc *Service
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateTrustCenterAliasRequest struct {
|
|
||||||
ResourceID gid.GID
|
|
||||||
Alias string
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrTrustCenterAliasResourceInvalid struct {
|
|
||||||
ResourceID gid.GID
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrTrustCenterAliasAuditReportMissing struct {
|
|
||||||
AuditID gid.GID
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (e ErrTrustCenterAliasResourceInvalid) Error() string {
|
|
||||||
return fmt.Sprintf("resource %q cannot have a trust center alias", e.ResourceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e ErrTrustCenterAliasAuditReportMissing) Error() string {
|
|
||||||
return fmt.Sprintf("audit %q has no report file", e.AuditID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (req *CreateTrustCenterAliasRequest) Validate() error {
|
|
||||||
v := validator.New()
|
|
||||||
|
|
||||||
v.Check(req.ResourceID, "resource_id", validator.Required(), validator.GID())
|
|
||||||
v.Check(req.Alias, "alias", validator.Required(), validator.Slug(NameMaxLength))
|
|
||||||
|
|
||||||
return v.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) ResolveAlias(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
organizationID gid.GID,
|
|
||||||
alias string,
|
|
||||||
) (gid.GID, error) {
|
|
||||||
record := &coredata.TrustCenterAlias{}
|
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
if err := record.LoadByAlias(ctx, conn, scope, organizationID, alias); err != nil {
|
|
||||||
return fmt.Errorf("cannot load trust center alias: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return gid.Nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return record.ResourceID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) Create(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
req CreateTrustCenterAliasRequest,
|
|
||||||
) (*coredata.TrustCenterAlias, error) {
|
|
||||||
if err := req.Validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
aliasResourceID, err := s.aliasResourceID(ctx, scope, req.ResourceID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
alias := &coredata.TrustCenterAlias{}
|
|
||||||
|
|
||||||
err = s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
if err := alias.Upsert(ctx, conn, scope, aliasResourceID, req.Alias); err != nil {
|
|
||||||
return fmt.Errorf("cannot create trust center alias: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return alias, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) Remove(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
resourceID gid.GID,
|
|
||||||
) (gid.GID, error) {
|
|
||||||
aliasResourceID, err := s.aliasResourceID(ctx, scope, resourceID)
|
|
||||||
if err != nil {
|
|
||||||
return gid.Nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
alias := &coredata.TrustCenterAlias{ResourceID: aliasResourceID}
|
|
||||||
if err := alias.Delete(ctx, conn, scope); err != nil {
|
|
||||||
return fmt.Errorf("cannot remove trust center alias: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return gid.Nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return aliasResourceID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) GetByResourceID(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
resourceID gid.GID,
|
|
||||||
) (*string, error) {
|
|
||||||
aliasResourceID, err := s.aliasResourceID(ctx, scope, resourceID)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
alias := &coredata.TrustCenterAlias{}
|
|
||||||
|
|
||||||
err = s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
if err := alias.LoadByResourceID(ctx, conn, scope, aliasResourceID); err != nil {
|
|
||||||
return fmt.Errorf("cannot load trust center alias: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &alias.Alias, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) aliasResourceID(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
resourceID gid.GID,
|
|
||||||
) (gid.GID, error) {
|
|
||||||
switch resourceID.EntityType() {
|
|
||||||
case coredata.DocumentEntityType, coredata.TrustCenterFileEntityType:
|
|
||||||
return resourceID, nil
|
|
||||||
|
|
||||||
case coredata.AuditEntityType:
|
|
||||||
audit, err := s.svc.Audits.Get(ctx, scope, resourceID)
|
|
||||||
if err != nil {
|
|
||||||
return gid.Nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if audit.ReportFileID == nil {
|
|
||||||
return gid.Nil, &ErrTrustCenterAliasAuditReportMissing{AuditID: audit.ID}
|
|
||||||
}
|
|
||||||
|
|
||||||
return *audit.ReportFileID, nil
|
|
||||||
|
|
||||||
default:
|
|
||||||
return gid.Nil, &ErrTrustCenterAliasResourceInvalid{ResourceID: resourceID}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -66,6 +66,7 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/mailer"
|
"go.probo.inc/probo/pkg/mailer"
|
||||||
"go.probo.inc/probo/pkg/mailman"
|
"go.probo.inc/probo/pkg/mailman"
|
||||||
"go.probo.inc/probo/pkg/probo"
|
"go.probo.inc/probo/pkg/probo"
|
||||||
|
"go.probo.inc/probo/pkg/resourcealias"
|
||||||
"go.probo.inc/probo/pkg/riskmanagement"
|
"go.probo.inc/probo/pkg/riskmanagement"
|
||||||
"go.probo.inc/probo/pkg/securecookie"
|
"go.probo.inc/probo/pkg/securecookie"
|
||||||
"go.probo.inc/probo/pkg/server"
|
"go.probo.inc/probo/pkg/server"
|
||||||
@@ -583,6 +584,8 @@ func (impl *Implm) Run(
|
|||||||
return fmt.Errorf("cannot create probo service: %w", err)
|
return fmt.Errorf("cannot create probo service: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resourceAliasService := resourcealias.NewService(pgClient)
|
||||||
|
|
||||||
trustService := trust.NewService(
|
trustService := trust.NewService(
|
||||||
pgClient,
|
pgClient,
|
||||||
s3Client,
|
s3Client,
|
||||||
@@ -595,6 +598,7 @@ func (impl *Implm) Run(
|
|||||||
fileManagerService,
|
fileManagerService,
|
||||||
l,
|
l,
|
||||||
slackService,
|
slackService,
|
||||||
|
resourceAliasService,
|
||||||
)
|
)
|
||||||
|
|
||||||
accessReviewService := accessreview.NewService(
|
accessReviewService := accessreview.NewService(
|
||||||
@@ -609,6 +613,11 @@ func (impl *Implm) Run(
|
|||||||
|
|
||||||
iamService.Authorizer.RegisterPolicySet(agentrun.PolicySet())
|
iamService.Authorizer.RegisterPolicySet(agentrun.PolicySet())
|
||||||
iamService.Authorizer.RegisterPolicySet(accessreview.PolicySet())
|
iamService.Authorizer.RegisterPolicySet(accessreview.PolicySet())
|
||||||
|
iamService.Authorizer.RegisterPolicySet(resourcealias.PolicySet())
|
||||||
|
iamService.OAuth2ScopeRegistry.Register(agentrun.OAuth2ScopeMappings)
|
||||||
|
iamService.OAuth2ScopeRegistry.Register(accessreview.OAuth2ScopeMappings)
|
||||||
|
iamService.OAuth2ScopeRegistry.Register(iam.IAMOAuth2ScopeMappings)
|
||||||
|
iamService.OAuth2ScopeRegistry.Register(resourcealias.OAuth2ScopeMappings)
|
||||||
|
|
||||||
thirdPartyService := thirdparty.NewService(pgClient, fileManagerService, thirdPartyVetter)
|
thirdPartyService := thirdparty.NewService(pgClient, fileManagerService, thirdPartyVetter)
|
||||||
riskManagementService := riskmanagement.NewService(pgClient)
|
riskManagementService := riskmanagement.NewService(pgClient)
|
||||||
@@ -618,6 +627,7 @@ func (impl *Implm) Run(
|
|||||||
AllowedOrigins: impl.cfg.Api.Cors.AllowedOrigins,
|
AllowedOrigins: impl.cfg.Api.Cors.AllowedOrigins,
|
||||||
ExtraHeaderFields: impl.cfg.Api.ExtraHeaderFields,
|
ExtraHeaderFields: impl.cfg.Api.ExtraHeaderFields,
|
||||||
Probo: proboService,
|
Probo: proboService,
|
||||||
|
ResourceAlias: resourceAliasService,
|
||||||
File: fileManagerService,
|
File: fileManagerService,
|
||||||
IAM: iamService,
|
IAM: iamService,
|
||||||
Trust: trustService,
|
Trust: trustService,
|
||||||
|
|||||||
23
pkg/resourcealias/actions.go
Normal file
23
pkg/resourcealias/actions.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2025-2026 Probo Inc <hello@probo.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 resourcealias
|
||||||
|
|
||||||
|
// Resource alias service actions.
|
||||||
|
// Format: resourcealias:alias:<action>
|
||||||
|
const (
|
||||||
|
ActionAliasGet = "resourcealias:alias:get"
|
||||||
|
ActionAliasSet = "resourcealias:alias:set"
|
||||||
|
ActionAliasRemove = "resourcealias:alias:remove"
|
||||||
|
)
|
||||||
33
pkg/resourcealias/oauth2_scopes.go
Normal file
33
pkg/resourcealias/oauth2_scopes.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) 2025-2026 Probo Inc <hello@probo.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 resourcealias
|
||||||
|
|
||||||
|
import "go.probo.inc/probo/pkg/coredata"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ScopeV1ResourceAliasRead coredata.OAuth2Scope = "v1:resource-alias:read"
|
||||||
|
ScopeV1ResourceAlias coredata.OAuth2Scope = "v1:resource-alias"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OAuth2ScopeMappings maps OAuth2 scopes to resource-alias actions.
|
||||||
|
var OAuth2ScopeMappings = map[coredata.OAuth2Scope][]string{
|
||||||
|
ScopeV1ResourceAliasRead: {
|
||||||
|
ActionAliasGet,
|
||||||
|
},
|
||||||
|
ScopeV1ResourceAlias: {
|
||||||
|
ActionAliasSet,
|
||||||
|
ActionAliasRemove,
|
||||||
|
},
|
||||||
|
}
|
||||||
55
pkg/resourcealias/policies.go
Normal file
55
pkg/resourcealias/policies.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) 2025-2026 Probo Inc <hello@probo.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 resourcealias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.probo.inc/probo/pkg/iam"
|
||||||
|
"go.probo.inc/probo/pkg/iam/policy"
|
||||||
|
)
|
||||||
|
|
||||||
|
var organizationCondition = policy.Equals("principal.organization_id", "resource.organization_id")
|
||||||
|
|
||||||
|
// FullAccessPolicy grants complete resource-alias access to organization owners
|
||||||
|
// and admins.
|
||||||
|
var FullAccessPolicy = policy.NewPolicy(
|
||||||
|
"resourcealias:full-access",
|
||||||
|
"Resource Alias Full Access",
|
||||||
|
policy.Allow(
|
||||||
|
ActionAliasGet,
|
||||||
|
ActionAliasSet,
|
||||||
|
ActionAliasRemove,
|
||||||
|
).WithSID("resource-alias-full-access").When(organizationCondition),
|
||||||
|
).WithDescription("Full resource-alias access including set and remove")
|
||||||
|
|
||||||
|
// ReadAccessPolicy grants read-only resource-alias access to viewers and auditors.
|
||||||
|
var ReadAccessPolicy = policy.NewPolicy(
|
||||||
|
"resourcealias:read-access",
|
||||||
|
"Resource Alias Read Access",
|
||||||
|
policy.Allow(
|
||||||
|
ActionAliasGet,
|
||||||
|
).WithSID("resource-alias-read-access").When(organizationCondition),
|
||||||
|
).WithDescription("Read-only resource-alias access")
|
||||||
|
|
||||||
|
// PolicySet returns the PolicySet for the resource-alias service. It is owned by
|
||||||
|
// this package and registered into the authorizer at composition time so the
|
||||||
|
// resource-alias authorization rules live alongside the resource-alias domain
|
||||||
|
// logic instead of in the core probo policy set.
|
||||||
|
func PolicySet() *iam.PolicySet {
|
||||||
|
return iam.NewPolicySet().
|
||||||
|
AddRolePolicy("OWNER", FullAccessPolicy).
|
||||||
|
AddRolePolicy("ADMIN", FullAccessPolicy).
|
||||||
|
AddRolePolicy("VIEWER", ReadAccessPolicy).
|
||||||
|
AddRolePolicy("AUDITOR", ReadAccessPolicy)
|
||||||
|
}
|
||||||
185
pkg/resourcealias/service.go
Normal file
185
pkg/resourcealias/service.go
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
// Copyright (c) 2025-2026 Probo Inc <hello@probo.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 resourcealias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.gearno.de/kit/pg"
|
||||||
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
|
"go.probo.inc/probo/pkg/gid"
|
||||||
|
"go.probo.inc/probo/pkg/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
const aliasMaxLength = 100
|
||||||
|
|
||||||
|
type (
|
||||||
|
Service struct {
|
||||||
|
pg *pg.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateRequest struct {
|
||||||
|
ResourceID gid.GID
|
||||||
|
Alias string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewService(pgClient *pg.Client) *Service {
|
||||||
|
return &Service{
|
||||||
|
pg: pgClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *CreateRequest) Validate() error {
|
||||||
|
v := validator.New()
|
||||||
|
|
||||||
|
v.Check(req.ResourceID, "resource_id", validator.Required(), validator.GID())
|
||||||
|
v.Check(req.Alias, "alias", validator.Required(), validator.Slug(aliasMaxLength))
|
||||||
|
|
||||||
|
return v.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) ResolveAlias(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
alias string,
|
||||||
|
) (gid.GID, error) {
|
||||||
|
record := &coredata.ResourceAlias{}
|
||||||
|
|
||||||
|
err := s.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
|
if err := record.LoadByAlias(ctx, conn, scope, alias); err != nil {
|
||||||
|
return fmt.Errorf("cannot load resource alias: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return gid.Nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return record.ResourceID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Create(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
req CreateRequest,
|
||||||
|
) (*coredata.ResourceAlias, error) {
|
||||||
|
if err := req.Validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
alias := &coredata.ResourceAlias{}
|
||||||
|
|
||||||
|
err := s.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
|
if err := alias.Upsert(ctx, conn, scope, req.ResourceID, req.Alias); err != nil {
|
||||||
|
return fmt.Errorf("cannot create resource alias: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return alias, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Remove(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
resourceID gid.GID,
|
||||||
|
) error {
|
||||||
|
return s.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
|
alias := &coredata.ResourceAlias{ResourceID: resourceID}
|
||||||
|
if err := alias.Delete(ctx, conn, scope); err != nil {
|
||||||
|
return fmt.Errorf("cannot remove resource alias: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetByResourceID(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
resourceID gid.GID,
|
||||||
|
) (*string, error) {
|
||||||
|
record := &coredata.ResourceAlias{}
|
||||||
|
|
||||||
|
err := s.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
|
if err := record.LoadByResourceID(ctx, conn, scope, resourceID); err != nil {
|
||||||
|
return fmt.Errorf("cannot load resource alias: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &record.Alias, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) LoadByResourceIDs(
|
||||||
|
ctx context.Context,
|
||||||
|
scope coredata.Scoper,
|
||||||
|
resourceIDs []gid.GID,
|
||||||
|
) (map[gid.GID]string, error) {
|
||||||
|
if len(resourceIDs) == 0 {
|
||||||
|
return map[gid.GID]string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var aliases coredata.ResourceAliases
|
||||||
|
|
||||||
|
err := s.pg.WithConn(
|
||||||
|
ctx,
|
||||||
|
func(ctx context.Context, conn pg.Querier) error {
|
||||||
|
if err := aliases.LoadByResourceIDs(ctx, conn, scope, resourceIDs); err != nil {
|
||||||
|
return fmt.Errorf("cannot load resource aliases: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[gid.GID]string, len(aliases))
|
||||||
|
for _, alias := range aliases {
|
||||||
|
result[alias.ResourceID] = alias.Alias
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/iam"
|
"go.probo.inc/probo/pkg/iam"
|
||||||
"go.probo.inc/probo/pkg/mailman"
|
"go.probo.inc/probo/pkg/mailman"
|
||||||
"go.probo.inc/probo/pkg/probo"
|
"go.probo.inc/probo/pkg/probo"
|
||||||
|
"go.probo.inc/probo/pkg/resourcealias"
|
||||||
"go.probo.inc/probo/pkg/riskmanagement"
|
"go.probo.inc/probo/pkg/riskmanagement"
|
||||||
"go.probo.inc/probo/pkg/securecookie"
|
"go.probo.inc/probo/pkg/securecookie"
|
||||||
connect_v1 "go.probo.inc/probo/pkg/server/api/connect/v1"
|
connect_v1 "go.probo.inc/probo/pkg/server/api/connect/v1"
|
||||||
@@ -56,6 +57,7 @@ type (
|
|||||||
BaseURL *baseurl.BaseURL
|
BaseURL *baseurl.BaseURL
|
||||||
AllowedOrigins []string
|
AllowedOrigins []string
|
||||||
Probo *probo.Service
|
Probo *probo.Service
|
||||||
|
ResourceAlias *resourcealias.Service
|
||||||
File *filemanager.Service
|
File *filemanager.Service
|
||||||
IAM *iam.Service
|
IAM *iam.Service
|
||||||
Trust *trust.Service
|
Trust *trust.Service
|
||||||
@@ -179,6 +181,7 @@ func NewServer(cfg Config) (*Server, error) {
|
|||||||
cfg.Logger.Named("trust.v1"),
|
cfg.Logger.Named("trust.v1"),
|
||||||
cfg.IAM,
|
cfg.IAM,
|
||||||
cfg.Trust,
|
cfg.Trust,
|
||||||
|
cfg.ResourceAlias,
|
||||||
cfg.File,
|
cfg.File,
|
||||||
cfg.ESign,
|
cfg.ESign,
|
||||||
cfg.Mailman,
|
cfg.Mailman,
|
||||||
@@ -189,6 +192,7 @@ func NewServer(cfg Config) (*Server, error) {
|
|||||||
consoleHandler: console_v1.NewMux(
|
consoleHandler: console_v1.NewMux(
|
||||||
cfg.Logger.Named("console.v1"),
|
cfg.Logger.Named("console.v1"),
|
||||||
cfg.Probo,
|
cfg.Probo,
|
||||||
|
cfg.ResourceAlias,
|
||||||
cfg.IAM,
|
cfg.IAM,
|
||||||
cfg.ESign,
|
cfg.ESign,
|
||||||
cfg.AccessReview,
|
cfg.AccessReview,
|
||||||
@@ -221,6 +225,7 @@ func NewServer(cfg Config) (*Server, error) {
|
|||||||
mcpHandler: mcp_v1.NewMux(
|
mcpHandler: mcp_v1.NewMux(
|
||||||
cfg.Logger.Named("mcp.v1"),
|
cfg.Logger.Named("mcp.v1"),
|
||||||
cfg.Probo,
|
cfg.Probo,
|
||||||
|
cfg.ResourceAlias,
|
||||||
cfg.ThirdParty,
|
cfg.ThirdParty,
|
||||||
cfg.IAM,
|
cfg.IAM,
|
||||||
cfg.AccessReview,
|
cfg.AccessReview,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/iam/oauth2"
|
"go.probo.inc/probo/pkg/iam/oauth2"
|
||||||
"go.probo.inc/probo/pkg/mailman"
|
"go.probo.inc/probo/pkg/mailman"
|
||||||
"go.probo.inc/probo/pkg/probo"
|
"go.probo.inc/probo/pkg/probo"
|
||||||
|
"go.probo.inc/probo/pkg/resourcealias"
|
||||||
"go.probo.inc/probo/pkg/riskmanagement"
|
"go.probo.inc/probo/pkg/riskmanagement"
|
||||||
"go.probo.inc/probo/pkg/securecookie"
|
"go.probo.inc/probo/pkg/securecookie"
|
||||||
"go.probo.inc/probo/pkg/server/api"
|
"go.probo.inc/probo/pkg/server/api"
|
||||||
@@ -55,6 +56,7 @@ type Config struct {
|
|||||||
AllowedOrigins []string
|
AllowedOrigins []string
|
||||||
ExtraHeaderFields map[string]string
|
ExtraHeaderFields map[string]string
|
||||||
Probo *probo.Service
|
Probo *probo.Service
|
||||||
|
ResourceAlias *resourcealias.Service
|
||||||
File *filemanager.Service
|
File *filemanager.Service
|
||||||
IAM *iam.Service
|
IAM *iam.Service
|
||||||
Trust *trust.Service
|
Trust *trust.Service
|
||||||
@@ -94,6 +96,7 @@ func NewServer(cfg Config) (*Server, error) {
|
|||||||
BaseURL: cfg.BaseURL,
|
BaseURL: cfg.BaseURL,
|
||||||
AllowedOrigins: cfg.AllowedOrigins,
|
AllowedOrigins: cfg.AllowedOrigins,
|
||||||
Probo: cfg.Probo,
|
Probo: cfg.Probo,
|
||||||
|
ResourceAlias: cfg.ResourceAlias,
|
||||||
File: cfg.File,
|
File: cfg.File,
|
||||||
IAM: cfg.IAM,
|
IAM: cfg.IAM,
|
||||||
Trust: cfg.Trust,
|
Trust: cfg.Trust,
|
||||||
|
|||||||
@@ -364,9 +364,9 @@ func (s *Service) fetchDocumentIDs(ctx context.Context, scope coredata.Scoper, o
|
|||||||
cursorKey = &ck
|
cursorKey = &ck
|
||||||
}
|
}
|
||||||
|
|
||||||
aliases, err := s.TrustCenterAliases.LoadByResourceIDs(ctx, scope, resourceIDs)
|
aliases, err := s.resourceAlias.LoadByResourceIDs(ctx, scope, resourceIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot load trust center aliases: %w", err)
|
return nil, fmt.Errorf("cannot load resource aliases: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
paths := make([]string, 0, len(resourceIDs))
|
paths := make([]string, 0, len(resourceIDs))
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import (
|
|||||||
"go.probo.inc/probo/pkg/html2pdf"
|
"go.probo.inc/probo/pkg/html2pdf"
|
||||||
"go.probo.inc/probo/pkg/iam"
|
"go.probo.inc/probo/pkg/iam"
|
||||||
"go.probo.inc/probo/pkg/probo"
|
"go.probo.inc/probo/pkg/probo"
|
||||||
|
"go.probo.inc/probo/pkg/resourcealias"
|
||||||
"go.probo.inc/probo/pkg/slack"
|
"go.probo.inc/probo/pkg/slack"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ type (
|
|||||||
Reports *ReportService
|
Reports *ReportService
|
||||||
Organizations *OrganizationService
|
Organizations *OrganizationService
|
||||||
ComplianceExternalURLs *ComplianceExternalURLService
|
ComplianceExternalURLs *ComplianceExternalURLService
|
||||||
TrustCenterAliases *TrustCenterAliasService
|
resourceAlias *resourcealias.Service
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -78,6 +79,7 @@ func NewService(
|
|||||||
fileManagerService *filemanager.Service,
|
fileManagerService *filemanager.Service,
|
||||||
logger *log.Logger,
|
logger *log.Logger,
|
||||||
slack *slack.Service,
|
slack *slack.Service,
|
||||||
|
resourceAliasSvc *resourcealias.Service,
|
||||||
) *Service {
|
) *Service {
|
||||||
svc := &Service{
|
svc := &Service{
|
||||||
pg: pgClient,
|
pg: pgClient,
|
||||||
@@ -91,6 +93,7 @@ func NewService(
|
|||||||
fileManager: fileManagerService,
|
fileManager: fileManagerService,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
slack: slack,
|
slack: slack,
|
||||||
|
resourceAlias: resourceAliasSvc,
|
||||||
}
|
}
|
||||||
svc.TrustCenters = &TrustCenterService{svc: svc}
|
svc.TrustCenters = &TrustCenterService{svc: svc}
|
||||||
svc.Documents = &DocumentService{svc: svc, html2pdfConverter: html2pdfConverter}
|
svc.Documents = &DocumentService{svc: svc, html2pdfConverter: html2pdfConverter}
|
||||||
@@ -104,7 +107,6 @@ func NewService(
|
|||||||
svc.Reports = &ReportService{svc: svc}
|
svc.Reports = &ReportService{svc: svc}
|
||||||
svc.Organizations = &OrganizationService{svc: svc}
|
svc.Organizations = &OrganizationService{svc: svc}
|
||||||
svc.ComplianceExternalURLs = &ComplianceExternalURLService{svc: svc}
|
svc.ComplianceExternalURLs = &ComplianceExternalURLService{svc: svc}
|
||||||
svc.TrustCenterAliases = &TrustCenterAliasService{svc: svc}
|
|
||||||
|
|
||||||
return svc
|
return svc
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
// Copyright (c) 2025-2026 Probo Inc <hello@probo.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 trust
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"go.gearno.de/kit/pg"
|
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
|
||||||
"go.probo.inc/probo/pkg/gid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TrustCenterAliasService struct {
|
|
||||||
svc *Service
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) ResolveAlias(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
organizationID gid.GID,
|
|
||||||
alias string,
|
|
||||||
) (gid.GID, error) {
|
|
||||||
record := &coredata.TrustCenterAlias{}
|
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
if err := record.LoadByAlias(ctx, conn, scope, organizationID, alias); err != nil {
|
|
||||||
return fmt.Errorf("cannot load trust center alias: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return gid.Nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return record.ResourceID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) GetByStorageResourceID(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
storageResourceID gid.GID,
|
|
||||||
) (*string, error) {
|
|
||||||
record := &coredata.TrustCenterAlias{}
|
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
if err := record.LoadByResourceID(ctx, conn, scope, storageResourceID); err != nil {
|
|
||||||
return fmt.Errorf("cannot load trust center alias: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &record.Alias, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s TrustCenterAliasService) LoadByResourceIDs(
|
|
||||||
ctx context.Context,
|
|
||||||
scope coredata.Scoper,
|
|
||||||
resourceIDs []gid.GID,
|
|
||||||
) (map[gid.GID]string, error) {
|
|
||||||
if len(resourceIDs) == 0 {
|
|
||||||
return map[gid.GID]string{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var aliases coredata.TrustCenterAliases
|
|
||||||
|
|
||||||
err := s.svc.pg.WithConn(
|
|
||||||
ctx,
|
|
||||||
func(ctx context.Context, conn pg.Querier) error {
|
|
||||||
if err := aliases.LoadByResourceIDs(ctx, conn, scope, resourceIDs); err != nil {
|
|
||||||
return fmt.Errorf("cannot load trust center aliases: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make(map[gid.GID]string, len(aliases))
|
|
||||||
for _, alias := range aliases {
|
|
||||||
result[alias.ResourceID] = alias.Alias
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user