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
|
TrustCenterFileEntityType uint16 = 41
|
||||||
SAMLConfigurationEntityType uint16 = 42
|
SAMLConfigurationEntityType uint16 = 42
|
||||||
PersonalAPIKeyEntityType uint16 = 43
|
PersonalAPIKeyEntityType uint16 = 43
|
||||||
PersonalAPIKeyMembershipEntityType uint16 = 44
|
_ uint16 = 44 // PersonalAPIKeyMembershipEntityType - removed
|
||||||
MeetingEntityType uint16 = 45
|
MeetingEntityType uint16 = 45
|
||||||
DataProtectionImpactAssessmentEntityType uint16 = 46
|
DataProtectionImpactAssessmentEntityType uint16 = 46
|
||||||
TransferImpactAssessmentEntityType uint16 = 47
|
TransferImpactAssessmentEntityType uint16 = 47
|
||||||
@@ -257,10 +257,6 @@ var entityRegistry = map[uint16]EntityInfo{
|
|||||||
Model: "PersonalAPIKey",
|
Model: "PersonalAPIKey",
|
||||||
Table: "iam_personal_api_keys",
|
Table: "iam_personal_api_keys",
|
||||||
},
|
},
|
||||||
PersonalAPIKeyMembershipEntityType: {
|
|
||||||
Model: "PersonalAPIKeyMembership",
|
|
||||||
Table: "iam_personal_api_key_memberships",
|
|
||||||
},
|
|
||||||
MeetingEntityType: {
|
MeetingEntityType: {
|
||||||
Model: "Meeting",
|
Model: "Meeting",
|
||||||
Table: "meetings",
|
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
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
// LEGACY: This is the legacy access management service that is used to authorize actions on entities.
|
// LEGACY ACCESS MANAGEMENT SERVICE - DEPRECATED
|
||||||
// It is deprecated and will be removed in the future.
|
//
|
||||||
// Use the Authorizer instead.
|
// 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
|
package iam
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -148,18 +159,21 @@ func (s *AccessManagementService) loadAPIKeyRoleForEntity(
|
|||||||
apiKeyID gid.GID,
|
apiKeyID gid.GID,
|
||||||
entityID gid.GID,
|
entityID gid.GID,
|
||||||
) (Role, error) {
|
) (Role, error) {
|
||||||
var akm coredata.PersonalAPIKeyMembership
|
// Load the API key to get the identity
|
||||||
if err := akm.LoadRoleByAPIKeyAndEntityID(ctx, conn, scope, apiKeyID, entityID); err != nil {
|
apiKey := &coredata.PersonalAPIKey{}
|
||||||
return "", err
|
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.
|
// Use the Identity's membership role for authorization
|
||||||
switch akm.Role {
|
var m coredata.Membership
|
||||||
case coredata.APIRoleFull:
|
if err := m.LoadRoleByIdentityAndEntityID(ctx, conn, scope, apiKey.IdentityID, entityID); err != nil {
|
||||||
return RoleFull, nil
|
if errors.Is(err, coredata.ErrResourceNotFound) {
|
||||||
default:
|
return "", err
|
||||||
return "", fmt.Errorf("unsupported api key role: %s", akm.Role)
|
|
||||||
}
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return Role(m.Role.String()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// requiredRoleNamesContain is a temporary evaluator for the current in-code permissions registry
|
// requiredRoleNamesContain is a temporary evaluator for the current in-code permissions registry
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ package iam
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"go.gearno.de/kit/pg"
|
"go.gearno.de/kit/pg"
|
||||||
"go.probo.inc/probo/pkg/coredata"
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
@@ -32,24 +34,28 @@ type Authorizer struct {
|
|||||||
policySet *PolicySet
|
policySet *PolicySet
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuthorizer creates a new authorizer with the given PolicySet.
|
// NewAuthorizer creates a new authorizer.
|
||||||
// The PolicySet should contain all role-based and self-management policies
|
// Services register their policies by calling RegisterPolicySet.
|
||||||
// from all services that need authorization.
|
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// policySet := iam.IAMPolicySet().
|
// authorizer := iam.NewAuthorizer(pgClient)
|
||||||
// Merge(documents.DocumentPolicySet()).
|
// authorizer.RegisterPolicySet(iam.IAMPolicySet())
|
||||||
// Merge(risks.RiskPolicySet())
|
// authorizer.RegisterPolicySet(probo.ProboPolicySet())
|
||||||
// authorizer := iam.NewAuthorizer(pgClient, policySet)
|
func NewAuthorizer(pgClient *pg.Client) *Authorizer {
|
||||||
func NewAuthorizer(pgClient *pg.Client, policySet *PolicySet) *Authorizer {
|
|
||||||
return &Authorizer{
|
return &Authorizer{
|
||||||
pg: pgClient,
|
pg: pgClient,
|
||||||
evaluator: policy.NewEvaluator(),
|
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.
|
// AuthorizeParams contains all parameters for an authorization check.
|
||||||
type AuthorizeParams struct {
|
type AuthorizeParams struct {
|
||||||
// Principal is the user requesting access.
|
// Principal is the user requesting access.
|
||||||
@@ -66,6 +72,53 @@ type AuthorizeParams struct {
|
|||||||
ResourceAttributes map[string]string
|
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.
|
// Authorize checks if the principal can perform the action on the resource.
|
||||||
// It combines self-management policies with role-based policies.
|
// It combines self-management policies with role-based policies.
|
||||||
func (a *Authorizer) Authorize(ctx context.Context, params AuthorizeParams) error {
|
func (a *Authorizer) Authorize(ctx context.Context, params AuthorizeParams) error {
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ const (
|
|||||||
ActionIAMIdentityListMemberships = "iam:identity:list-memberships"
|
ActionIAMIdentityListMemberships = "iam:identity:list-memberships"
|
||||||
ActionIAMIdentityListInvitations = "iam:identity:list-invitations"
|
ActionIAMIdentityListInvitations = "iam:identity:list-invitations"
|
||||||
ActionIAMIdentityListSessions = "iam:identity:list-sessions"
|
ActionIAMIdentityListSessions = "iam:identity:list-sessions"
|
||||||
|
ActionIAMIdentityListPersonalAPIKeys = "iam:identity:list-personal-api-keys"
|
||||||
|
|
||||||
// Session actions
|
// Session actions
|
||||||
ActionIAMSessionGet = "iam:session:get"
|
ActionIAMSessionGet = "iam:session:get"
|
||||||
@@ -52,4 +53,17 @@ const (
|
|||||||
// Membership actions
|
// Membership actions
|
||||||
ActionIAMMembershipGet = "iam:membership:get"
|
ActionIAMMembershipGet = "iam:membership:get"
|
||||||
ActionIAMMembershipUpdate = "iam:membership:update"
|
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").
|
).WithSID("manage-own-identity").
|
||||||
When(policy.Equals("principal.id", "resource.id")),
|
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(
|
policy.Allow(
|
||||||
ActionIAMIdentityListMemberships,
|
ActionIAMIdentityListMemberships,
|
||||||
ActionIAMIdentityListInvitations,
|
ActionIAMIdentityListInvitations,
|
||||||
ActionIAMIdentityListSessions,
|
ActionIAMIdentityListSessions,
|
||||||
|
ActionIAMIdentityListPersonalAPIKeys,
|
||||||
).WithSID("list-own-associations").
|
).WithSID("list-own-associations").
|
||||||
When(policy.Equals("principal.id", "resource.id")),
|
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.
|
// IAMSelfManageSessionPolicy allows users to manage their own sessions.
|
||||||
var IAMSelfManageSessionPolicy = policy.NewPolicy(
|
var IAMSelfManageSessionPolicy = policy.NewPolicy(
|
||||||
@@ -79,6 +80,20 @@ var IAMSelfManageMembershipPolicy = policy.NewPolicy(
|
|||||||
When(policy.Equals("principal.id", "resource.user_id")),
|
When(policy.Equals("principal.id", "resource.user_id")),
|
||||||
).WithDescription("Allows users to view their organization memberships")
|
).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.
|
// IAMOwnerPolicy defines permissions for organization owners.
|
||||||
var IAMOwnerPolicy = policy.NewPolicy(
|
var IAMOwnerPolicy = policy.NewPolicy(
|
||||||
"iam:owner",
|
"iam:owner",
|
||||||
@@ -92,6 +107,8 @@ var IAMOwnerPolicy = policy.NewPolicy(
|
|||||||
ActionIAMInvitationGet,
|
ActionIAMInvitationGet,
|
||||||
ActionIAMInvitationDelete,
|
ActionIAMInvitationDelete,
|
||||||
).WithSID("manage-invitations"),
|
).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")
|
).WithDescription("Full IAM access for organization owners")
|
||||||
|
|
||||||
// IAMAdminPolicy defines permissions for organization admins.
|
// IAMAdminPolicy defines permissions for organization admins.
|
||||||
@@ -116,11 +133,22 @@ var IAMAdminPolicy = policy.NewPolicy(
|
|||||||
ActionIAMInvitationGet,
|
ActionIAMInvitationGet,
|
||||||
ActionIAMInvitationDelete,
|
ActionIAMInvitationDelete,
|
||||||
).WithSID("invitation-admin-access"),
|
).WithSID("invitation-admin-access"),
|
||||||
|
// Can view SAML configurations
|
||||||
|
policy.Allow(
|
||||||
|
ActionIAMSAMLConfigurationGet,
|
||||||
|
ActionIAMSAMLConfigurationList,
|
||||||
|
).WithSID("saml-viewer-access"),
|
||||||
// Cannot delete organization
|
// Cannot delete organization
|
||||||
policy.Deny(ActionIAMOrganizationDelete).WithSID("deny-org-delete"),
|
policy.Deny(ActionIAMOrganizationDelete).WithSID("deny-org-delete"),
|
||||||
// Cannot remove members (only owner can)
|
// Cannot remove members (only owner can)
|
||||||
policy.Deny(ActionIAMOrganizationRemoveMember).WithSID("deny-remove-member"),
|
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.
|
// IAMViewerPolicy defines permissions for organization viewers.
|
||||||
var IAMViewerPolicy = policy.NewPolicy(
|
var IAMViewerPolicy = policy.NewPolicy(
|
||||||
|
|||||||
@@ -12,9 +12,25 @@
|
|||||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
// LEGACY: This is the legacy access management service that is used to authorize actions on entities.
|
// LEGACY PERMISSION SYSTEM - DEPRECATED
|
||||||
// It is deprecated and will be removed in the future.
|
//
|
||||||
// Use the Authorizer instead.
|
// 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
|
package iam
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -67,5 +67,6 @@ func IAMPolicySet() *PolicySet {
|
|||||||
IAMSelfManageSessionPolicy,
|
IAMSelfManageSessionPolicy,
|
||||||
IAMSelfManageInvitationPolicy,
|
IAMSelfManageInvitationPolicy,
|
||||||
IAMSelfManageMembershipPolicy,
|
IAMSelfManageMembershipPolicy,
|
||||||
|
IAMSelfManagePersonalAPIKeyPolicy,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ type (
|
|||||||
Certificate *x509.Certificate
|
Certificate *x509.Certificate
|
||||||
PrivateKey *rsa.PrivateKey
|
PrivateKey *rsa.PrivateKey
|
||||||
Logger *log.Logger
|
Logger *log.Logger
|
||||||
PolicySet *PolicySet
|
|
||||||
TracerProvider trace.TracerProvider
|
TracerProvider trace.TracerProvider
|
||||||
DomainVerificationInterval time.Duration
|
DomainVerificationInterval time.Duration
|
||||||
DomainVerificationResolverAddr string
|
DomainVerificationResolverAddr string
|
||||||
@@ -113,15 +112,8 @@ func NewService(
|
|||||||
svc.APIKeyService = NewAPIKeyService(svc)
|
svc.APIKeyService = NewAPIKeyService(svc)
|
||||||
svc.LegacyAccessManagementService = NewAccessManagementService(svc)
|
svc.LegacyAccessManagementService = NewAccessManagementService(svc)
|
||||||
|
|
||||||
// Use provided PolicySet or default to IAM-only policies
|
svc.Authorizer = NewAuthorizer(pgClient)
|
||||||
policySet := NewPolicySet()
|
svc.Authorizer.RegisterPolicySet(IAMPolicySet())
|
||||||
if cfg.PolicySet != nil {
|
|
||||||
policySet = cfg.PolicySet
|
|
||||||
}
|
|
||||||
|
|
||||||
policySet.Merge(IAMPolicySet())
|
|
||||||
|
|
||||||
svc.Authorizer = NewAuthorizer(pgClient, policySet)
|
|
||||||
|
|
||||||
samlService, err := saml.NewService(svc.pg, svc.encryptionKey, svc.baseURL, svc.certificate, svc.privateKey, cfg.Logger)
|
samlService, err := saml.NewService(svc.pg, svc.encryptionKey, svc.baseURL, svc.certificate, svc.privateKey, cfg.Logger)
|
||||||
if err != nil {
|
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/filevalidation"
|
||||||
"go.probo.inc/probo/pkg/gid"
|
"go.probo.inc/probo/pkg/gid"
|
||||||
"go.probo.inc/probo/pkg/html2pdf"
|
"go.probo.inc/probo/pkg/html2pdf"
|
||||||
|
"go.probo.inc/probo/pkg/iam"
|
||||||
"go.probo.inc/probo/pkg/mail"
|
"go.probo.inc/probo/pkg/mail"
|
||||||
"go.probo.inc/probo/pkg/slack"
|
"go.probo.inc/probo/pkg/slack"
|
||||||
)
|
)
|
||||||
@@ -135,11 +136,14 @@ func NewService(
|
|||||||
fileManagerService *filemanager.Service,
|
fileManagerService *filemanager.Service,
|
||||||
logger *log.Logger,
|
logger *log.Logger,
|
||||||
slackService *slack.Service,
|
slackService *slack.Service,
|
||||||
|
iamService *iam.Service,
|
||||||
) (*Service, error) {
|
) (*Service, error) {
|
||||||
if bucket == "" {
|
if bucket == "" {
|
||||||
return nil, fmt.Errorf("bucket is required")
|
return nil, fmt.Errorf("bucket is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iamService.Authorizer.RegisterPolicySet(ProboPolicySet())
|
||||||
|
|
||||||
svc := &Service{
|
svc := &Service{
|
||||||
pg: pgClient,
|
pg: pgClient,
|
||||||
s3: s3Client,
|
s3: s3Client,
|
||||||
|
|||||||
@@ -388,6 +388,7 @@ func (impl *Implm) Run(
|
|||||||
fileManagerService,
|
fileManagerService,
|
||||||
l.Named("probo"),
|
l.Named("probo"),
|
||||||
slackService,
|
slackService,
|
||||||
|
iamService,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot create probo service: %w", err)
|
return fmt.Errorf("cannot create probo service: %w", err)
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ type Membership implements Node {
|
|||||||
profile: MembershipProfile @goField(forceResolver: true) @isViewer
|
profile: MembershipProfile @goField(forceResolver: true) @isViewer
|
||||||
organization: Organization @goField(forceResolver: true)
|
organization: Organization @goField(forceResolver: true)
|
||||||
role: MembershipRole!
|
role: MembershipRole!
|
||||||
permissions: [Permission!]!
|
permissions: [Permission!] @goField(forceResolver: true)
|
||||||
|
|
||||||
lastSession: Session @goField(forceResolver: true) @isViewer
|
lastSession: Session @goField(forceResolver: true) @isViewer
|
||||||
}
|
}
|
||||||
@@ -276,11 +276,6 @@ type Permission implements Node {
|
|||||||
principalId: ID!
|
principalId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
type PermissionGrant {
|
|
||||||
application: Application!
|
|
||||||
accessLevel: AccessLevel!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Application {
|
type Application {
|
||||||
id: ApplicationId!
|
id: ApplicationId!
|
||||||
name: String!
|
name: String!
|
||||||
@@ -303,7 +298,6 @@ type SAMLConfiguration implements Node {
|
|||||||
spMetadataUrl: String!
|
spMetadataUrl: String!
|
||||||
testLoginUrl: String!
|
testLoginUrl: String!
|
||||||
attributeMappings: SAMLAttributeMappings!
|
attributeMappings: SAMLAttributeMappings!
|
||||||
defaultPermissions: [PermissionGrant!]!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SAMLAttributeMappings {
|
type SAMLAttributeMappings {
|
||||||
|
|||||||
@@ -267,11 +267,6 @@ type ComplexityRoot struct {
|
|||||||
PrincipalType func(childComplexity int) int
|
PrincipalType func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
PermissionGrant struct {
|
|
||||||
AccessLevel func(childComplexity int) int
|
|
||||||
Application func(childComplexity int) int
|
|
||||||
}
|
|
||||||
|
|
||||||
PersonalAPIKey struct {
|
PersonalAPIKey struct {
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
ExpiresAt func(childComplexity int) int
|
ExpiresAt func(childComplexity int) int
|
||||||
@@ -335,7 +330,6 @@ type ComplexityRoot struct {
|
|||||||
AttributeMappings func(childComplexity int) int
|
AttributeMappings func(childComplexity int) int
|
||||||
AutoSignupEnabled func(childComplexity int) int
|
AutoSignupEnabled func(childComplexity int) int
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
DefaultPermissions func(childComplexity int) int
|
|
||||||
DomainVerificationToken func(childComplexity int) int
|
DomainVerificationToken func(childComplexity int) int
|
||||||
DomainVerifiedAt func(childComplexity int) int
|
DomainVerifiedAt func(childComplexity int) int
|
||||||
EmailDomain 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)
|
Profile(ctx context.Context, obj *types.Membership) (*types.MembershipProfile, error)
|
||||||
Organization(ctx context.Context, obj *types.Membership) (*types.Organization, 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)
|
LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error)
|
||||||
}
|
}
|
||||||
type MembershipConnectionResolver interface {
|
type MembershipConnectionResolver interface {
|
||||||
@@ -1385,19 +1380,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
|||||||
|
|
||||||
return e.complexity.Permission.PrincipalType(childComplexity), true
|
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":
|
case "PersonalAPIKey.createdAt":
|
||||||
if e.complexity.PersonalAPIKey.CreatedAt == nil {
|
if e.complexity.PersonalAPIKey.CreatedAt == nil {
|
||||||
break
|
break
|
||||||
@@ -1593,12 +1575,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.SAMLConfiguration.CreatedAt(childComplexity), true
|
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":
|
case "SAMLConfiguration.domainVerificationToken":
|
||||||
if e.complexity.SAMLConfiguration.DomainVerificationToken == nil {
|
if e.complexity.SAMLConfiguration.DomainVerificationToken == nil {
|
||||||
break
|
break
|
||||||
@@ -2219,7 +2195,7 @@ type Membership implements Node {
|
|||||||
profile: MembershipProfile @goField(forceResolver: true) @isViewer
|
profile: MembershipProfile @goField(forceResolver: true) @isViewer
|
||||||
organization: Organization @goField(forceResolver: true)
|
organization: Organization @goField(forceResolver: true)
|
||||||
role: MembershipRole!
|
role: MembershipRole!
|
||||||
permissions: [Permission!]!
|
permissions: [Permission!] @goField(forceResolver: true)
|
||||||
|
|
||||||
lastSession: Session @goField(forceResolver: true) @isViewer
|
lastSession: Session @goField(forceResolver: true) @isViewer
|
||||||
}
|
}
|
||||||
@@ -2265,11 +2241,6 @@ type Permission implements Node {
|
|||||||
principalId: ID!
|
principalId: ID!
|
||||||
}
|
}
|
||||||
|
|
||||||
type PermissionGrant {
|
|
||||||
application: Application!
|
|
||||||
accessLevel: AccessLevel!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Application {
|
type Application {
|
||||||
id: ApplicationId!
|
id: ApplicationId!
|
||||||
name: String!
|
name: String!
|
||||||
@@ -2292,7 +2263,6 @@ type SAMLConfiguration implements Node {
|
|||||||
spMetadataUrl: String!
|
spMetadataUrl: String!
|
||||||
testLoginUrl: String!
|
testLoginUrl: String!
|
||||||
attributeMappings: SAMLAttributeMappings!
|
attributeMappings: SAMLAttributeMappings!
|
||||||
defaultPermissions: [PermissionGrant!]!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SAMLAttributeMappings {
|
type SAMLAttributeMappings {
|
||||||
@@ -5132,12 +5102,12 @@ func (ec *executionContext) _Membership_permissions(ctx context.Context, field g
|
|||||||
field,
|
field,
|
||||||
ec.fieldContext_Membership_permissions,
|
ec.fieldContext_Membership_permissions,
|
||||||
func(ctx context.Context) (any, error) {
|
func(ctx context.Context) (any, error) {
|
||||||
return obj.Permissions, nil
|
return ec.resolvers.Membership().Permissions(ctx, obj)
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
ec.marshalNPermission2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionᚄ,
|
ec.marshalOPermission2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconnectᚋv1ᚋtypesᚐPermissionᚄ,
|
||||||
true,
|
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5145,8 +5115,8 @@ func (ec *executionContext) fieldContext_Membership_permissions(_ context.Contex
|
|||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "Membership",
|
Object: "Membership",
|
||||||
Field: field,
|
Field: field,
|
||||||
IsMethod: false,
|
IsMethod: true,
|
||||||
IsResolver: false,
|
IsResolver: true,
|
||||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "id":
|
case "id":
|
||||||
@@ -8107,74 +8077,6 @@ func (ec *executionContext) fieldContext_Permission_principalId(_ context.Contex
|
|||||||
return fc, nil
|
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) {
|
func (ec *executionContext) _PersonalAPIKey_id(ctx context.Context, field graphql.CollectedField, obj *types.PersonalAPIKey) (ret graphql.Marshaler) {
|
||||||
return graphql.ResolveField(
|
return graphql.ResolveField(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -9623,41 +9525,6 @@ func (ec *executionContext) fieldContext_SAMLConfiguration_attributeMappings(_ c
|
|||||||
return fc, nil
|
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) {
|
func (ec *executionContext) _SAMLConfigurationConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.SAMLConfigurationConnection) (ret graphql.Marshaler) {
|
||||||
return graphql.ResolveField(
|
return graphql.ResolveField(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -9813,8 +9680,6 @@ func (ec *executionContext) fieldContext_SAMLConfigurationEdge_node(_ context.Co
|
|||||||
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||||
case "attributeMappings":
|
case "attributeMappings":
|
||||||
return ec.fieldContext_SAMLConfiguration_attributeMappings(ctx, field)
|
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)
|
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)
|
return ec.fieldContext_SAMLConfiguration_testLoginUrl(ctx, field)
|
||||||
case "attributeMappings":
|
case "attributeMappings":
|
||||||
return ec.fieldContext_SAMLConfiguration_attributeMappings(ctx, field)
|
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)
|
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)
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
case "permissions":
|
case "permissions":
|
||||||
out.Values[i] = ec._Membership_permissions(ctx, field, obj)
|
field := field
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
atomic.AddUint32(&out.Invalids, 1)
|
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":
|
case "lastSession":
|
||||||
field := field
|
field := field
|
||||||
|
|
||||||
@@ -15387,50 +15278,6 @@ func (ec *executionContext) _Permission(ctx context.Context, sel ast.SelectionSe
|
|||||||
return out
|
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"}
|
var personalAPIKeyImplementors = []string{"PersonalAPIKey", "Node"}
|
||||||
|
|
||||||
func (ec *executionContext) _PersonalAPIKey(ctx context.Context, sel ast.SelectionSet, obj *types.PersonalAPIKey) graphql.Marshaler {
|
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 {
|
if out.Values[i] == graphql.Null {
|
||||||
out.Invalids++
|
out.Invalids++
|
||||||
}
|
}
|
||||||
case "defaultPermissions":
|
|
||||||
out.Values[i] = ec._SAMLConfiguration_defaultPermissions(ctx, field, obj)
|
|
||||||
if out.Values[i] == graphql.Null {
|
|
||||||
out.Invalids++
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic("unknown field " + strconv.Quote(field.Name))
|
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)
|
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 {
|
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 v == nil {
|
||||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
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)
|
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 {
|
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 v == nil {
|
||||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
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)
|
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 {
|
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 {
|
if v == nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ type Membership struct {
|
|||||||
Profile *MembershipProfile `json:"profile,omitempty"`
|
Profile *MembershipProfile `json:"profile,omitempty"`
|
||||||
Organization *Organization `json:"organization,omitempty"`
|
Organization *Organization `json:"organization,omitempty"`
|
||||||
Role coredata.MembershipRole `json:"role"`
|
Role coredata.MembershipRole `json:"role"`
|
||||||
Permissions []*Permission `json:"permissions"`
|
Permissions []*Permission `json:"permissions,omitempty"`
|
||||||
LastSession *Session `json:"lastSession,omitempty"`
|
LastSession *Session `json:"lastSession,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,11 +278,6 @@ type Permission struct {
|
|||||||
func (Permission) IsNode() {}
|
func (Permission) IsNode() {}
|
||||||
func (this Permission) GetID() gid.GID { return this.ID }
|
func (this Permission) GetID() gid.GID { return this.ID }
|
||||||
|
|
||||||
type PermissionGrant struct {
|
|
||||||
Application *Application `json:"application"`
|
|
||||||
AccessLevel AccessLevel `json:"accessLevel"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PersonalAPIKey struct {
|
type PersonalAPIKey struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -378,7 +373,6 @@ type SAMLConfiguration struct {
|
|||||||
SpMetadataURL string `json:"spMetadataUrl"`
|
SpMetadataURL string `json:"spMetadataUrl"`
|
||||||
TestLoginURL string `json:"testLoginUrl"`
|
TestLoginURL string `json:"testLoginUrl"`
|
||||||
AttributeMappings *SAMLAttributeMappings `json:"attributeMappings"`
|
AttributeMappings *SAMLAttributeMappings `json:"attributeMappings"`
|
||||||
DefaultPermissions []*PermissionGrant `json:"defaultPermissions"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (SAMLConfiguration) IsNode() {}
|
func (SAMLConfiguration) IsNode() {}
|
||||||
|
|||||||
@@ -233,6 +233,11 @@ func (r *membershipResolver) Organization(ctx context.Context, obj *types.Member
|
|||||||
return types.NewOrganization(organization), nil
|
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.
|
// LastSession is the resolver for the lastSession field.
|
||||||
func (r *membershipResolver) LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error) {
|
func (r *membershipResolver) LastSession(ctx context.Context, obj *types.Membership) (*types.Session, error) {
|
||||||
session := SessionFromContext(ctx)
|
session := SessionFromContext(ctx)
|
||||||
|
|||||||
@@ -316,16 +316,23 @@ func GetTenantService(ctx context.Context, proboSvc *probo.Service, tenantID gid
|
|||||||
return proboSvc.WithTenant(tenantID)
|
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)
|
user := connect_v1.IdentityFromContext(ctx)
|
||||||
apiKey := connect_v1.APIKeyFromContext(ctx)
|
// apiKey := connect_v1.APIKeyFromContext(ctx)
|
||||||
|
|
||||||
var credentialID *gid.GID
|
// var credentialID *gid.GID
|
||||||
if apiKey != nil {
|
// if apiKey != nil {
|
||||||
credentialID = &apiKey.ID
|
// 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 {
|
if err != nil {
|
||||||
panic(err)
|
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
|
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
|
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
|
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
|
SectionTitle func(childComplexity int) int
|
||||||
Snapshots func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SnapshotOrderBy) 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
|
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)
|
TotalCount(ctx context.Context, obj *types.ContinualImprovementConnection) (int, error)
|
||||||
}
|
}
|
||||||
type ControlResolver interface {
|
type ControlResolver interface {
|
||||||
|
Organization(ctx context.Context, obj *types.Control) (*types.Organization, error)
|
||||||
|
|
||||||
Framework(ctx context.Context, obj *types.Control) (*types.Framework, 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)
|
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)
|
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
|
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":
|
case "Control.sectionTitle":
|
||||||
if e.complexity.Control.SectionTitle == nil {
|
if e.complexity.Control.SectionTitle == nil {
|
||||||
break
|
break
|
||||||
@@ -11762,6 +11771,7 @@ type Framework implements Node {
|
|||||||
|
|
||||||
type Control implements Node {
|
type Control implements Node {
|
||||||
id: ID!
|
id: ID!
|
||||||
|
organization: Organization @goField(forceResolver: true)
|
||||||
sectionTitle: String!
|
sectionTitle: String!
|
||||||
name: String!
|
name: String!
|
||||||
description: String
|
description: String
|
||||||
@@ -21218,6 +21228,109 @@ func (ec *executionContext) fieldContext_Control_id(_ context.Context, field gra
|
|||||||
return fc, nil
|
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) {
|
func (ec *executionContext) _Control_sectionTitle(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||||
return graphql.ResolveField(
|
return graphql.ResolveField(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -21951,6 +22064,8 @@ func (ec *executionContext) fieldContext_ControlEdge_node(_ context.Context, fie
|
|||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "id":
|
case "id":
|
||||||
return ec.fieldContext_Control_id(ctx, field)
|
return ec.fieldContext_Control_id(ctx, field)
|
||||||
|
case "organization":
|
||||||
|
return ec.fieldContext_Control_organization(ctx, field)
|
||||||
case "sectionTitle":
|
case "sectionTitle":
|
||||||
return ec.fieldContext_Control_sectionTitle(ctx, field)
|
return ec.fieldContext_Control_sectionTitle(ctx, field)
|
||||||
case "name":
|
case "name":
|
||||||
@@ -51012,6 +51127,8 @@ func (ec *executionContext) fieldContext_UpdateControlPayload_control(_ context.
|
|||||||
switch field.Name {
|
switch field.Name {
|
||||||
case "id":
|
case "id":
|
||||||
return ec.fieldContext_Control_id(ctx, field)
|
return ec.fieldContext_Control_id(ctx, field)
|
||||||
|
case "organization":
|
||||||
|
return ec.fieldContext_Control_organization(ctx, field)
|
||||||
case "sectionTitle":
|
case "sectionTitle":
|
||||||
return ec.fieldContext_Control_sectionTitle(ctx, field)
|
return ec.fieldContext_Control_sectionTitle(ctx, field)
|
||||||
case "name":
|
case "name":
|
||||||
@@ -67994,6 +68111,39 @@ func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet,
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
atomic.AddUint32(&out.Invalids, 1)
|
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":
|
case "sectionTitle":
|
||||||
out.Values[i] = ec._Control_sectionTitle(ctx, field, obj)
|
out.Values[i] = ec._Control_sectionTitle(ctx, field, obj)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ func NewControlEdge(control *coredata.Control, orderField coredata.ControlOrderF
|
|||||||
func NewControl(control *coredata.Control) *Control {
|
func NewControl(control *coredata.Control) *Control {
|
||||||
return &Control{
|
return &Control{
|
||||||
ID: control.ID,
|
ID: control.ID,
|
||||||
|
Organization: &Organization{
|
||||||
|
ID: control.OrganizationID,
|
||||||
|
},
|
||||||
Framework: &Framework{
|
Framework: &Framework{
|
||||||
ID: control.FrameworkID,
|
ID: control.FrameworkID,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ type ContinualImprovementFilter struct {
|
|||||||
|
|
||||||
type Control struct {
|
type Control struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
|
Organization *Organization `json:"organization,omitempty"`
|
||||||
SectionTitle string `json:"sectionTitle"`
|
SectionTitle string `json:"sectionTitle"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -30,8 +30,35 @@ func (r *Resolver) MustBeAuthorized(ctx context.Context, entityID gid.GID, actio
|
|||||||
credentialID = &apiKey.ID
|
credentialID = &apiKey.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
err := r.iamSvc.LegacyAccessManagementService.Authorize(ctx, user.ID, credentialID, entityID, action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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