Route audit-log and SCIM-event exports through export_jobs with typed arguments, an iam BuildAndUploadExport/SendExportEmail implementation, and a concurrent export-job worker with stale recovery. Stream JSONL via page.WalkAll into S3, and expose the request flow on console, connect, MCP, and CLI. Co-authored-by: Bryan Frimin <bryan@getprobo.com> Signed-off-by: Sacha Al Himdani <sacha@probo.com>
425 lines
14 KiB
Go
425 lines
14 KiB
Go
// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
// SOFTWARE.
|
|
|
|
package iam
|
|
|
|
import "go.probo.inc/probo/pkg/iam/policy"
|
|
|
|
// IAM Policies
|
|
//
|
|
// These policies define access control for the IAM service.
|
|
// They are applied in addition to role-based organization policies.
|
|
|
|
// IAMSelfManageIdentityPolicy allows users to manage their own identity.
|
|
// This is applied to all authenticated users regardless of organization membership.
|
|
var IAMSelfManageIdentityPolicy = policy.NewPolicy(
|
|
"iam:self-manage-identity",
|
|
"Self-Manage Identity",
|
|
|
|
// Users can view and update their own identity
|
|
policy.Allow(
|
|
ActionIdentityGet,
|
|
ActionIdentityUpdate,
|
|
ActionIdentityDelete,
|
|
).
|
|
WithSID("manage-own-identity").
|
|
When(policy.Equals("principal.id", "resource.identity_id")),
|
|
|
|
// Users can list their own memberships, invitations, sessions, and API keys
|
|
policy.Allow(
|
|
ActionMembershipList,
|
|
ActionInvitationList,
|
|
ActionSessionList,
|
|
ActionPersonalAPIKeyList,
|
|
ActionOAuth2AccessTokenList,
|
|
).
|
|
WithSID("list-own-associations").
|
|
When(policy.Equals("principal.id", "resource.identity_id")),
|
|
).
|
|
WithDescription("Allows users to manage their own identity, sessions, API keys, and view their memberships")
|
|
|
|
// IAMSelfManageSessionPolicy allows users to manage their own sessions.
|
|
var IAMSelfManageSessionPolicy = policy.NewPolicy(
|
|
"iam:self-manage-session",
|
|
"Self-Manage Sessions",
|
|
|
|
// Users can view and revoke their own sessions
|
|
policy.Allow(
|
|
ActionSessionGet,
|
|
ActionSessionRevoke,
|
|
ActionSessionRevokeAll,
|
|
).
|
|
WithSID("manage-own-sessions").
|
|
When(policy.Equals("principal.id", "resource.identity_id")),
|
|
).
|
|
WithDescription("Allows users to view and revoke their own sessions")
|
|
|
|
// IAMSelfManageInvitationPolicy allows users to manage invitations sent to them.
|
|
var IAMSelfManageInvitationPolicy = policy.NewPolicy(
|
|
"iam:self-manage-invitation",
|
|
"Self-Manage Invitations",
|
|
|
|
// Users can view and accept invitations sent to their email
|
|
policy.Allow(
|
|
ActionInvitationGet,
|
|
ActionInvitationAccept,
|
|
).
|
|
WithSID("manage-own-invitations").
|
|
When(policy.Equals("principal.email", "resource.email")),
|
|
).
|
|
WithDescription("Allows users to view and accept invitations sent to them")
|
|
|
|
// IAMSelfManageProfilePolicy allows users to view their own profiles.
|
|
var IAMSelfManageProfilePolicy = policy.NewPolicy(
|
|
"iam:self-manage-profile",
|
|
"Self-Manage Profiles",
|
|
|
|
// Users can view their own profiles
|
|
policy.Allow(
|
|
ActionMembershipProfileGet,
|
|
ActionMembershipProfileList,
|
|
).
|
|
WithSID("view-own-profiles").
|
|
When(policy.Equals("principal.id", "resource.identity_id")),
|
|
).
|
|
WithDescription("Allows users to view their organization profiles")
|
|
|
|
// IAMSelfManageMembershipPolicy allows users to view their own memberships.
|
|
var IAMSelfManageMembershipPolicy = policy.NewPolicy(
|
|
"iam:self-manage-membership",
|
|
"Self-Manage Memberships",
|
|
|
|
// Users can view their own memberships
|
|
policy.Allow(ActionMembershipGet).
|
|
WithSID("view-own-memberships").
|
|
When(policy.Equals("principal.id", "resource.identity_id")),
|
|
).
|
|
WithDescription("Allows users to view their organization memberships")
|
|
|
|
// IAMSelfManagePersonalAPIKeyPolicy allows users to manage their own API keys.
|
|
var IAMSelfManagePersonalAPIKeyPolicy = policy.NewPolicy(
|
|
"iam:self-manage-personal-api-key",
|
|
"Self-Manage Personal API Keys",
|
|
|
|
// Users can create, view, update, and delete their own API keys
|
|
policy.Allow(
|
|
ActionPersonalAPIKeyCreate,
|
|
ActionPersonalAPIKeyGet,
|
|
ActionPersonalAPIKeyUpdate,
|
|
ActionPersonalAPIKeyDelete,
|
|
).
|
|
WithSID("manage-own-api-keys").
|
|
When(policy.Equals("principal.id", "resource.identity_id")),
|
|
).
|
|
WithDescription("Allows users to manage their own personal API keys")
|
|
|
|
// IAMSelfManageOAuth2AccessTokenPolicy allows users to manage their own OAuth2 access tokens.
|
|
var IAMSelfManageOAuth2AccessTokenPolicy = policy.NewPolicy(
|
|
"iam:self-manage-oauth2-access-token",
|
|
"Self-Manage OAuth2 Access Tokens",
|
|
|
|
policy.Allow(
|
|
ActionOAuth2AccessTokenCreate,
|
|
ActionOAuth2AccessTokenGet,
|
|
ActionOAuth2AccessTokenDelete,
|
|
).
|
|
WithSID("manage-own-oauth2-access-tokens").
|
|
When(policy.Equals("principal.id", "resource.identity_id")),
|
|
).
|
|
WithDescription("Allows users to manage their own manually created OAuth2 access tokens")
|
|
|
|
// IAMSelfManageOAuth2ConsentPolicy allows users to manage their own OAuth2 consents.
|
|
var IAMSelfManageOAuth2ConsentPolicy = policy.NewPolicy(
|
|
"iam:self-manage-oauth2-consent",
|
|
"Self-Manage OAuth2 Consents",
|
|
|
|
policy.Allow(
|
|
ActionOAuth2ConsentGet,
|
|
ActionOAuth2ConsentApprove,
|
|
).
|
|
WithSID("manage-own-consents").
|
|
When(
|
|
policy.Equals("principal.id", "resource.identity_id"),
|
|
policy.Equals("principal.session_id", "resource.session_id"),
|
|
),
|
|
).
|
|
WithDescription("Allows users to view and approve their own OAuth2 consents")
|
|
|
|
// IAMOwnerPolicy defines permissions for organization owners.
|
|
var IAMOwnerPolicy = policy.NewPolicy(
|
|
"iam:owner",
|
|
"Organization Owner",
|
|
|
|
// Full access to organization management
|
|
policy.Allow("iam:organization:*").
|
|
WithSID("full-org-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Full access to member management (scoped to own organization), except deletion on SCIM sourced memberships
|
|
policy.Allow(
|
|
ActionMembershipGet,
|
|
ActionMembershipList,
|
|
ActionMembershipUpdate,
|
|
).
|
|
WithSID("membership-owner-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
policy.Allow(
|
|
ActionMembershipDelete,
|
|
).
|
|
WithSID("membership-deletion-owner-access").
|
|
When(
|
|
policy.Equals("principal.organization_id", "resource.organization_id"),
|
|
policy.NotEquals("resource.source", "SCIM"),
|
|
),
|
|
|
|
// Full access to membership profiles (scoped to own organization)
|
|
policy.Allow(
|
|
ActionMembershipProfileGet,
|
|
ActionMembershipProfileList,
|
|
ActionMembershipProfileCreate,
|
|
ActionMembershipProfileUpdate,
|
|
ActionMembershipProfileDelete,
|
|
ActionMembershipProfileActivate,
|
|
ActionMembershipProfileDeactivate,
|
|
).
|
|
WithSID("full-membership-profile-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can view identities of members in the same organization
|
|
policy.Allow(ActionIdentityGet).
|
|
WithSID("view-member-identity").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can manage invitations (scoped to own organization)
|
|
policy.Allow(
|
|
ActionInvitationList,
|
|
ActionInvitationCreate,
|
|
ActionInvitationGet,
|
|
ActionInvitationList,
|
|
ActionInvitationDelete,
|
|
).
|
|
WithSID("manage-invitations").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Full access to SAML configuration management (scoped to own organization)
|
|
policy.Allow("iam:saml-configuration:*").
|
|
WithSID("full-saml-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Full access to SCIM configuration management (scoped to own organization)
|
|
policy.Allow("iam:scim-configuration:*").
|
|
WithSID("full-scim-configuration-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Full access to SCIM event viewing (scoped to own organization)
|
|
policy.Allow("iam:scim-event:*").
|
|
WithSID("full-scim-event-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Full access to SCIM bridge management (scoped to own organization)
|
|
policy.Allow("iam:scim-bridge:*").
|
|
WithSID("full-scim-bridge-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Full access to audit log entries (scoped to own organization)
|
|
policy.Allow(
|
|
ActionAuditLogEntryGet,
|
|
ActionAuditLogEntryList,
|
|
ActionAuditLogExport,
|
|
).
|
|
WithSID("audit-log-entry-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
).
|
|
WithDescription("Full IAM access for organization owners")
|
|
|
|
// IAMAdminPolicy defines permissions for organization admins.
|
|
var IAMAdminPolicy = policy.NewPolicy(
|
|
"iam:admin",
|
|
"Organization Admin",
|
|
|
|
// Can view and update organization (but not delete)
|
|
policy.Allow(
|
|
ActionOrganizationGet,
|
|
ActionOrganizationUpdate,
|
|
ActionMembershipList,
|
|
ActionInvitationList,
|
|
ActionInvitationCreate,
|
|
).
|
|
WithSID("org-admin-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can manage memberships (scoped to own organization)
|
|
policy.Allow(
|
|
ActionMembershipGet,
|
|
).
|
|
WithSID("membership-admin-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can update memberships, but neither of an existing owner (resource.role)
|
|
// nor to grant ownership (resource.target_role); only owner can grant
|
|
// ownership.
|
|
policy.Allow(
|
|
ActionMembershipUpdate,
|
|
).
|
|
WithSID("membership-role-admin-access").
|
|
When(
|
|
policy.Equals("principal.organization_id", "resource.organization_id"),
|
|
policy.NotEquals("resource.role", "OWNER"),
|
|
policy.NotEquals("resource.target_role", "OWNER"),
|
|
),
|
|
|
|
// Can view and manage membership profiles (scoped to own organization)
|
|
policy.Allow(
|
|
ActionMembershipProfileGet,
|
|
ActionMembershipProfileList,
|
|
ActionMembershipProfileUpdate,
|
|
ActionMembershipProfileDelete,
|
|
ActionMembershipProfileActivate,
|
|
ActionMembershipProfileDeactivate,
|
|
).
|
|
WithSID("membership-profile-admin-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can create members, but not with the OWNER role (resource.target_role);
|
|
// only owner can grant ownership.
|
|
policy.Allow(ActionMembershipProfileCreate).
|
|
WithSID("membership-profile-admin-create").
|
|
When(
|
|
policy.Equals("principal.organization_id", "resource.organization_id"),
|
|
policy.NotEquals("resource.target_role", "OWNER"),
|
|
),
|
|
|
|
// Can view identities of members in the same organization
|
|
policy.Allow(ActionIdentityGet).
|
|
WithSID("view-member-identity").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can manage invitations (scoped to own organization)
|
|
policy.Allow(
|
|
ActionInvitationGet,
|
|
ActionInvitationDelete,
|
|
).
|
|
WithSID("invitation-admin-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can view SAML configurations (scoped to own organization)
|
|
policy.Allow(ActionSAMLConfigurationGet).
|
|
WithSID("saml-configuration-admin-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Cannot delete organization
|
|
policy.Deny(ActionOrganizationDelete).
|
|
WithSID("deny-org-delete"),
|
|
|
|
// Cannot remove members (only owner can)
|
|
policy.Deny(ActionMembershipDelete).
|
|
WithSID("deny-remove-member"),
|
|
|
|
// Cannot manage SAML configurations (only owner can)
|
|
policy.Deny(
|
|
ActionSAMLConfigurationCreate,
|
|
ActionSAMLConfigurationUpdate,
|
|
ActionSAMLConfigurationDelete,
|
|
).
|
|
WithSID("deny-saml-management"),
|
|
|
|
// Can view SCIM configuration, bridge, and events (scoped to own organization)
|
|
policy.Allow(
|
|
ActionSCIMConfigurationGet,
|
|
ActionSCIMBridgeGet,
|
|
ActionSCIMEventList,
|
|
ActionSCIMEventGet,
|
|
).
|
|
WithSID("scim-admin-view-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Cannot manage SCIM configurations or bridges (only owner can)
|
|
policy.Deny(
|
|
ActionSCIMConfigurationCreate,
|
|
ActionSCIMConfigurationUpdate,
|
|
ActionSCIMConfigurationDelete,
|
|
ActionSCIMBridgeCreate,
|
|
ActionSCIMBridgeUpdate,
|
|
ActionSCIMBridgeDelete,
|
|
).
|
|
WithSID("deny-scim-management"),
|
|
|
|
// Can view and export audit log entries (scoped to own organization)
|
|
policy.Allow(
|
|
ActionAuditLogEntryGet,
|
|
ActionAuditLogEntryList,
|
|
ActionAuditLogExport,
|
|
).
|
|
WithSID("audit-log-entry-admin-access").
|
|
When(
|
|
policy.Equals("principal.organization_id", "resource.organization_id"),
|
|
),
|
|
|
|
// Can export SCIM events (scoped to own organization)
|
|
policy.Allow(ActionSCIMEventExport).
|
|
WithSID("scim-event-export-admin-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
).
|
|
WithDescription("IAM admin access - can manage members but cannot delete organization or manage SAML/SCIM")
|
|
|
|
// IAMViewerPolicy defines permissions for organization viewers.
|
|
var IAMViewerPolicy = policy.NewPolicy(
|
|
"iam:viewer",
|
|
"Organization Viewer",
|
|
|
|
// Read-only access to organization
|
|
policy.Allow(
|
|
ActionOrganizationGet,
|
|
ActionMembershipList,
|
|
ActionInvitationList,
|
|
).
|
|
WithSID("org-viewer-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can view memberships (scoped to own organization)
|
|
policy.Allow(ActionMembershipGet).
|
|
WithSID("membership-viewer-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can view membership profiles (scoped to own organization)
|
|
policy.Allow(
|
|
ActionMembershipProfileGet,
|
|
ActionMembershipProfileList,
|
|
).
|
|
WithSID("membership-profile-viewer-access").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can view identities of members in the same organization
|
|
policy.Allow(ActionIdentityGet).
|
|
WithSID("view-member-identity").
|
|
When(policy.Equals("principal.organization_id", "resource.organization_id")),
|
|
|
|
// Can view audit log entries (scoped to own organization)
|
|
policy.Allow(
|
|
ActionAuditLogEntryGet,
|
|
ActionAuditLogEntryList,
|
|
).
|
|
WithSID("audit-log-entry-viewer-access").
|
|
When(
|
|
policy.Equals("principal.organization_id", "resource.organization_id"),
|
|
),
|
|
).
|
|
WithDescription("Read-only IAM access for organization viewers")
|