Update RBAC on console
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -1,359 +0,0 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
)
|
||||
|
||||
type (
|
||||
PersonalAPIKeyMembership struct {
|
||||
ID gid.GID `db:"id"`
|
||||
PersonalAPIKeyID gid.GID `db:"personal_api_key_id"`
|
||||
MembershipID gid.GID `db:"membership_id"`
|
||||
Role APIRole `db:"role"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
OrganizationName string `db:"organization_name"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
|
||||
PersonalAPIKeyMemberships []*PersonalAPIKeyMembership
|
||||
)
|
||||
|
||||
func (a *PersonalAPIKeyMembership) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO
|
||||
iam_personal_api_key_memberships (id, tenant_id, personal_api_key_id, membership_id, role, organization_id, created_at, updated_at)
|
||||
VALUES (
|
||||
@id,
|
||||
@tenant_id,
|
||||
@personal_api_key_id,
|
||||
@membership_id,
|
||||
@role,
|
||||
@organization_id,
|
||||
@created_at,
|
||||
@updated_at
|
||||
)
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"id": a.ID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"personal_api_key_id": a.PersonalAPIKeyID,
|
||||
"membership_id": a.MembershipID,
|
||||
"role": a.Role,
|
||||
"organization_id": a.OrganizationID,
|
||||
"created_at": a.CreatedAt,
|
||||
"updated_at": a.UpdatedAt,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert personal api key membership: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PersonalAPIKeyMemberships) LoadByPersonalAPIKeyID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
personalAPIKeyID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
akm.id,
|
||||
akm.personal_api_key_id,
|
||||
akm.membership_id,
|
||||
akm.role,
|
||||
akm.created_at,
|
||||
akm.updated_at,
|
||||
m.organization_id,
|
||||
o.name as organization_name
|
||||
FROM
|
||||
iam_personal_api_key_memberships akm
|
||||
JOIN
|
||||
iam_memberships m ON akm.membership_id = m.id
|
||||
JOIN
|
||||
organizations o ON m.organization_id = o.id
|
||||
WHERE
|
||||
akm.personal_api_key_id = @personal_api_key_id
|
||||
AND m.%s
|
||||
ORDER BY akm.created_at DESC
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"personal_api_key_id": personalAPIKeyID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query personal api key memberships: %w", err)
|
||||
}
|
||||
|
||||
memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PersonalAPIKeyMembership])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect personal api key memberships: %w", err)
|
||||
}
|
||||
|
||||
*a = memberships
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadRoleByAPIKeyAndEntityID loads an API key's role by querying any entity to extract its organization_id
|
||||
func (a *PersonalAPIKeyMembership) LoadRoleByAPIKeyAndEntityID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
apiKeyID gid.GID,
|
||||
entityID gid.GID,
|
||||
) error {
|
||||
entityType := entityID.EntityType()
|
||||
|
||||
// For organization, the entity ID is the organization ID
|
||||
if entityType == OrganizationEntityType {
|
||||
return a.LoadByAPIKeyIDAndOrganizationID(ctx, conn, scope, apiKeyID, entityID)
|
||||
}
|
||||
|
||||
tableName, ok := EntityTable(entityType)
|
||||
if !ok {
|
||||
return fmt.Errorf("unsupported entity type for API key role lookup: %d", entityType)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
akm.id,
|
||||
akm.personal_api_key_id,
|
||||
akm.membership_id,
|
||||
akm.role,
|
||||
akm.created_at,
|
||||
akm.updated_at
|
||||
FROM
|
||||
iam_personal_api_key_memberships akm
|
||||
INNER JOIN iam_memberships m ON m.id = akm.membership_id
|
||||
INNER JOIN %s e ON e.id = @entity_id
|
||||
WHERE
|
||||
%s
|
||||
AND akm.personal_api_key_id = @api_key_id
|
||||
AND m.organization_id = e.organization_id
|
||||
LIMIT 1;
|
||||
`, tableName, scope.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{
|
||||
"api_key_id": apiKeyID,
|
||||
"entity_id": entityID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, query, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query API key membership by entity: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
if !rows.Next() {
|
||||
return fmt.Errorf("API key membership not found for key %s and entity %s", apiKeyID, entityID)
|
||||
}
|
||||
|
||||
var membership PersonalAPIKeyMembership
|
||||
err = rows.Scan(
|
||||
&membership.ID,
|
||||
&membership.PersonalAPIKeyID,
|
||||
&membership.MembershipID,
|
||||
&membership.Role,
|
||||
&membership.CreatedAt,
|
||||
&membership.UpdatedAt,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot scan API key membership: %w", err)
|
||||
}
|
||||
|
||||
*a = membership
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PersonalAPIKeyMembership) LoadByAPIKeyIDAndOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
apiKeyID gid.GID,
|
||||
organizationID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
akm.id,
|
||||
akm.personal_api_key_id,
|
||||
akm.membership_id,
|
||||
akm.role,
|
||||
akm.created_at,
|
||||
akm.updated_at,
|
||||
m.organization_id,
|
||||
o.name as organization_name
|
||||
FROM
|
||||
iam_personal_api_key_memberships akm
|
||||
JOIN
|
||||
iam_memberships m ON akm.membership_id = m.id
|
||||
JOIN
|
||||
organizations o ON m.organization_id = o.id
|
||||
WHERE
|
||||
akm.personal_api_key_id = @api_key_id
|
||||
AND m.organization_id = @organization_id
|
||||
AND m.%s
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"api_key_id": apiKeyID,
|
||||
"organization_id": organizationID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query personal api key membership: %w", err)
|
||||
}
|
||||
|
||||
membership, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[PersonalAPIKeyMembership])
|
||||
if err != nil {
|
||||
if err == pgx.ErrNoRows {
|
||||
return fmt.Errorf("API key does not have access to organization")
|
||||
}
|
||||
return fmt.Errorf("cannot collect personal api key membership: %w", err)
|
||||
}
|
||||
|
||||
*a = membership
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PersonalAPIKeyMembership) Delete(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
DELETE FROM
|
||||
iam_personal_api_key_memberships
|
||||
WHERE
|
||||
id = @id
|
||||
AND %s
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"id": a.ID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete personal api key membership: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PersonalAPIKeyMemberships) LoadByMembershipID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
membershipID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
akm.id,
|
||||
akm.personal_api_key_id,
|
||||
akm.membership_id,
|
||||
akm.role,
|
||||
akm.created_at,
|
||||
akm.updated_at,
|
||||
m.organization_id,
|
||||
o.name as organization_name
|
||||
FROM
|
||||
iam_personal_api_key_memberships akm
|
||||
JOIN
|
||||
iam_memberships m ON akm.membership_id = m.id
|
||||
JOIN
|
||||
organizations o ON m.organization_id = o.id
|
||||
WHERE
|
||||
akm.membership_id = @membership_id
|
||||
AND m.%s
|
||||
ORDER BY akm.created_at DESC
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"membership_id": membershipID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query personal api key memberships by membership id: %w", err)
|
||||
}
|
||||
|
||||
memberships, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PersonalAPIKeyMembership])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect personal api key memberships: %w", err)
|
||||
}
|
||||
|
||||
*a = memberships
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteAllPersonalAPIKeyMembershipsByPersonalAPIKeyID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
personalAPIKeyID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
DELETE FROM
|
||||
iam_personal_api_key_memberships
|
||||
WHERE
|
||||
personal_api_key_id = @personal_api_key_id
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"personal_api_key_id": personalAPIKeyID,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete personal api key memberships: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type APIRole string
|
||||
|
||||
const (
|
||||
APIRoleFull APIRole = "FULL"
|
||||
)
|
||||
|
||||
func (r APIRole) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func (r *APIRole) Scan(value any) error {
|
||||
var s string
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
s = v
|
||||
case []byte:
|
||||
s = string(v)
|
||||
default:
|
||||
return fmt.Errorf("unsupported type for APIRole: %T", value)
|
||||
}
|
||||
|
||||
switch s {
|
||||
case "FULL":
|
||||
*r = APIRoleFull
|
||||
default:
|
||||
return fmt.Errorf("invalid APIRole value: %q", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r APIRole) Value() (driver.Value, error) {
|
||||
return r.String(), nil
|
||||
}
|
||||
@@ -65,7 +65,7 @@ const (
|
||||
TrustCenterFileEntityType uint16 = 41
|
||||
SAMLConfigurationEntityType uint16 = 42
|
||||
PersonalAPIKeyEntityType uint16 = 43
|
||||
PersonalAPIKeyMembershipEntityType uint16 = 44
|
||||
_ uint16 = 44 // PersonalAPIKeyMembershipEntityType - removed
|
||||
MeetingEntityType uint16 = 45
|
||||
DataProtectionImpactAssessmentEntityType uint16 = 46
|
||||
TransferImpactAssessmentEntityType uint16 = 47
|
||||
@@ -257,10 +257,6 @@ var entityRegistry = map[uint16]EntityInfo{
|
||||
Model: "PersonalAPIKey",
|
||||
Table: "iam_personal_api_keys",
|
||||
},
|
||||
PersonalAPIKeyMembershipEntityType: {
|
||||
Model: "PersonalAPIKeyMembership",
|
||||
Table: "iam_personal_api_key_memberships",
|
||||
},
|
||||
MeetingEntityType: {
|
||||
Model: "Meeting",
|
||||
Table: "meetings",
|
||||
|
||||
3
pkg/coredata/migrations/20251222T150000Z.sql
Normal file
3
pkg/coredata/migrations/20251222T150000Z.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
DELETE FROM iam_personal_api_key_memberships;
|
||||
DROP TABLE iam_personal_api_key_memberships;
|
||||
|
||||
@@ -12,9 +12,20 @@
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// LEGACY: This is the legacy access management service that is used to authorize actions on entities.
|
||||
// It is deprecated and will be removed in the future.
|
||||
// Use the Authorizer instead.
|
||||
// LEGACY ACCESS MANAGEMENT SERVICE - DEPRECATED
|
||||
//
|
||||
// This service implements the legacy authorization model that uses the Permissions
|
||||
// map from permissions.go to check if a principal can perform an action.
|
||||
//
|
||||
// It is being replaced by Authorizer which uses a policy-based evaluation system.
|
||||
// During migration, this service is still used for:
|
||||
// - API key authorization (intersection semantics between user and API key roles)
|
||||
// - Fallback for any unmapped legacy actions
|
||||
//
|
||||
// Once all actions are migrated and API key authorization is implemented in the
|
||||
// new system, this service will be removed.
|
||||
//
|
||||
// Deprecated: Use Authorizer.Authorize() instead for new code.
|
||||
package iam
|
||||
|
||||
import (
|
||||
@@ -148,18 +159,21 @@ func (s *AccessManagementService) loadAPIKeyRoleForEntity(
|
||||
apiKeyID gid.GID,
|
||||
entityID gid.GID,
|
||||
) (Role, error) {
|
||||
var akm coredata.PersonalAPIKeyMembership
|
||||
if err := akm.LoadRoleByAPIKeyAndEntityID(ctx, conn, scope, apiKeyID, entityID); err != nil {
|
||||
return "", err
|
||||
// Load the API key to get the identity
|
||||
apiKey := &coredata.PersonalAPIKey{}
|
||||
if err := apiKey.LoadByID(ctx, conn, apiKeyID); err != nil {
|
||||
return "", fmt.Errorf("cannot load api key: %w", err)
|
||||
}
|
||||
|
||||
// Strict API key semantics: FULL only matches RoleFull explicitly.
|
||||
switch akm.Role {
|
||||
case coredata.APIRoleFull:
|
||||
return RoleFull, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported api key role: %s", akm.Role)
|
||||
// Use the Identity's membership role for authorization
|
||||
var m coredata.Membership
|
||||
if err := m.LoadRoleByIdentityAndEntityID(ctx, conn, scope, apiKey.IdentityID, entityID); err != nil {
|
||||
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||
return "", err
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
return Role(m.Role.String()), nil
|
||||
}
|
||||
|
||||
// requiredRoleNamesContain is a temporary evaluator for the current in-code permissions registry
|
||||
|
||||
@@ -17,7 +17,9 @@ package iam
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"go.gearno.de/kit/pg"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
@@ -32,24 +34,28 @@ type Authorizer struct {
|
||||
policySet *PolicySet
|
||||
}
|
||||
|
||||
// NewAuthorizer creates a new authorizer with the given PolicySet.
|
||||
// The PolicySet should contain all role-based and self-management policies
|
||||
// from all services that need authorization.
|
||||
// NewAuthorizer creates a new authorizer.
|
||||
// Services register their policies by calling RegisterPolicySet.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// policySet := iam.IAMPolicySet().
|
||||
// Merge(documents.DocumentPolicySet()).
|
||||
// Merge(risks.RiskPolicySet())
|
||||
// authorizer := iam.NewAuthorizer(pgClient, policySet)
|
||||
func NewAuthorizer(pgClient *pg.Client, policySet *PolicySet) *Authorizer {
|
||||
// authorizer := iam.NewAuthorizer(pgClient)
|
||||
// authorizer.RegisterPolicySet(iam.IAMPolicySet())
|
||||
// authorizer.RegisterPolicySet(probo.ProboPolicySet())
|
||||
func NewAuthorizer(pgClient *pg.Client) *Authorizer {
|
||||
return &Authorizer{
|
||||
pg: pgClient,
|
||||
evaluator: policy.NewEvaluator(),
|
||||
policySet: policySet,
|
||||
policySet: NewPolicySet(),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterPolicySet merges policies from another service into this authorizer.
|
||||
// Services call this method to register their policies.
|
||||
func (a *Authorizer) RegisterPolicySet(policySet *PolicySet) {
|
||||
a.policySet.Merge(policySet)
|
||||
}
|
||||
|
||||
// AuthorizeParams contains all parameters for an authorization check.
|
||||
type AuthorizeParams struct {
|
||||
// Principal is the user requesting access.
|
||||
@@ -66,6 +72,53 @@ type AuthorizeParams struct {
|
||||
ResourceAttributes map[string]string
|
||||
}
|
||||
|
||||
func (a *Authorizer) GetPermissionsForMembership(ctx context.Context, identityID gid.GID, membershipID gid.GID) (map[string]map[Action]bool, error) {
|
||||
var (
|
||||
scope = coredata.NewScopeFromObjectID(membershipID)
|
||||
membership = &coredata.Membership{}
|
||||
)
|
||||
|
||||
err := a.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := membership.LoadByID(ctx, conn, scope, membershipID)
|
||||
if err != nil {
|
||||
if err == coredata.ErrResourceNotFound {
|
||||
return NewMembershipNotFoundError(membershipID)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot load membership: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
permissions := make(map[string]map[Action]bool)
|
||||
|
||||
for entityType, actions := range Permissions {
|
||||
entityTypeName, ok := coredata.EntityModel(entityType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if permissions[entityTypeName] == nil {
|
||||
permissions[entityTypeName] = make(map[Action]bool)
|
||||
}
|
||||
|
||||
for action, allowedRoles := range actions {
|
||||
if slices.Contains(allowedRoles, Role(membership.Role)) {
|
||||
permissions[entityTypeName][action] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// Authorize checks if the principal can perform the action on the resource.
|
||||
// It combines self-management policies with role-based policies.
|
||||
func (a *Authorizer) Authorize(ctx context.Context, params AuthorizeParams) error {
|
||||
|
||||
@@ -32,12 +32,13 @@ const (
|
||||
ActionIAMOrganizationListInvitations = "iam:organization:list-invitations"
|
||||
|
||||
// Identity actions
|
||||
ActionIAMIdentityGet = "iam:identity:get"
|
||||
ActionIAMIdentityUpdate = "iam:identity:update"
|
||||
ActionIAMIdentityDelete = "iam:identity:delete"
|
||||
ActionIAMIdentityListMemberships = "iam:identity:list-memberships"
|
||||
ActionIAMIdentityListInvitations = "iam:identity:list-invitations"
|
||||
ActionIAMIdentityListSessions = "iam:identity:list-sessions"
|
||||
ActionIAMIdentityGet = "iam:identity:get"
|
||||
ActionIAMIdentityUpdate = "iam:identity:update"
|
||||
ActionIAMIdentityDelete = "iam:identity:delete"
|
||||
ActionIAMIdentityListMemberships = "iam:identity:list-memberships"
|
||||
ActionIAMIdentityListInvitations = "iam:identity:list-invitations"
|
||||
ActionIAMIdentityListSessions = "iam:identity:list-sessions"
|
||||
ActionIAMIdentityListPersonalAPIKeys = "iam:identity:list-personal-api-keys"
|
||||
|
||||
// Session actions
|
||||
ActionIAMSessionGet = "iam:session:get"
|
||||
@@ -52,4 +53,17 @@ const (
|
||||
// Membership actions
|
||||
ActionIAMMembershipGet = "iam:membership:get"
|
||||
ActionIAMMembershipUpdate = "iam:membership:update"
|
||||
|
||||
// Personal API Key actions
|
||||
ActionIAMPersonalAPIKeyCreate = "iam:personal-api-key:create"
|
||||
ActionIAMPersonalAPIKeyGet = "iam:personal-api-key:get"
|
||||
ActionIAMPersonalAPIKeyUpdate = "iam:personal-api-key:update"
|
||||
ActionIAMPersonalAPIKeyDelete = "iam:personal-api-key:delete"
|
||||
|
||||
// SAML Configuration actions
|
||||
ActionIAMSAMLConfigurationCreate = "iam:saml-configuration:create"
|
||||
ActionIAMSAMLConfigurationGet = "iam:saml-configuration:get"
|
||||
ActionIAMSAMLConfigurationUpdate = "iam:saml-configuration:update"
|
||||
ActionIAMSAMLConfigurationDelete = "iam:saml-configuration:delete"
|
||||
ActionIAMSAMLConfigurationList = "iam:saml-configuration:list"
|
||||
)
|
||||
|
||||
@@ -34,14 +34,15 @@ var IAMSelfManageIdentityPolicy = policy.NewPolicy(
|
||||
).WithSID("manage-own-identity").
|
||||
When(policy.Equals("principal.id", "resource.id")),
|
||||
|
||||
// Users can list their own memberships and invitations
|
||||
// Users can list their own memberships, invitations, sessions, and API keys
|
||||
policy.Allow(
|
||||
ActionIAMIdentityListMemberships,
|
||||
ActionIAMIdentityListInvitations,
|
||||
ActionIAMIdentityListSessions,
|
||||
ActionIAMIdentityListPersonalAPIKeys,
|
||||
).WithSID("list-own-associations").
|
||||
When(policy.Equals("principal.id", "resource.id")),
|
||||
).WithDescription("Allows users to manage their own identity, sessions, and view their memberships")
|
||||
).WithDescription("Allows users to manage their own identity, sessions, API keys, and view their memberships")
|
||||
|
||||
// IAMSelfManageSessionPolicy allows users to manage their own sessions.
|
||||
var IAMSelfManageSessionPolicy = policy.NewPolicy(
|
||||
@@ -79,6 +80,20 @@ var IAMSelfManageMembershipPolicy = policy.NewPolicy(
|
||||
When(policy.Equals("principal.id", "resource.user_id")),
|
||||
).WithDescription("Allows users to view their organization memberships")
|
||||
|
||||
// IAMSelfManagePersonalAPIKeyPolicy allows users to manage their own API keys.
|
||||
var IAMSelfManagePersonalAPIKeyPolicy = policy.NewPolicy(
|
||||
"iam:self-manage-personal-api-key",
|
||||
"Self-Manage Personal API Keys",
|
||||
// Users can create, view, update, and delete their own API keys
|
||||
policy.Allow(
|
||||
ActionIAMPersonalAPIKeyCreate,
|
||||
ActionIAMPersonalAPIKeyGet,
|
||||
ActionIAMPersonalAPIKeyUpdate,
|
||||
ActionIAMPersonalAPIKeyDelete,
|
||||
).WithSID("manage-own-api-keys").
|
||||
When(policy.Equals("principal.id", "resource.user_id")),
|
||||
).WithDescription("Allows users to manage their own personal API keys")
|
||||
|
||||
// IAMOwnerPolicy defines permissions for organization owners.
|
||||
var IAMOwnerPolicy = policy.NewPolicy(
|
||||
"iam:owner",
|
||||
@@ -92,6 +107,8 @@ var IAMOwnerPolicy = policy.NewPolicy(
|
||||
ActionIAMInvitationGet,
|
||||
ActionIAMInvitationDelete,
|
||||
).WithSID("manage-invitations"),
|
||||
// Full access to SAML configuration management
|
||||
policy.Allow("iam:saml-configuration:*").WithSID("full-saml-access"),
|
||||
).WithDescription("Full IAM access for organization owners")
|
||||
|
||||
// IAMAdminPolicy defines permissions for organization admins.
|
||||
@@ -116,11 +133,22 @@ var IAMAdminPolicy = policy.NewPolicy(
|
||||
ActionIAMInvitationGet,
|
||||
ActionIAMInvitationDelete,
|
||||
).WithSID("invitation-admin-access"),
|
||||
// Can view SAML configurations
|
||||
policy.Allow(
|
||||
ActionIAMSAMLConfigurationGet,
|
||||
ActionIAMSAMLConfigurationList,
|
||||
).WithSID("saml-viewer-access"),
|
||||
// Cannot delete organization
|
||||
policy.Deny(ActionIAMOrganizationDelete).WithSID("deny-org-delete"),
|
||||
// Cannot remove members (only owner can)
|
||||
policy.Deny(ActionIAMOrganizationRemoveMember).WithSID("deny-remove-member"),
|
||||
).WithDescription("IAM admin access - can manage members but cannot delete organization")
|
||||
// Cannot manage SAML configurations (only owner can)
|
||||
policy.Deny(
|
||||
ActionIAMSAMLConfigurationCreate,
|
||||
ActionIAMSAMLConfigurationUpdate,
|
||||
ActionIAMSAMLConfigurationDelete,
|
||||
).WithSID("deny-saml-management"),
|
||||
).WithDescription("IAM admin access - can manage members but cannot delete organization or manage SAML")
|
||||
|
||||
// IAMViewerPolicy defines permissions for organization viewers.
|
||||
var IAMViewerPolicy = policy.NewPolicy(
|
||||
|
||||
@@ -12,9 +12,25 @@
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// LEGACY: This is the legacy access management service that is used to authorize actions on entities.
|
||||
// It is deprecated and will be removed in the future.
|
||||
// Use the Authorizer instead.
|
||||
// LEGACY PERMISSION SYSTEM - DEPRECATED
|
||||
//
|
||||
// This file contains the legacy permission system that maps entity types and actions
|
||||
// to allowed roles. It is being replaced by a policy-based authorization system.
|
||||
//
|
||||
// Migration path:
|
||||
// - New actions are defined in core_actions.go with namespaced format (e.g., "core:asset:get")
|
||||
// - New policies are defined in core_policies.go and iam_policies.go
|
||||
// - The action_mapping.go file provides a bridge between legacy and new actions
|
||||
// - The Authorizer in authorizer.go evaluates policies using the new system
|
||||
//
|
||||
// During the migration period, the MustBeAuthorized function in resolvers will:
|
||||
// 1. Attempt to map legacy actions to new namespaced actions
|
||||
// 2. Use the new Authorizer if mapping succeeds
|
||||
// 3. Fall back to LegacyAccessManagementService for unmapped actions or API key requests
|
||||
//
|
||||
// Once migration is complete, this file and access_management_service.go will be removed.
|
||||
//
|
||||
// Deprecated: Use Authorizer.Authorize() with new namespaced actions instead.
|
||||
package iam
|
||||
|
||||
import (
|
||||
|
||||
@@ -67,5 +67,6 @@ func IAMPolicySet() *PolicySet {
|
||||
IAMSelfManageSessionPolicy,
|
||||
IAMSelfManageInvitationPolicy,
|
||||
IAMSelfManageMembershipPolicy,
|
||||
IAMSelfManagePersonalAPIKeyPolicy,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ type (
|
||||
Certificate *x509.Certificate
|
||||
PrivateKey *rsa.PrivateKey
|
||||
Logger *log.Logger
|
||||
PolicySet *PolicySet
|
||||
TracerProvider trace.TracerProvider
|
||||
DomainVerificationInterval time.Duration
|
||||
DomainVerificationResolverAddr string
|
||||
@@ -113,15 +112,8 @@ func NewService(
|
||||
svc.APIKeyService = NewAPIKeyService(svc)
|
||||
svc.LegacyAccessManagementService = NewAccessManagementService(svc)
|
||||
|
||||
// Use provided PolicySet or default to IAM-only policies
|
||||
policySet := NewPolicySet()
|
||||
if cfg.PolicySet != nil {
|
||||
policySet = cfg.PolicySet
|
||||
}
|
||||
|
||||
policySet.Merge(IAMPolicySet())
|
||||
|
||||
svc.Authorizer = NewAuthorizer(pgClient, policySet)
|
||||
svc.Authorizer = NewAuthorizer(pgClient)
|
||||
svc.Authorizer.RegisterPolicySet(IAMPolicySet())
|
||||
|
||||
samlService, err := saml.NewService(svc.pg, svc.encryptionKey, svc.baseURL, svc.certificate, svc.privateKey, cfg.Logger)
|
||||
if err != nil {
|
||||
|
||||
394
pkg/probo/action_mapping.go
Normal file
394
pkg/probo/action_mapping.go
Normal file
@@ -0,0 +1,394 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package probo
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
)
|
||||
|
||||
// legacyActionMapping maps entity types to a mapping of legacy actions to new namespaced actions.
|
||||
// This is used during the migration period to translate old action strings to new policy actions.
|
||||
var legacyActionMapping = map[uint16]map[iam.Action]iam.Action{
|
||||
coredata.OrganizationEntityType: {
|
||||
iam.ActionGet: ActionOrganizationGet,
|
||||
iam.ActionGetLogoUrl: ActionOrganizationGetLogoUrl,
|
||||
iam.ActionGetHorizontalLogoUrl: ActionOrganizationGetHorizontalLogoUrl,
|
||||
iam.ActionListDocuments: ActionDocumentList,
|
||||
iam.ActionListSignableDocuments: ActionDocumentList,
|
||||
iam.ActionPeoples: ActionPeopleList,
|
||||
iam.ActionTotalCount: ActionOrganizationGet,
|
||||
iam.ActionListFrameworks: ActionFrameworkList,
|
||||
iam.ActionListControls: ActionControlList,
|
||||
iam.ActionListVendors: ActionVendorList,
|
||||
iam.ActionListPeople: ActionPeopleList,
|
||||
iam.ActionListMeasures: ActionMeasureList,
|
||||
iam.ActionListRisks: ActionRiskList,
|
||||
iam.ActionListAssets: ActionAssetList,
|
||||
iam.ActionListData: ActionDatumList,
|
||||
iam.ActionListAudits: ActionAuditList,
|
||||
iam.ActionListNonconformities: ActionNonconformityList,
|
||||
iam.ActionListObligations: ActionObligationList,
|
||||
iam.ActionListContinualImprovements: ActionContinualImprovementList,
|
||||
iam.ActionListProcessingActivities: ActionProcessingActivityList,
|
||||
iam.ActionListSnapshots: ActionSnapshotList,
|
||||
iam.ActionAcceptInvitation: iam.ActionIAMInvitationAccept,
|
||||
iam.ActionListTrustCenterFiles: ActionTrustCenterFileList,
|
||||
iam.ActionGetTrustCenter: ActionTrustCenterGet,
|
||||
iam.ActionMemberships: iam.ActionIAMOrganizationListMembers,
|
||||
iam.ActionListMembers: iam.ActionIAMOrganizationListMembers,
|
||||
iam.ActionListInvitations: iam.ActionIAMOrganizationListInvitations,
|
||||
iam.ActionListSlackConnections: ActionSlackConnectionList,
|
||||
iam.ActionGetCustomDomain: ActionCustomDomainGet,
|
||||
iam.ActionListMeetings: ActionMeetingList,
|
||||
iam.ActionListTasks: ActionTaskList,
|
||||
iam.ActionUpdateOrganization: ActionOrganizationGet,
|
||||
iam.ActionDeleteOrganizationHorizontalLogo: ActionOrganizationGet,
|
||||
iam.ActionCreateTrustCenter: ActionTrustCenterGet,
|
||||
iam.ActionInviteUser: iam.ActionIAMOrganizationInviteMember,
|
||||
iam.ActionUpdateMembership: iam.ActionIAMMembershipUpdate,
|
||||
iam.ActionCreatePeople: ActionPeopleCreate,
|
||||
iam.ActionCreateVendor: ActionVendorCreate,
|
||||
iam.ActionCreateFramework: ActionFrameworkCreate,
|
||||
iam.ActionImportFramework: ActionFrameworkImport,
|
||||
iam.ActionCreateControl: ActionControlCreate,
|
||||
iam.ActionCreateMeasure: ActionMeasureCreate,
|
||||
iam.ActionImportMeasure: ActionMeasureImport,
|
||||
iam.ActionCreateMeeting: ActionMeetingCreate,
|
||||
iam.ActionCreateTask: ActionTaskCreate,
|
||||
iam.ActionCreateRisk: ActionRiskCreate,
|
||||
iam.ActionCreateDocument: ActionDocumentCreate,
|
||||
iam.ActionCreateAsset: ActionAssetCreate,
|
||||
iam.ActionCreateDatum: ActionDatumCreate,
|
||||
iam.ActionCreateAudit: ActionAuditCreate,
|
||||
iam.ActionCreateNonconformity: ActionNonconformityCreate,
|
||||
iam.ActionCreateObligation: ActionObligationCreate,
|
||||
iam.ActionCreateContinualImprovement: ActionContinualImprovementCreate,
|
||||
iam.ActionCreateProcessingActivity: ActionProcessingActivityCreate,
|
||||
iam.ActionCreateSnapshot: ActionSnapshotCreate,
|
||||
iam.ActionCreateTrustCenterFile: ActionTrustCenterFileCreate,
|
||||
iam.ActionSendSigningNotifications: ActionDocumentSendSigningNotifications,
|
||||
iam.ActionRemoveMember: iam.ActionIAMOrganizationRemoveMember,
|
||||
iam.ActionCreateCustomDomain: ActionCustomDomainCreate,
|
||||
iam.ActionDeleteCustomDomain: ActionCustomDomainDelete,
|
||||
},
|
||||
coredata.TrustCenterEntityType: {
|
||||
iam.ActionGet: ActionTrustCenterGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionUpdateTrustCenter: ActionTrustCenterUpdate,
|
||||
iam.ActionUploadTrustCenterNDA: ActionTrustCenterNonDisclosureAgreementUpload,
|
||||
iam.ActionDeleteTrustCenterNDA: ActionTrustCenterNonDisclosureAgreementDelete,
|
||||
iam.ActionCreateTrustCenterAccess: ActionTrustCenterAccessCreate,
|
||||
iam.ActionCreateTrustCenterReference: ActionTrustCenterReferenceCreate,
|
||||
},
|
||||
coredata.TrustCenterAccessEntityType: {
|
||||
iam.ActionUpdateTrustCenterAccess: ActionTrustCenterAccessUpdate,
|
||||
iam.ActionDeleteTrustCenterAccess: ActionTrustCenterAccessDelete,
|
||||
},
|
||||
coredata.TrustCenterReferenceEntityType: {
|
||||
iam.ActionUpdateTrustCenterReference: ActionTrustCenterReferenceUpdate,
|
||||
iam.ActionDeleteTrustCenterReference: ActionTrustCenterReferenceDelete,
|
||||
},
|
||||
coredata.TrustCenterFileEntityType: {
|
||||
iam.ActionUpdateTrustCenterFile: ActionTrustCenterFileUpdate,
|
||||
iam.ActionDeleteTrustCenterFile: ActionTrustCenterFileDelete,
|
||||
},
|
||||
coredata.IdentityEntityType: {
|
||||
iam.ActionGet: iam.ActionIAMIdentityGet,
|
||||
},
|
||||
coredata.MembershipEntityType: {
|
||||
iam.ActionGet: iam.ActionIAMMembershipGet,
|
||||
iam.ActionGetAuthMethod: iam.ActionIAMMembershipGet,
|
||||
},
|
||||
coredata.InvitationEntityType: {
|
||||
iam.ActionGet: iam.ActionIAMInvitationGet,
|
||||
iam.ActionGetOrganization: iam.ActionIAMInvitationGet,
|
||||
iam.ActionDeleteInvitation: iam.ActionIAMInvitationDelete,
|
||||
},
|
||||
coredata.PeopleEntityType: {
|
||||
iam.ActionGet: ActionPeopleGet,
|
||||
iam.ActionUpdatePeople: ActionPeopleUpdate,
|
||||
iam.ActionDeletePeople: ActionPeopleDelete,
|
||||
},
|
||||
coredata.VendorEntityType: {
|
||||
iam.ActionGet: ActionVendorList,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionGetBusinessOwner: ActionPeopleGet,
|
||||
iam.ActionGetSecurityOwner: ActionPeopleGet,
|
||||
iam.ActionUpdateVendor: ActionVendorUpdate,
|
||||
iam.ActionDeleteVendor: ActionVendorDelete,
|
||||
iam.ActionCreateVendorContact: ActionVendorContactCreate,
|
||||
iam.ActionCreateVendorService: ActionVendorServiceCreate,
|
||||
iam.ActionUploadVendorComplianceReport: ActionVendorComplianceReportUpload,
|
||||
iam.ActionUploadVendorBusinessAssociateAgreement: ActionVendorBusinessAssociateAgreementUpload,
|
||||
iam.ActionDeleteVendorBusinessAssociateAgreement: ActionVendorBusinessAssociateAgreementDelete,
|
||||
iam.ActionUploadVendorDataPrivacyAgreement: ActionVendorDataPrivacyAgreementUpload,
|
||||
iam.ActionCreateVendorRiskAssessment: ActionVendorRiskAssessmentCreate,
|
||||
iam.ActionAssessVendor: ActionVendorAssess,
|
||||
},
|
||||
coredata.VendorComplianceReportEntityType: {
|
||||
iam.ActionGet: ActionVendorList,
|
||||
iam.ActionGetVendor: ActionVendorList,
|
||||
iam.ActionDeleteVendorComplianceReport: ActionVendorComplianceReportDelete,
|
||||
},
|
||||
coredata.VendorBusinessAssociateAgreementEntityType: {
|
||||
iam.ActionGet: ActionVendorList,
|
||||
iam.ActionGetVendor: ActionVendorList,
|
||||
iam.ActionGetFileUrl: ActionFileDownloadUrl,
|
||||
iam.ActionUpdateVendorBusinessAssociateAgreement: ActionVendorBusinessAssociateAgreementUpdate,
|
||||
iam.ActionDeleteVendorBusinessAssociateAgreement: ActionVendorBusinessAssociateAgreementDelete,
|
||||
},
|
||||
coredata.VendorContactEntityType: {
|
||||
iam.ActionGet: ActionVendorList,
|
||||
iam.ActionGetVendor: ActionVendorList,
|
||||
iam.ActionUpdateVendorContact: ActionVendorContactUpdate,
|
||||
iam.ActionDeleteVendorContact: ActionVendorContactDelete,
|
||||
},
|
||||
coredata.VendorServiceEntityType: {
|
||||
iam.ActionGet: ActionVendorList,
|
||||
iam.ActionGetVendor: ActionVendorList,
|
||||
iam.ActionUpdateVendorService: ActionVendorServiceUpdate,
|
||||
iam.ActionDeleteVendorService: ActionVendorServiceDelete,
|
||||
},
|
||||
coredata.VendorDataPrivacyAgreementEntityType: {
|
||||
iam.ActionGet: ActionVendorList,
|
||||
iam.ActionGetVendor: ActionVendorList,
|
||||
iam.ActionGetFileUrl: ActionFileDownloadUrl,
|
||||
iam.ActionUpdateVendorDataPrivacyAgreement: ActionVendorDataPrivacyAgreementUpdate,
|
||||
iam.ActionDeleteVendorDataPrivacyAgreement: ActionVendorDataPrivacyAgreementDelete,
|
||||
},
|
||||
coredata.VendorRiskAssessmentEntityType: {
|
||||
iam.ActionGet: ActionVendorList,
|
||||
},
|
||||
coredata.FrameworkEntityType: {
|
||||
iam.ActionGet: ActionFrameworkGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionListControls: ActionControlList,
|
||||
iam.ActionCreateControl: ActionControlCreate,
|
||||
iam.ActionUpdateFramework: ActionFrameworkUpdate,
|
||||
iam.ActionDeleteFramework: ActionFrameworkDelete,
|
||||
iam.ActionGenerateFrameworkStateOfApplicability: ActionFrameworkStateOfApplicabilityGenerate,
|
||||
iam.ActionExportFramework: ActionFrameworkExport,
|
||||
},
|
||||
coredata.ControlEntityType: {
|
||||
iam.ActionGet: ActionControlList,
|
||||
iam.ActionGetFramework: ActionFrameworkGet,
|
||||
iam.ActionListMeasures: ActionMeasureList,
|
||||
iam.ActionListDocuments: ActionDocumentList,
|
||||
iam.ActionListAudits: ActionAuditList,
|
||||
iam.ActionListSnapshots: ActionSnapshotList,
|
||||
iam.ActionUpdateControl: ActionControlUpdate,
|
||||
iam.ActionDeleteControl: ActionControlDelete,
|
||||
iam.ActionCreateControlMeasureMapping: ActionControlMeasureMappingCreate,
|
||||
iam.ActionCreateControlDocumentMapping: ActionControlDocumentMappingCreate,
|
||||
iam.ActionDeleteControlMeasureMapping: ActionControlMeasureMappingDelete,
|
||||
iam.ActionDeleteControlDocumentMapping: ActionControlDocumentMappingDelete,
|
||||
iam.ActionCreateControlAuditMapping: ActionControlAuditMappingCreate,
|
||||
iam.ActionDeleteControlAuditMapping: ActionControlAuditMappingDelete,
|
||||
iam.ActionCreateControlSnapshotMapping: ActionControlSnapshotMappingCreate,
|
||||
iam.ActionDeleteControlSnapshotMapping: ActionControlSnapshotMappingDelete,
|
||||
},
|
||||
coredata.MeasureEntityType: {
|
||||
iam.ActionGet: ActionMeasureGet,
|
||||
iam.ActionListTasks: ActionTaskList,
|
||||
iam.ActionListEvidences: ActionEvidenceList,
|
||||
iam.ActionListRisks: ActionRiskList,
|
||||
iam.ActionListControls: ActionControlList,
|
||||
iam.ActionTotalCount: ActionMeasureList,
|
||||
iam.ActionUpdateMeasure: ActionMeasureUpdate,
|
||||
iam.ActionDeleteMeasure: ActionMeasureDelete,
|
||||
iam.ActionUploadMeasureEvidence: ActionMeasureEvidenceUpload,
|
||||
},
|
||||
coredata.TaskEntityType: {
|
||||
iam.ActionGet: ActionTaskGet,
|
||||
iam.ActionGetAssignedTo: ActionPeopleGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionGetMeasure: ActionMeasureGet,
|
||||
iam.ActionListEvidences: ActionEvidenceList,
|
||||
iam.ActionUpdateTask: ActionTaskUpdate,
|
||||
iam.ActionDeleteTask: ActionTaskDelete,
|
||||
iam.ActionAssignTask: ActionTaskAssign,
|
||||
iam.ActionUnassignTask: ActionTaskUnassign,
|
||||
},
|
||||
coredata.EvidenceEntityType: {
|
||||
iam.ActionGet: ActionEvidenceList,
|
||||
iam.ActionGetFile: ActionFileGet,
|
||||
iam.ActionGetTask: ActionTaskGet,
|
||||
iam.ActionGetMeasure: ActionMeasureList,
|
||||
iam.ActionDeleteEvidence: ActionEvidenceDelete,
|
||||
},
|
||||
coredata.DocumentEntityType: {
|
||||
iam.ActionGet: ActionDocumentGet,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionGetSigned: ActionDocumentList,
|
||||
iam.ActionGetSignableDocument: ActionDocumentList,
|
||||
iam.ActionListSignableDocumentVersion: ActionDocumentList,
|
||||
iam.ActionBulkExportDocuments: ActionDocumentList,
|
||||
iam.ActionTotalCount: ActionDocumentList,
|
||||
iam.ActionListControls: ActionControlList,
|
||||
iam.ActionListVersions: ActionDocumentVersionList,
|
||||
iam.ActionUpdateDocument: ActionDocumentUpdate,
|
||||
iam.ActionDeleteDocument: ActionDocumentDelete,
|
||||
iam.ActionBulkDeleteDocuments: ActionDocumentDelete,
|
||||
iam.ActionPublishDocumentVersion: ActionDocumentVersionPublish,
|
||||
iam.ActionBulkPublishDocumentVersions: ActionDocumentVersionPublish,
|
||||
iam.ActionGenerateDocumentChangelog: ActionDocumentChangelogGenerate,
|
||||
iam.ActionCreateDraftDocumentVersion: ActionDocumentDraftVersionCreate,
|
||||
iam.ActionDeleteDraftDocumentVersion: ActionDocumentVersionDeleteDraft,
|
||||
iam.ActionUpdateDocumentVersion: ActionDocumentVersionUpdate,
|
||||
iam.ActionRequestSignature: ActionDocumentVersionSignatureRequest,
|
||||
iam.ActionBulkRequestSignatures: ActionDocumentVersionSignatureRequest,
|
||||
iam.ActionSendSigningNotifications: ActionDocumentSendSigningNotifications,
|
||||
iam.ActionCancelSignatureRequest: ActionDocumentVersionCancelSignature,
|
||||
},
|
||||
coredata.DocumentVersionEntityType: {
|
||||
iam.ActionGet: ActionDocumentVersionGet,
|
||||
iam.ActionGetFile: ActionFileGet,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionGetDocument: ActionDocumentGet,
|
||||
iam.ActionGetSigned: ActionDocumentVersionGet,
|
||||
iam.ActionSignatures: ActionDocumentVersionSignatureList,
|
||||
iam.ActionExportDocumentVersionPDF: ActionDocumentVersionExportPDF,
|
||||
iam.ActionExportSignableVersionDocumentPDF: ActionDocumentVersionExportSignable,
|
||||
iam.ActionSignDocument: ActionDocumentVersionSign,
|
||||
iam.ActionUpdateDocumentVersion: ActionDocumentVersionUpdate,
|
||||
iam.ActionRequestSignature: ActionDocumentVersionSignatureRequest,
|
||||
iam.ActionDeleteDraftDocumentVersion: ActionDocumentVersionDeleteDraft,
|
||||
},
|
||||
coredata.DocumentVersionSignatureEntityType: {
|
||||
iam.ActionGet: ActionDocumentVersionSignatureList,
|
||||
iam.ActionDocumentVersion: ActionDocumentVersionGet,
|
||||
iam.ActionSignedBy: ActionPeopleGet,
|
||||
},
|
||||
coredata.RiskEntityType: {
|
||||
iam.ActionGet: ActionRiskList,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionTotalCount: ActionRiskList,
|
||||
iam.ActionListControls: ActionControlList,
|
||||
iam.ActionListMeasures: ActionMeasureList,
|
||||
iam.ActionListDocuments: ActionDocumentList,
|
||||
iam.ActionListObligations: ActionObligationList,
|
||||
iam.ActionUpdateRisk: ActionRiskUpdate,
|
||||
iam.ActionDeleteRisk: ActionRiskDelete,
|
||||
iam.ActionCreateRiskMeasureMapping: ActionRiskMeasureMappingCreate,
|
||||
iam.ActionDeleteRiskMeasureMapping: ActionRiskMeasureMappingDelete,
|
||||
iam.ActionCreateRiskDocumentMapping: ActionRiskDocumentMappingCreate,
|
||||
iam.ActionDeleteRiskDocumentMapping: ActionRiskDocumentMappingDelete,
|
||||
iam.ActionCreateRiskObligationMapping: ActionRiskObligationMappingCreate,
|
||||
iam.ActionDeleteRiskObligationMapping: ActionRiskObligationMappingDelete,
|
||||
},
|
||||
coredata.AssetEntityType: {
|
||||
iam.ActionGet: ActionAssetList,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionListVendors: ActionVendorList,
|
||||
iam.ActionGetAssetType: ActionAssetList,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionUpdateAsset: ActionAssetUpdate,
|
||||
iam.ActionDeleteAsset: ActionAssetDelete,
|
||||
},
|
||||
coredata.DatumEntityType: {
|
||||
iam.ActionGet: ActionDatumList,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionListVendors: ActionVendorList,
|
||||
iam.ActionUpdateDatum: ActionDatumUpdate,
|
||||
iam.ActionDeleteDatum: ActionDatumDelete,
|
||||
},
|
||||
coredata.AuditEntityType: {
|
||||
iam.ActionGet: ActionAuditGet,
|
||||
iam.ActionGetFile: ActionFileGet,
|
||||
iam.ActionGetFramework: ActionFrameworkGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionReport: ActionAuditList,
|
||||
iam.ActionReportUrl: ActionAuditList,
|
||||
iam.ActionListControls: ActionControlList,
|
||||
iam.ActionUpdateAudit: ActionAuditUpdate,
|
||||
iam.ActionDeleteAudit: ActionAuditDelete,
|
||||
iam.ActionUploadAuditReport: ActionAuditReportUpload,
|
||||
iam.ActionDeleteAuditReport: ActionAuditReportDelete,
|
||||
},
|
||||
coredata.ReportEntityType: {
|
||||
iam.ActionGet: ActionReportGet,
|
||||
iam.ActionGetAudit: ActionAuditGet,
|
||||
iam.ActionGetFile: ActionFileGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionGetSnapshot: ActionSnapshotList,
|
||||
iam.ActionDownloadUrl: ActionReportDownloadUrlGet,
|
||||
},
|
||||
coredata.NonconformityEntityType: {
|
||||
iam.ActionGet: ActionNonconformityList,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionAudit: ActionAuditList,
|
||||
iam.ActionUpdateNonconformity: ActionNonconformityUpdate,
|
||||
iam.ActionDeleteNonconformity: ActionNonconformityDelete,
|
||||
},
|
||||
coredata.ObligationEntityType: {
|
||||
iam.ActionGet: ActionObligationList,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionListRisks: ActionRiskList,
|
||||
iam.ActionUpdateObligation: ActionObligationUpdate,
|
||||
iam.ActionDeleteObligation: ActionObligationDelete,
|
||||
},
|
||||
coredata.ContinualImprovementEntityType: {
|
||||
iam.ActionGet: ActionContinualImprovementList,
|
||||
iam.ActionGetOwner: ActionPeopleGet,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionUpdateContinualImprovement: ActionContinualImprovementUpdate,
|
||||
iam.ActionDeleteContinualImprovement: ActionContinualImprovementDelete,
|
||||
},
|
||||
coredata.ProcessingActivityEntityType: {
|
||||
iam.ActionGet: ActionProcessingActivityList,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionListVendors: ActionVendorList,
|
||||
iam.ActionUpdateProcessingActivity: ActionProcessingActivityUpdate,
|
||||
iam.ActionDeleteProcessingActivity: ActionProcessingActivityDelete,
|
||||
},
|
||||
coredata.SnapshotEntityType: {
|
||||
iam.ActionGet: ActionSnapshotList,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionListControls: ActionControlList,
|
||||
iam.ActionDeleteSnapshot: ActionSnapshotDelete,
|
||||
},
|
||||
coredata.CustomDomainEntityType: {
|
||||
iam.ActionGet: ActionCustomDomainGet,
|
||||
iam.ActionDeleteCustomDomain: ActionCustomDomainDelete,
|
||||
},
|
||||
coredata.FileEntityType: {
|
||||
iam.ActionGet: ActionFileGet,
|
||||
iam.ActionDownloadUrl: ActionFileDownloadUrl,
|
||||
},
|
||||
coredata.MeetingEntityType: {
|
||||
iam.ActionGet: ActionMeetingList,
|
||||
iam.ActionGetOrganization: ActionOrganizationGet,
|
||||
iam.ActionTotalCount: ActionMeetingList,
|
||||
iam.ActionUpdateMeeting: ActionMeetingUpdate,
|
||||
iam.ActionDeleteMeeting: ActionMeetingDelete,
|
||||
},
|
||||
}
|
||||
|
||||
// MapLegacyAction converts a legacy action string to a new namespaced action.
|
||||
// Returns the new action and true if found, or empty string and false if not mapped.
|
||||
func MapLegacyAction(entityType uint16, legacyAction iam.Action) (iam.Action, bool) {
|
||||
if entityActions, ok := legacyActionMapping[entityType]; ok {
|
||||
if newAction, ok := entityActions[legacyAction]; ok {
|
||||
return newAction, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
281
pkg/probo/actions.go
Normal file
281
pkg/probo/actions.go
Normal file
@@ -0,0 +1,281 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package probo
|
||||
|
||||
// Probo Service Actions
|
||||
// Format: core:<entity>:<action>
|
||||
const (
|
||||
// Organization actions
|
||||
ActionOrganizationGet = "core:organization:get"
|
||||
ActionOrganizationGetLogoUrl = "core:organization:get-logo-url"
|
||||
ActionOrganizationGetHorizontalLogoUrl = "core:organization:get-horizontal-logo-url"
|
||||
|
||||
// OrganizationContext actions
|
||||
ActionOrganizationContextGet = "core:organization-context:get"
|
||||
ActionOrganizationContextUpdate = "core:organization-context:update"
|
||||
|
||||
// TrustCenter actions
|
||||
ActionTrustCenterGet = "core:trust-center:get"
|
||||
ActionTrustCenterUpdate = "core:trust-center:update"
|
||||
ActionTrustCenterNonDisclosureAgreementUpload = "core:trust-center:upload-nda"
|
||||
ActionTrustCenterNonDisclosureAgreementDelete = "core:trust-center:delete-nda"
|
||||
ActionTrustCenterAccessCreate = "core:trust-center:create-access"
|
||||
ActionTrustCenterReferenceCreate = "core:trust-center:create-reference"
|
||||
|
||||
// TrustCenterAccess actions
|
||||
ActionTrustCenterAccessUpdate = "core:trust-center-access:update"
|
||||
ActionTrustCenterAccessDelete = "core:trust-center-access:delete"
|
||||
|
||||
// TrustCenterReference actions
|
||||
ActionTrustCenterReferenceUpdate = "core:trust-center-reference:update"
|
||||
ActionTrustCenterReferenceDelete = "core:trust-center-reference:delete"
|
||||
|
||||
// TrustCenterFile actions
|
||||
ActionTrustCenterFileGet = "core:trust-center-file:get"
|
||||
ActionTrustCenterFileList = "core:trust-center-file:list"
|
||||
ActionTrustCenterFileUpdate = "core:trust-center-file:update"
|
||||
ActionTrustCenterFileDelete = "core:trust-center-file:delete"
|
||||
ActionTrustCenterFileCreate = "core:trust-center-file:create"
|
||||
|
||||
// People actions
|
||||
ActionPeopleGet = "core:people:get"
|
||||
ActionPeopleList = "core:people:list"
|
||||
ActionPeopleCreate = "core:people:create"
|
||||
ActionPeopleUpdate = "core:people:update"
|
||||
ActionPeopleDelete = "core:people:delete"
|
||||
|
||||
// Vendor actions
|
||||
ActionVendorList = "core:vendor:list"
|
||||
ActionVendorGet = "core:vendor:get"
|
||||
ActionVendorCreate = "core:vendor:create"
|
||||
ActionVendorUpdate = "core:vendor:update"
|
||||
ActionVendorDelete = "core:vendor:delete"
|
||||
ActionVendorAssess = "core:vendor:assess"
|
||||
|
||||
// VendorContact actions
|
||||
ActionVendorContactCreate = "core:vendor-contact:create"
|
||||
ActionVendorContactUpdate = "core:vendor-contact:update"
|
||||
ActionVendorContactDelete = "core:vendor-contact:delete"
|
||||
|
||||
// VendorService actions
|
||||
ActionVendorServiceCreate = "core:vendor-service:create"
|
||||
ActionVendorServiceUpdate = "core:vendor-service:update"
|
||||
ActionVendorServiceDelete = "core:vendor-service:delete"
|
||||
|
||||
// VendorComplianceReport actions
|
||||
ActionVendorComplianceReportUpload = "core:vendor-compliance-report:upload"
|
||||
ActionVendorComplianceReportDelete = "core:vendor-compliance-report:delete"
|
||||
|
||||
// VendorBusinessAssociateAgreement actions
|
||||
ActionVendorBusinessAssociateAgreementUpload = "core:vendor-business-associate-agreement:upload"
|
||||
ActionVendorBusinessAssociateAgreementUpdate = "core:vendor-business-associate-agreement:update"
|
||||
ActionVendorBusinessAssociateAgreementDelete = "core:vendor-business-associate-agreement:delete"
|
||||
|
||||
// VendorDataPrivacyAgreement actions
|
||||
ActionVendorDataPrivacyAgreementUpload = "core:vendor-data-privacy-agreement:upload"
|
||||
ActionVendorDataPrivacyAgreementUpdate = "core:vendor-data-privacy-agreement:update"
|
||||
ActionVendorDataPrivacyAgreementDelete = "core:vendor-data-privacy-agreement:delete"
|
||||
|
||||
// VendorRiskAssessment actions
|
||||
ActionVendorRiskAssessmentCreate = "core:vendor-risk-assessment:create"
|
||||
|
||||
// Framework actions
|
||||
ActionFrameworkGet = "core:framework:get"
|
||||
ActionFrameworkList = "core:framework:list"
|
||||
ActionFrameworkCreate = "core:framework:create"
|
||||
ActionFrameworkUpdate = "core:framework:update"
|
||||
ActionFrameworkDelete = "core:framework:delete"
|
||||
ActionFrameworkStateOfApplicabilityGenerate = "core:framework:generate-state-of-applicability"
|
||||
ActionFrameworkExport = "core:framework:export"
|
||||
ActionFrameworkImport = "core:framework:import"
|
||||
|
||||
// Control actions
|
||||
ActionControlList = "core:control:list"
|
||||
ActionControlCreate = "core:control:create"
|
||||
ActionControlUpdate = "core:control:update"
|
||||
ActionControlDelete = "core:control:delete"
|
||||
ActionControlMeasureMappingCreate = "core:control:create-measure-mapping"
|
||||
ActionControlMeasureMappingDelete = "core:control:delete-measure-mapping"
|
||||
ActionControlDocumentMappingCreate = "core:control:create-document-mapping"
|
||||
ActionControlDocumentMappingDelete = "core:control:delete-document-mapping"
|
||||
ActionControlAuditMappingCreate = "core:control:create-audit-mapping"
|
||||
ActionControlAuditMappingDelete = "core:control:delete-audit-mapping"
|
||||
ActionControlSnapshotMappingCreate = "core:control:create-snapshot-mapping"
|
||||
ActionControlSnapshotMappingDelete = "core:control:delete-snapshot-mapping"
|
||||
|
||||
// Measure actions
|
||||
ActionMeasureGet = "core:measure:get"
|
||||
ActionMeasureList = "core:measure:list"
|
||||
ActionMeasureCreate = "core:measure:create"
|
||||
ActionMeasureUpdate = "core:measure:update"
|
||||
ActionMeasureDelete = "core:measure:delete"
|
||||
ActionMeasureEvidenceUpload = "core:measure:upload-evidence"
|
||||
ActionMeasureImport = "core:measure:import"
|
||||
|
||||
// Task actions
|
||||
ActionTaskGet = "core:task:get"
|
||||
ActionTaskList = "core:task:list"
|
||||
ActionTaskCreate = "core:task:create"
|
||||
ActionTaskUpdate = "core:task:update"
|
||||
ActionTaskDelete = "core:task:delete"
|
||||
ActionTaskAssign = "core:task:assign"
|
||||
ActionTaskUnassign = "core:task:unassign"
|
||||
|
||||
// Evidence actions
|
||||
ActionEvidenceList = "core:evidence:list"
|
||||
ActionEvidenceDelete = "core:evidence:delete"
|
||||
|
||||
// Document actions
|
||||
ActionDocumentGet = "core:document:get"
|
||||
ActionDocumentList = "core:document:list"
|
||||
ActionDocumentCreate = "core:document:create"
|
||||
ActionDocumentUpdate = "core:document:update"
|
||||
ActionDocumentDelete = "core:document:delete"
|
||||
ActionDocumentChangelogGenerate = "core:document:generate-changelog"
|
||||
ActionDocumentDraftVersionCreate = "core:document:create-draft-version"
|
||||
ActionDocumentSendSigningNotifications = "core:document:send-signing-notifications"
|
||||
|
||||
// DocumentVersion actions
|
||||
ActionDocumentVersionGet = "core:document-version:get"
|
||||
ActionDocumentVersionList = "core:document-version:list"
|
||||
ActionDocumentVersionExportPDF = "core:document-version:export-pdf"
|
||||
ActionDocumentVersionExportSignable = "core:document-version:export-signable-pdf"
|
||||
ActionDocumentVersionSign = "core:document-version:sign"
|
||||
ActionDocumentVersionUpdate = "core:document-version:update"
|
||||
ActionDocumentVersionSignatureRequest = "core:document-version:request-signature"
|
||||
ActionDocumentVersionDeleteDraft = "core:document-version:delete-draft"
|
||||
ActionDocumentVersionPublish = "core:document-version:publish"
|
||||
ActionDocumentVersionCancelSignature = "core:document-version:cancel-signature"
|
||||
ActionDocumentVersionExport = "core:document-version:export"
|
||||
|
||||
// DocumentVersionSignature actions
|
||||
ActionDocumentVersionSignatureList = "core:document-version-signature:list"
|
||||
|
||||
// Risk actions
|
||||
ActionRiskList = "core:risk:list"
|
||||
ActionRiskCreate = "core:risk:create"
|
||||
ActionRiskUpdate = "core:risk:update"
|
||||
ActionRiskDelete = "core:risk:delete"
|
||||
ActionRiskMeasureMappingCreate = "core:risk:create-measure-mapping"
|
||||
ActionRiskMeasureMappingDelete = "core:risk:delete-measure-mapping"
|
||||
ActionRiskDocumentMappingCreate = "core:risk:create-document-mapping"
|
||||
ActionRiskDocumentMappingDelete = "core:risk:delete-document-mapping"
|
||||
ActionRiskObligationMappingCreate = "core:risk:create-obligation-mapping"
|
||||
ActionRiskObligationMappingDelete = "core:risk:delete-obligation-mapping"
|
||||
|
||||
// Asset actions
|
||||
ActionAssetList = "core:asset:list"
|
||||
ActionAssetCreate = "core:asset:create"
|
||||
ActionAssetUpdate = "core:asset:update"
|
||||
ActionAssetDelete = "core:asset:delete"
|
||||
|
||||
// Datum actions
|
||||
ActionDatumList = "core:datum:list"
|
||||
ActionDatumCreate = "core:datum:create"
|
||||
ActionDatumUpdate = "core:datum:update"
|
||||
ActionDatumDelete = "core:datum:delete"
|
||||
|
||||
// Audit actions
|
||||
ActionAuditGet = "core:audit:get"
|
||||
ActionAuditList = "core:audit:list"
|
||||
ActionAuditCreate = "core:audit:create"
|
||||
ActionAuditUpdate = "core:audit:update"
|
||||
ActionAuditDelete = "core:audit:delete"
|
||||
ActionAuditReportUpload = "core:audit:upload-report"
|
||||
ActionAuditReportDelete = "core:audit:delete-report"
|
||||
|
||||
// Report actions
|
||||
ActionReportGet = "core:report:get"
|
||||
ActionReportGetReportUrl = "core:report:get-report-url"
|
||||
ActionReportDownloadUrlGet = "core:report:get-download-url"
|
||||
|
||||
// Nonconformity actions
|
||||
ActionNonconformityList = "core:nonconformity:list"
|
||||
ActionNonconformityCreate = "core:nonconformity:create"
|
||||
ActionNonconformityUpdate = "core:nonconformity:update"
|
||||
ActionNonconformityDelete = "core:nonconformity:delete"
|
||||
|
||||
// Obligation actions
|
||||
ActionObligationList = "core:obligation:list"
|
||||
ActionObligationCreate = "core:obligation:create"
|
||||
ActionObligationUpdate = "core:obligation:update"
|
||||
ActionObligationDelete = "core:obligation:delete"
|
||||
|
||||
// ContinualImprovement actions
|
||||
ActionContinualImprovementList = "core:continual-improvement:list"
|
||||
ActionContinualImprovementCreate = "core:continual-improvement:create"
|
||||
ActionContinualImprovementUpdate = "core:continual-improvement:update"
|
||||
ActionContinualImprovementDelete = "core:continual-improvement:delete"
|
||||
|
||||
// ProcessingActivity actions
|
||||
ActionProcessingActivityList = "core:processing-activity:list"
|
||||
ActionProcessingActivityGet = "core:processing-activity:get"
|
||||
ActionProcessingActivityCreate = "core:processing-activity:create"
|
||||
ActionProcessingActivityUpdate = "core:processing-activity:update"
|
||||
ActionProcessingActivityDelete = "core:processing-activity:delete"
|
||||
ActionProcessingActivityExport = "core:processing-activity:export"
|
||||
|
||||
// Snapshot actions
|
||||
ActionSnapshotList = "core:snapshot:list"
|
||||
ActionSnapshotCreate = "core:snapshot:create"
|
||||
ActionSnapshotDelete = "core:snapshot:delete"
|
||||
|
||||
// CustomDomain actions
|
||||
ActionCustomDomainGet = "core:custom-domain:get"
|
||||
ActionCustomDomainCreate = "core:custom-domain:create"
|
||||
ActionCustomDomainDelete = "core:custom-domain:delete"
|
||||
|
||||
// File actions
|
||||
ActionFileGet = "core:file:get"
|
||||
ActionFileDownloadUrl = "core:file:download-url"
|
||||
|
||||
// Meeting actions
|
||||
ActionMeetingList = "core:meeting:list"
|
||||
ActionMeetingCreate = "core:meeting:create"
|
||||
ActionMeetingUpdate = "core:meeting:update"
|
||||
ActionMeetingDelete = "core:meeting:delete"
|
||||
|
||||
// SlackConnection actions
|
||||
ActionSlackConnectionList = "core:slack-connection:list"
|
||||
|
||||
ActionDataProtectionImpactAssessmentList = "core:data-protection-impact-assessment:list"
|
||||
ActionDataProtectionImpactAssessmentCreate = "core:data-protection-impact-assessment:create"
|
||||
ActionDataProtectionImpactAssessmentUpdate = "core:data-protection-impact-assessment:update"
|
||||
ActionDataProtectionImpactAssessmentDelete = "core:data-protection-impact-assessment:delete"
|
||||
|
||||
ActionTransferImpactAssessmentList = "core:transfer-impact-assessment:list"
|
||||
ActionTransferImpactAssessmentCreate = "core:transfer-impact-assessment:create"
|
||||
ActionTransferImpactAssessmentUpdate = "core:transfer-impact-assessment:update"
|
||||
ActionTransferImpactAssessmentDelete = "core:transfer-impact-assessment:delete"
|
||||
|
||||
ActionDataProtectionOfficerList = "core:data-protection-officer:list"
|
||||
|
||||
ActionRightsRequesList = "core:rights-request:list"
|
||||
|
||||
ActionStateOfApplicabilityList = "core:state-of-applicability:list"
|
||||
ActionStateOfApplicabilityGet = "core:state-of-applicability:get"
|
||||
ActionStateOfApplicabilityCreate = "core:state-of-applicability:create"
|
||||
ActionStateOfApplicabilityUpdate = "core:state-of-applicability:update"
|
||||
ActionStateOfApplicabilityDelete = "core:state-of-applicability:delete"
|
||||
ActionStateOfApplicabilityExport = "core:state-of-applicability:export"
|
||||
|
||||
ActionStateOfApplicabilityControlMappingList = "core:state-of-applicability-control-mapping:list"
|
||||
ActionStateOfApplicabilityControlMappingCreate = "core:state-of-applicability-control-mapping:create"
|
||||
ActionStateOfApplicabilityControlMappingDelete = "core:state-of-applicability-control-mapping:delete"
|
||||
|
||||
ActionControlObligationMappingList = "core:control-obligation-mapping:list"
|
||||
ActionControlObligationMappingCreate = "core:control-obligation-mapping:create"
|
||||
ActionControlObligationMappingDelete = "core:control-obligation-mapping:delete"
|
||||
)
|
||||
169
pkg/probo/policies.go
Normal file
169
pkg/probo/policies.go
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package probo
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/iam/policy"
|
||||
)
|
||||
|
||||
// OwnerPolicy defines permissions for organization owners.
|
||||
var OwnerPolicy = policy.NewPolicy(
|
||||
"probo:owner",
|
||||
"Probo Owner",
|
||||
// Full access to all probo resources
|
||||
policy.Allow("core:*").WithSID("full-core-access"),
|
||||
).WithDescription("Full probo access for organization owners")
|
||||
|
||||
// AdminPolicy defines permissions for organization admins.
|
||||
var AdminPolicy = policy.NewPolicy(
|
||||
"probo:admin",
|
||||
"Probo Admin",
|
||||
// Full access to all probo resources (same as owner for core entities)
|
||||
policy.Allow("core:*").WithSID("full-core-access"),
|
||||
).WithDescription("Probo admin access - can manage core entities")
|
||||
|
||||
// ViewerPolicy defines read-only permissions for organization viewers.
|
||||
var ViewerPolicy = policy.NewPolicy(
|
||||
"probo:viewer",
|
||||
"Probo Viewer",
|
||||
// Organization read actions
|
||||
policy.Allow(
|
||||
ActionOrganizationGet,
|
||||
ActionOrganizationGetLogoUrl,
|
||||
ActionOrganizationGetHorizontalLogoUrl,
|
||||
).WithSID("org-read-access"),
|
||||
|
||||
// Entity read actions
|
||||
policy.Allow(
|
||||
ActionPeopleGet, ActionPeopleList,
|
||||
ActionVendorList,
|
||||
ActionFrameworkGet, ActionFrameworkList,
|
||||
ActionControlList,
|
||||
ActionMeasureGet, ActionMeasureList,
|
||||
ActionTaskGet, ActionTaskList,
|
||||
ActionEvidenceList,
|
||||
ActionDocumentGet, ActionDocumentList,
|
||||
ActionDocumentVersionGet, ActionDocumentVersionList,
|
||||
ActionDocumentVersionSignatureList,
|
||||
ActionRiskList,
|
||||
ActionAssetList,
|
||||
ActionDatumList,
|
||||
ActionAuditGet, ActionAuditList,
|
||||
ActionReportGet, ActionReportGetReportUrl, ActionReportDownloadUrlGet,
|
||||
ActionNonconformityList,
|
||||
ActionObligationList,
|
||||
ActionContinualImprovementList,
|
||||
ActionProcessingActivityList,
|
||||
ActionSnapshotList,
|
||||
ActionMeetingList,
|
||||
ActionFileGet, ActionFileDownloadUrl,
|
||||
ActionSlackConnectionList,
|
||||
).WithSID("entity-read-access"),
|
||||
|
||||
// TrustCenter read actions
|
||||
policy.Allow(
|
||||
ActionTrustCenterGet,
|
||||
ActionTrustCenterFileGet, ActionTrustCenterFileList,
|
||||
).WithSID("trust-center-read-access"),
|
||||
|
||||
// CustomDomain read actions
|
||||
policy.Allow(ActionCustomDomainGet).WithSID("custom-domain-read"),
|
||||
|
||||
// OrganizationContext read actions
|
||||
policy.Allow(ActionOrganizationContextGet).WithSID("organization-context-read"),
|
||||
|
||||
// Document signing actions
|
||||
policy.Allow(
|
||||
ActionDocumentVersionExportPDF, ActionDocumentVersionExportSignable, ActionDocumentVersionSign,
|
||||
).WithSID("document-signing"),
|
||||
).WithDescription("Read-only probo access for organization viewers")
|
||||
|
||||
// AuditorPolicy defines permissions for auditor role.
|
||||
// Auditors have read access to non-employee content plus some specific auditor features.
|
||||
var AuditorPolicy = policy.NewPolicy(
|
||||
"probo:auditor",
|
||||
"Probo Auditor",
|
||||
// Same as viewer but without employee-specific content
|
||||
policy.Allow(
|
||||
ActionOrganizationGet,
|
||||
ActionOrganizationGetLogoUrl,
|
||||
ActionOrganizationGetHorizontalLogoUrl,
|
||||
).WithSID("org-read-access"),
|
||||
|
||||
// Entity read access (same as viewer)
|
||||
policy.Allow(
|
||||
ActionPeopleGet, ActionPeopleList,
|
||||
ActionVendorList,
|
||||
ActionFrameworkGet, ActionFrameworkList,
|
||||
ActionControlList,
|
||||
ActionMeasureGet, ActionMeasureList,
|
||||
ActionEvidenceList,
|
||||
ActionDocumentGet, ActionDocumentList,
|
||||
ActionDocumentVersionGet, ActionDocumentVersionList,
|
||||
ActionDocumentVersionSignatureList,
|
||||
ActionRiskList,
|
||||
ActionAssetList,
|
||||
ActionDatumList,
|
||||
ActionAuditGet, ActionAuditList,
|
||||
ActionReportGet, ActionReportGetReportUrl, ActionReportDownloadUrlGet,
|
||||
ActionNonconformityList,
|
||||
ActionObligationList,
|
||||
ActionContinualImprovementList,
|
||||
ActionProcessingActivityList,
|
||||
ActionSnapshotList,
|
||||
ActionFileGet, ActionFileDownloadUrl,
|
||||
).WithSID("entity-read-access"),
|
||||
|
||||
// Document signing actions
|
||||
policy.Allow(
|
||||
ActionDocumentVersionExportPDF, ActionDocumentVersionExportSignable, ActionDocumentVersionSign,
|
||||
).WithSID("document-signing"),
|
||||
).WithDescription("Read-only probo access for auditors (excludes internal/employee content)")
|
||||
|
||||
// EmployeePolicy defines permissions for employee role.
|
||||
// Employees have access to internal documents and some limited read access.
|
||||
var EmployeePolicy = policy.NewPolicy(
|
||||
"probo:employee",
|
||||
"Probo Employee",
|
||||
// Basic organization access
|
||||
policy.Allow(
|
||||
ActionOrganizationGet,
|
||||
ActionOrganizationGetLogoUrl,
|
||||
).WithSID("org-basic-access"),
|
||||
|
||||
// Document signing access
|
||||
policy.Allow(
|
||||
ActionDocumentGet, ActionDocumentList,
|
||||
).WithSID("document-signing-access"),
|
||||
|
||||
// Document version signing
|
||||
policy.Allow(
|
||||
ActionDocumentVersionGet, ActionDocumentVersionList,
|
||||
ActionDocumentVersionSign,
|
||||
ActionDocumentVersionExportSignable,
|
||||
).WithSID("document-version-signing"),
|
||||
).WithDescription("Employee access - can sign documents and view internal content")
|
||||
|
||||
// ProboPolicySet returns the PolicySet for the probo service.
|
||||
// This is registered with the IAM Authorizer when probo.Service is created.
|
||||
func ProboPolicySet() *iam.PolicySet {
|
||||
return iam.NewPolicySet().
|
||||
AddRolePolicy("OWNER", OwnerPolicy).
|
||||
AddRolePolicy("ADMIN", AdminPolicy).
|
||||
AddRolePolicy("VIEWER", ViewerPolicy).
|
||||
AddRolePolicy("AUDITOR", AuditorPolicy).
|
||||
AddRolePolicy("EMPLOYEE", EmployeePolicy)
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/filevalidation"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/html2pdf"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/mail"
|
||||
"go.probo.inc/probo/pkg/slack"
|
||||
)
|
||||
@@ -135,11 +136,14 @@ func NewService(
|
||||
fileManagerService *filemanager.Service,
|
||||
logger *log.Logger,
|
||||
slackService *slack.Service,
|
||||
iamService *iam.Service,
|
||||
) (*Service, error) {
|
||||
if bucket == "" {
|
||||
return nil, fmt.Errorf("bucket is required")
|
||||
}
|
||||
|
||||
iamService.Authorizer.RegisterPolicySet(ProboPolicySet())
|
||||
|
||||
svc := &Service{
|
||||
pg: pgClient,
|
||||
s3: s3Client,
|
||||
|
||||
@@ -388,6 +388,7 @@ func (impl *Implm) Run(
|
||||
fileManagerService,
|
||||
l.Named("probo"),
|
||||
slackService,
|
||||
iamService,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create probo service: %w", err)
|
||||
|
||||
@@ -230,7 +230,7 @@ type Membership implements Node {
|
||||
profile: MembershipProfile @goField(forceResolver: true) @isViewer
|
||||
organization: Organization @goField(forceResolver: true)
|
||||
role: MembershipRole!
|
||||
permissions: [Permission!]!
|
||||
permissions: [Permission!] @goField(forceResolver: true)
|
||||
|
||||
lastSession: Session @goField(forceResolver: true) @isViewer
|
||||
}
|
||||
@@ -276,11 +276,6 @@ type Permission implements Node {
|
||||
principalId: ID!
|
||||
}
|
||||
|
||||
type PermissionGrant {
|
||||
application: Application!
|
||||
accessLevel: AccessLevel!
|
||||
}
|
||||
|
||||
type Application {
|
||||
id: ApplicationId!
|
||||
name: String!
|
||||
@@ -303,7 +298,6 @@ type SAMLConfiguration implements Node {
|
||||
spMetadataUrl: String!
|
||||
testLoginUrl: String!
|
||||
attributeMappings: SAMLAttributeMappings!
|
||||
defaultPermissions: [PermissionGrant!]!
|
||||
}
|
||||
|
||||
type SAMLAttributeMappings {
|
||||
|
||||
@@ -267,11 +267,6 @@ type ComplexityRoot struct {
|
||||
PrincipalType func(childComplexity int) int
|
||||
}
|
||||
|
||||
PermissionGrant struct {
|
||||
AccessLevel func(childComplexity int) int
|
||||
Application func(childComplexity int) int
|
||||
}
|
||||
|
||||
PersonalAPIKey struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
ExpiresAt func(childComplexity int) int
|
||||
@@ -335,7 +330,6 @@ type ComplexityRoot struct {
|
||||
AttributeMappings func(childComplexity int) int
|
||||
AutoSignupEnabled func(childComplexity int) int
|
||||
CreatedAt func(childComplexity int) int
|
||||
DefaultPermissions func(childComplexity int) int
|
||||
DomainVerificationToken func(childComplexity int) int
|
||||
DomainVerifiedAt func(childComplexity int) int
|
||||
EmailDomain func(childComplexity int) int
|
||||
@@ -438,6 +432,7 @@ type MembershipResolver interface {
|
||||
Profile(ctx context.Context, obj *types.Membership) (*types.MembershipProfile, error)
|
||||
Organization(ctx context.Context, obj *types.Membership) (*types.Organization, error)
|
||||
|
||||
Permissions(ctx context.Context, obj *types.Membership) ([]*types.Permission, error)
|
||||
LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error)
|
||||
}
|
||||
type MembershipConnectionResolver interface {
|
||||
@@ -1385,19 +1380,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
|
||||
return e.complexity.Permission.PrincipalType(childComplexity), true
|
||||
|
||||
case "PermissionGrant.accessLevel":
|
||||
if e.complexity.PermissionGrant.AccessLevel == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PermissionGrant.AccessLevel(childComplexity), true
|
||||
case "PermissionGrant.application":
|
||||
if e.complexity.PermissionGrant.Application == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.PermissionGrant.Application(childComplexity), true
|
||||
|
||||
case "PersonalAPIKey.createdAt":
|
||||
if e.complexity.PersonalAPIKey.CreatedAt == nil {
|
||||
break
|
||||
@@ -1593,12 +1575,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
}
|
||||
|
||||
return e.complexity.SAMLConfiguration.CreatedAt(childComplexity), true
|
||||
case "SAMLConfiguration.defaultPermissions":
|
||||
if e.complexity.SAMLConfiguration.DefaultPermissions == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.SAMLConfiguration.DefaultPermissions(childComplexity), true
|
||||
case "SAMLConfiguration.domainVerificationToken":
|
||||
if e.complexity.SAMLConfiguration.DomainVerificationToken == nil {
|
||||
break
|
||||
@@ -2219,7 +2195,7 @@ type Membership implements Node {
|
||||
profile: MembershipProfile @goField(forceResolver: true) @isViewer
|
||||
organization: Organization @goField(forceResolver: true)
|
||||
role: MembershipRole!
|
||||
permissions: [Permission!]!
|
||||
permissions: [Permission!] @goField(forceResolver: true)
|
||||
|
||||
lastSession: Session @goField(forceResolver: true) @isViewer
|
||||
}
|
||||
@@ -2265,11 +2241,6 @@ type Permission implements Node {
|
||||
principalId: ID!
|
||||
}
|
||||
|
||||
type PermissionGrant {
|
||||
application: Application!
|
||||
accessLevel: AccessLevel!
|
||||
}
|
||||
|
||||
type Application {
|
||||
id: ApplicationId!
|
||||
name: String!
|
||||
@@ -2292,7 +2263,6 @@ type SAMLConfiguration implements Node {
|
||||
spMetadataUrl: String!
|
||||
testLoginUrl: String!
|
||||
attributeMappings: SAMLAttributeMappings!
|
||||
defaultPermissions: [PermissionGrant!]!
|
||||
}
|
||||
|
||||
type SAMLAttributeMappings {
|
||||
@@ -5132,12 +5102,12 @@ func (ec *executionContext) _Membership_permissions(ctx context.Context, field g
|
||||
field,
|
||||
ec.fieldContext_Membership_permissions,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Permissions, nil
|
||||
return ec.resolvers.Membership().Permissions(ctx, obj)
|
||||
},
|
||||
nil,
|
||||
ec.marshalNPermission2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionᚄ,
|
||||
true,
|
||||
ec.marshalOPermission2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionᚄ,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5145,8 +5115,8 @@ func (ec *executionContext) fieldContext_Membership_permissions(_ context.Contex
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Membership",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "id":
|
||||
@@ -8107,74 +8077,6 @@ func (ec *executionContext) fieldContext_Permission_principalId(_ context.Contex
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PermissionGrant_application(ctx context.Context, field graphql.CollectedField, obj *types.PermissionGrant) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_PermissionGrant_application,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.Application, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNApplication2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐApplication,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_PermissionGrant_application(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "PermissionGrant",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "id":
|
||||
return ec.fieldContext_Application_id(ctx, field)
|
||||
case "name":
|
||||
return ec.fieldContext_Application_name(ctx, field)
|
||||
case "description":
|
||||
return ec.fieldContext_Application_description(ctx, field)
|
||||
case "availableAccessLevels":
|
||||
return ec.fieldContext_Application_availableAccessLevels(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type Application", field.Name)
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PermissionGrant_accessLevel(ctx context.Context, field graphql.CollectedField, obj *types.PermissionGrant) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_PermissionGrant_accessLevel,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.AccessLevel, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNAccessLevel2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐAccessLevel,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_PermissionGrant_accessLevel(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "PermissionGrant",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type AccessLevel does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _PersonalAPIKey_id(ctx context.Context, field graphql.CollectedField, obj *types.PersonalAPIKey) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -9623,41 +9525,6 @@ func (ec *executionContext) fieldContext_SAMLConfiguration_attributeMappings(_ c
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _SAMLConfiguration_defaultPermissions(ctx context.Context, field graphql.CollectedField, obj *types.SAMLConfiguration) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_SAMLConfiguration_defaultPermissions,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return obj.DefaultPermissions, nil
|
||||
},
|
||||
nil,
|
||||
ec.marshalNPermissionGrant2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionGrantᚄ,
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_SAMLConfiguration_defaultPermissions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "SAMLConfiguration",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "application":
|
||||
return ec.fieldContext_PermissionGrant_application(ctx, field)
|
||||
case "accessLevel":
|
||||
return ec.fieldContext_PermissionGrant_accessLevel(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type PermissionGrant", field.Name)
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _SAMLConfigurationConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.SAMLConfigurationConnection) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -9813,8 +9680,6 @@ func (ec *executionContext) fieldContext_SAMLConfigurationEdge_node(_ context.Co
|
||||
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||
case "attributeMappings":
|
||||
return ec.fieldContext_SAMLConfiguration_attributeMappings(ctx, field)
|
||||
case "defaultPermissions":
|
||||
return ec.fieldContext_SAMLConfiguration_defaultPermissions(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type SAMLConfiguration", field.Name)
|
||||
},
|
||||
@@ -10736,8 +10601,6 @@ func (ec *executionContext) fieldContext_UpdateSAMLConfigurationPayload_samlConf
|
||||
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||
case "attributeMappings":
|
||||
return ec.fieldContext_SAMLConfiguration_attributeMappings(ctx, field)
|
||||
case "defaultPermissions":
|
||||
return ec.fieldContext_SAMLConfiguration_defaultPermissions(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type SAMLConfiguration", field.Name)
|
||||
},
|
||||
@@ -14543,10 +14406,38 @@ func (ec *executionContext) _Membership(ctx context.Context, sel ast.SelectionSe
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "permissions":
|
||||
out.Values[i] = ec._Membership_permissions(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Membership_permissions(ctx, field, obj)
|
||||
return res
|
||||
}
|
||||
|
||||
if field.Deferrable != nil {
|
||||
dfs, ok := deferred[field.Deferrable.Label]
|
||||
di := 0
|
||||
if ok {
|
||||
dfs.AddField(field)
|
||||
di = len(dfs.Values) - 1
|
||||
} else {
|
||||
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
|
||||
deferred[field.Deferrable.Label] = dfs
|
||||
}
|
||||
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
|
||||
return innerFunc(ctx, dfs)
|
||||
})
|
||||
|
||||
// don't run the out.Concurrently() call below
|
||||
out.Values[i] = graphql.Null
|
||||
continue
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
case "lastSession":
|
||||
field := field
|
||||
|
||||
@@ -15387,50 +15278,6 @@ func (ec *executionContext) _Permission(ctx context.Context, sel ast.SelectionSe
|
||||
return out
|
||||
}
|
||||
|
||||
var permissionGrantImplementors = []string{"PermissionGrant"}
|
||||
|
||||
func (ec *executionContext) _PermissionGrant(ctx context.Context, sel ast.SelectionSet, obj *types.PermissionGrant) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, permissionGrantImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
deferred := make(map[string]*graphql.FieldSet)
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("PermissionGrant")
|
||||
case "application":
|
||||
out.Values[i] = ec._PermissionGrant_application(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "accessLevel":
|
||||
out.Values[i] = ec._PermissionGrant_accessLevel(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch(ctx)
|
||||
if out.Invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
|
||||
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
|
||||
|
||||
for label, dfs := range deferred {
|
||||
ec.processDeferredGroup(graphql.DeferredGroup{
|
||||
Label: label,
|
||||
Path: graphql.GetPath(ctx),
|
||||
FieldSet: dfs,
|
||||
Context: ctx,
|
||||
})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
var personalAPIKeyImplementors = []string{"PersonalAPIKey", "Node"}
|
||||
|
||||
func (ec *executionContext) _PersonalAPIKey(ctx context.Context, sel ast.SelectionSet, obj *types.PersonalAPIKey) graphql.Marshaler {
|
||||
@@ -16096,11 +15943,6 @@ func (ec *executionContext) _SAMLConfiguration(ctx context.Context, sel ast.Sele
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
case "defaultPermissions":
|
||||
out.Values[i] = ec._SAMLConfiguration_defaultPermissions(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
out.Invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
@@ -17767,50 +17609,6 @@ func (ec *executionContext) marshalNPageInfo2goᚗproboᚗincᚋproboᚋpkgᚋse
|
||||
return ec._PageInfo(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPermission2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.Permission) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalNPermission2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermission(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, e := range ret {
|
||||
if e == graphql.Null {
|
||||
return graphql.Null
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPermission2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermission(ctx context.Context, sel ast.SelectionSet, v *types.Permission) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
@@ -17821,60 +17619,6 @@ func (ec *executionContext) marshalNPermission2ᚖgoᚗproboᚗincᚋproboᚋpkg
|
||||
return ec._Permission(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPermissionGrant2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionGrantᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.PermissionGrant) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalNPermissionGrant2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionGrant(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, e := range ret {
|
||||
if e == graphql.Null {
|
||||
return graphql.Null
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPermissionGrant2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionGrant(ctx context.Context, sel ast.SelectionSet, v *types.PermissionGrant) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._PermissionGrant(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNPersonalAPIKey2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPersonalAPIKey(ctx context.Context, sel ast.SelectionSet, v *types.PersonalAPIKey) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
@@ -18878,6 +18622,53 @@ func (ec *executionContext) marshalOOrganization2ᚖgoᚗproboᚗincᚋproboᚋp
|
||||
return ec._Organization(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOPermission2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.Permission) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
isLen1 := len(v) == 1
|
||||
if !isLen1 {
|
||||
wg.Add(len(v))
|
||||
}
|
||||
for i := range v {
|
||||
i := i
|
||||
fc := &graphql.FieldContext{
|
||||
Index: &i,
|
||||
Result: &v[i],
|
||||
}
|
||||
ctx := graphql.WithFieldContext(ctx, fc)
|
||||
f := func(i int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = nil
|
||||
}
|
||||
}()
|
||||
if !isLen1 {
|
||||
defer wg.Done()
|
||||
}
|
||||
ret[i] = ec.marshalNPermission2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermission(ctx, sel, v[i])
|
||||
}
|
||||
if isLen1 {
|
||||
f(i)
|
||||
} else {
|
||||
go f(i)
|
||||
}
|
||||
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, e := range ret {
|
||||
if e == graphql.Null {
|
||||
return graphql.Null
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalOPersonalAPIKey2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPersonalAPIKey(ctx context.Context, sel ast.SelectionSet, v *types.PersonalAPIKey) graphql.Marshaler {
|
||||
if v == nil {
|
||||
return graphql.Null
|
||||
|
||||
@@ -200,7 +200,7 @@ type Membership struct {
|
||||
Profile *MembershipProfile `json:"profile,omitempty"`
|
||||
Organization *Organization `json:"organization,omitempty"`
|
||||
Role coredata.MembershipRole `json:"role"`
|
||||
Permissions []*Permission `json:"permissions"`
|
||||
Permissions []*Permission `json:"permissions,omitempty"`
|
||||
LastSession *Session `json:"lastSession,omitempty"`
|
||||
}
|
||||
|
||||
@@ -278,11 +278,6 @@ type Permission struct {
|
||||
func (Permission) IsNode() {}
|
||||
func (this Permission) GetID() gid.GID { return this.ID }
|
||||
|
||||
type PermissionGrant struct {
|
||||
Application *Application `json:"application"`
|
||||
AccessLevel AccessLevel `json:"accessLevel"`
|
||||
}
|
||||
|
||||
type PersonalAPIKey struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -378,7 +373,6 @@ type SAMLConfiguration struct {
|
||||
SpMetadataURL string `json:"spMetadataUrl"`
|
||||
TestLoginURL string `json:"testLoginUrl"`
|
||||
AttributeMappings *SAMLAttributeMappings `json:"attributeMappings"`
|
||||
DefaultPermissions []*PermissionGrant `json:"defaultPermissions"`
|
||||
}
|
||||
|
||||
func (SAMLConfiguration) IsNode() {}
|
||||
|
||||
@@ -233,6 +233,11 @@ func (r *membershipResolver) Organization(ctx context.Context, obj *types.Member
|
||||
return types.NewOrganization(organization), nil
|
||||
}
|
||||
|
||||
// Permissions is the resolver for the permissions field.
|
||||
func (r *membershipResolver) Permissions(ctx context.Context, obj *types.Membership) ([]*types.Permission, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
// LastSession is the resolver for the lastSession field.
|
||||
func (r *membershipResolver) LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error) {
|
||||
session := SessionFromContext(ctx)
|
||||
|
||||
@@ -316,16 +316,23 @@ func GetTenantService(ctx context.Context, proboSvc *probo.Service, tenantID gid
|
||||
return proboSvc.WithTenant(tenantID)
|
||||
}
|
||||
|
||||
func (r *Resolver) MustBeAuthorized(ctx context.Context, entityID gid.GID, action iam.Action) {
|
||||
func (r *Resolver) MustAuthorize(ctx context.Context, entityID gid.GID, action iam.Action) {
|
||||
user := connect_v1.IdentityFromContext(ctx)
|
||||
apiKey := connect_v1.APIKeyFromContext(ctx)
|
||||
// apiKey := connect_v1.APIKeyFromContext(ctx)
|
||||
|
||||
var credentialID *gid.GID
|
||||
if apiKey != nil {
|
||||
credentialID = &apiKey.ID
|
||||
}
|
||||
// var credentialID *gid.GID
|
||||
// if apiKey != nil {
|
||||
// credentialID = &apiKey.ID
|
||||
// }
|
||||
|
||||
err := r.iam.LegacyAccessManagementService.Authorize(ctx, user.ID, credentialID, entityID, action)
|
||||
err := r.iam.Authorizer.Authorize(
|
||||
ctx,
|
||||
iam.AuthorizeParams{
|
||||
Principal: user.ID,
|
||||
Resource: entityID,
|
||||
Action: action,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -252,6 +252,7 @@ type ComplexityRoot struct {
|
||||
Measures func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) int
|
||||
Name func(childComplexity int) int
|
||||
Obligations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) int
|
||||
Organization func(childComplexity int) int
|
||||
SectionTitle func(childComplexity int) int
|
||||
Snapshots func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SnapshotOrderBy) int
|
||||
StateOfApplicabilityControls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.StateOfApplicabilityOrderBy) int
|
||||
@@ -1932,6 +1933,8 @@ type ContinualImprovementConnectionResolver interface {
|
||||
TotalCount(ctx context.Context, obj *types.ContinualImprovementConnection) (int, error)
|
||||
}
|
||||
type ControlResolver interface {
|
||||
Organization(ctx context.Context, obj *types.Control) (*types.Organization, error)
|
||||
|
||||
Framework(ctx context.Context, obj *types.Control) (*types.Framework, error)
|
||||
Measures(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) (*types.MeasureConnection, error)
|
||||
Documents(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy, filter *types.DocumentFilter) (*types.DocumentConnection, error)
|
||||
@@ -2926,6 +2929,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
||||
}
|
||||
|
||||
return e.complexity.Control.Obligations(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ObligationOrderBy), args["filter"].(*types.ObligationFilter)), true
|
||||
case "Control.organization":
|
||||
if e.complexity.Control.Organization == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Control.Organization(childComplexity), true
|
||||
case "Control.sectionTitle":
|
||||
if e.complexity.Control.SectionTitle == nil {
|
||||
break
|
||||
@@ -11762,6 +11771,7 @@ type Framework implements Node {
|
||||
|
||||
type Control implements Node {
|
||||
id: ID!
|
||||
organization: Organization @goField(forceResolver: true)
|
||||
sectionTitle: String!
|
||||
name: String!
|
||||
description: String
|
||||
@@ -21218,6 +21228,109 @@ func (ec *executionContext) fieldContext_Control_id(_ context.Context, field gra
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Control_organization(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
ec.OperationContext,
|
||||
field,
|
||||
ec.fieldContext_Control_organization,
|
||||
func(ctx context.Context) (any, error) {
|
||||
return ec.resolvers.Control().Organization(ctx, obj)
|
||||
},
|
||||
nil,
|
||||
ec.marshalOOrganization2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐOrganization,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Control_organization(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Control",
|
||||
Field: field,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
switch field.Name {
|
||||
case "id":
|
||||
return ec.fieldContext_Organization_id(ctx, field)
|
||||
case "name":
|
||||
return ec.fieldContext_Organization_name(ctx, field)
|
||||
case "logoUrl":
|
||||
return ec.fieldContext_Organization_logoUrl(ctx, field)
|
||||
case "horizontalLogoUrl":
|
||||
return ec.fieldContext_Organization_horizontalLogoUrl(ctx, field)
|
||||
case "description":
|
||||
return ec.fieldContext_Organization_description(ctx, field)
|
||||
case "websiteUrl":
|
||||
return ec.fieldContext_Organization_websiteUrl(ctx, field)
|
||||
case "email":
|
||||
return ec.fieldContext_Organization_email(ctx, field)
|
||||
case "headquarterAddress":
|
||||
return ec.fieldContext_Organization_headquarterAddress(ctx, field)
|
||||
case "context":
|
||||
return ec.fieldContext_Organization_context(ctx, field)
|
||||
case "slackConnections":
|
||||
return ec.fieldContext_Organization_slackConnections(ctx, field)
|
||||
case "frameworks":
|
||||
return ec.fieldContext_Organization_frameworks(ctx, field)
|
||||
case "controls":
|
||||
return ec.fieldContext_Organization_controls(ctx, field)
|
||||
case "vendors":
|
||||
return ec.fieldContext_Organization_vendors(ctx, field)
|
||||
case "peoples":
|
||||
return ec.fieldContext_Organization_peoples(ctx, field)
|
||||
case "documents":
|
||||
return ec.fieldContext_Organization_documents(ctx, field)
|
||||
case "meetings":
|
||||
return ec.fieldContext_Organization_meetings(ctx, field)
|
||||
case "statesOfApplicability":
|
||||
return ec.fieldContext_Organization_statesOfApplicability(ctx, field)
|
||||
case "measures":
|
||||
return ec.fieldContext_Organization_measures(ctx, field)
|
||||
case "risks":
|
||||
return ec.fieldContext_Organization_risks(ctx, field)
|
||||
case "tasks":
|
||||
return ec.fieldContext_Organization_tasks(ctx, field)
|
||||
case "assets":
|
||||
return ec.fieldContext_Organization_assets(ctx, field)
|
||||
case "data":
|
||||
return ec.fieldContext_Organization_data(ctx, field)
|
||||
case "audits":
|
||||
return ec.fieldContext_Organization_audits(ctx, field)
|
||||
case "nonconformities":
|
||||
return ec.fieldContext_Organization_nonconformities(ctx, field)
|
||||
case "obligations":
|
||||
return ec.fieldContext_Organization_obligations(ctx, field)
|
||||
case "continualImprovements":
|
||||
return ec.fieldContext_Organization_continualImprovements(ctx, field)
|
||||
case "rightsRequests":
|
||||
return ec.fieldContext_Organization_rightsRequests(ctx, field)
|
||||
case "processingActivities":
|
||||
return ec.fieldContext_Organization_processingActivities(ctx, field)
|
||||
case "dataProtectionImpactAssessments":
|
||||
return ec.fieldContext_Organization_dataProtectionImpactAssessments(ctx, field)
|
||||
case "transferImpactAssessments":
|
||||
return ec.fieldContext_Organization_transferImpactAssessments(ctx, field)
|
||||
case "snapshots":
|
||||
return ec.fieldContext_Organization_snapshots(ctx, field)
|
||||
case "trustCenterFiles":
|
||||
return ec.fieldContext_Organization_trustCenterFiles(ctx, field)
|
||||
case "trustCenter":
|
||||
return ec.fieldContext_Organization_trustCenter(ctx, field)
|
||||
case "customDomain":
|
||||
return ec.fieldContext_Organization_customDomain(ctx, field)
|
||||
case "createdAt":
|
||||
return ec.fieldContext_Organization_createdAt(ctx, field)
|
||||
case "updatedAt":
|
||||
return ec.fieldContext_Organization_updatedAt(ctx, field)
|
||||
}
|
||||
return nil, fmt.Errorf("no field named %q was found under type Organization", field.Name)
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Control_sectionTitle(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||
return graphql.ResolveField(
|
||||
ctx,
|
||||
@@ -21951,6 +22064,8 @@ func (ec *executionContext) fieldContext_ControlEdge_node(_ context.Context, fie
|
||||
switch field.Name {
|
||||
case "id":
|
||||
return ec.fieldContext_Control_id(ctx, field)
|
||||
case "organization":
|
||||
return ec.fieldContext_Control_organization(ctx, field)
|
||||
case "sectionTitle":
|
||||
return ec.fieldContext_Control_sectionTitle(ctx, field)
|
||||
case "name":
|
||||
@@ -51012,6 +51127,8 @@ func (ec *executionContext) fieldContext_UpdateControlPayload_control(_ context.
|
||||
switch field.Name {
|
||||
case "id":
|
||||
return ec.fieldContext_Control_id(ctx, field)
|
||||
case "organization":
|
||||
return ec.fieldContext_Control_organization(ctx, field)
|
||||
case "sectionTitle":
|
||||
return ec.fieldContext_Control_sectionTitle(ctx, field)
|
||||
case "name":
|
||||
@@ -67994,6 +68111,39 @@ func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet,
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "organization":
|
||||
field := field
|
||||
|
||||
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Control_organization(ctx, field, obj)
|
||||
return res
|
||||
}
|
||||
|
||||
if field.Deferrable != nil {
|
||||
dfs, ok := deferred[field.Deferrable.Label]
|
||||
di := 0
|
||||
if ok {
|
||||
dfs.AddField(field)
|
||||
di = len(dfs.Values) - 1
|
||||
} else {
|
||||
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
|
||||
deferred[field.Deferrable.Label] = dfs
|
||||
}
|
||||
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
|
||||
return innerFunc(ctx, dfs)
|
||||
})
|
||||
|
||||
// don't run the out.Concurrently() call below
|
||||
out.Values[i] = graphql.Null
|
||||
continue
|
||||
}
|
||||
|
||||
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
|
||||
case "sectionTitle":
|
||||
out.Values[i] = ec._Control_sectionTitle(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
|
||||
@@ -51,6 +51,9 @@ func NewControlEdge(control *coredata.Control, orderField coredata.ControlOrderF
|
||||
func NewControl(control *coredata.Control) *Control {
|
||||
return &Control{
|
||||
ID: control.ID,
|
||||
Organization: &Organization{
|
||||
ID: control.OrganizationID,
|
||||
},
|
||||
Framework: &Framework{
|
||||
ID: control.FrameworkID,
|
||||
},
|
||||
|
||||
@@ -168,6 +168,7 @@ type ContinualImprovementFilter struct {
|
||||
|
||||
type Control struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Organization *Organization `json:"organization,omitempty"`
|
||||
SectionTitle string `json:"sectionTitle"`
|
||||
Name string `json:"name"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,34 @@ func (r *Resolver) MustBeAuthorized(ctx context.Context, entityID gid.GID, actio
|
||||
credentialID = &apiKey.ID
|
||||
}
|
||||
|
||||
err := r.iamSvc.LegacyAccessManagementService.Authorize(ctx, user.ID, credentialID, entityID, action)
|
||||
// When API key is used, fall back to legacy system for intersection semantics.
|
||||
// The legacy system handles API key role checking properly.
|
||||
// TODO: Migrate API key authorization to new system.
|
||||
if credentialID != nil {
|
||||
err := r.iamSvc.LegacyAccessManagementService.Authorize(ctx, user.ID, credentialID, entityID, action)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Map legacy action to new namespaced action
|
||||
newAction, ok := probo.MapLegacyAction(entityID.EntityType(), action)
|
||||
if !ok {
|
||||
// Fall back to legacy system for unmapped actions
|
||||
err := r.iamSvc.LegacyAccessManagementService.Authorize(ctx, user.ID, credentialID, entityID, action)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Use new authorizer with mapped action
|
||||
err := r.iamSvc.Authorizer.Authorize(ctx, iam.AuthorizeParams{
|
||||
Principal: user.ID,
|
||||
Resource: entityID,
|
||||
Action: newAction,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user