UNPKG

@cerbos/core

Version:
878 lines (798 loc) 20.1 kB
/* eslint-disable @typescript-eslint/no-deprecated */ import { fromJson } from "@bufbuild/protobuf"; import type { Duration, Value as ValueProtobuf } from "@bufbuild/protobuf/wkt"; import { ValueSchema, timestampFromDate } from "@bufbuild/protobuf/wkt"; import { v4 as uuidv4 } from "uuid"; import { Effect as EffectProtobuf } from "@cerbos/api/cerbos/effect/v1/effect_pb"; import type { PlanResourcesInput_ResourceValid, PrincipalValid, ResourceValid, } from "@cerbos/api/cerbos/engine/v1/engine_pb"; import type { ConditionValid, ConstantsValid, DerivedRolesValid, ExportConstantsValid, ExportVariablesValid, MatchValid, Match_ExprListValid, OutputValid, Output_WhenValid, PolicyValid, PrincipalPolicyValid, PrincipalRuleValid, PrincipalRule_ActionValid, ResourcePolicyValid, ResourceRuleValid, RoleDefValid, RolePolicyValid, RoleRuleValid, SchemasValid, Schemas_SchemaValid, VariablesValid, } from "@cerbos/api/cerbos/policy/v1/policy_pb"; import { ScopePermissions as ScopePermissionsProtobuf } from "@cerbos/api/cerbos/policy/v1/policy_pb"; import type { AddOrUpdatePolicyRequestValid, AddOrUpdateSchemaRequestValid, AuxDataValid, AuxData_JWTValid, CheckResourcesRequestValid, CheckResourcesRequest_ResourceEntryValid, DeleteSchemaRequestValid, DisablePolicyRequestValid, EnablePolicyRequestValid, GetPolicyRequestValid, GetSchemaRequestValid, InspectPoliciesRequestValid, ListAuditLogEntriesRequestValid, ListPoliciesRequestValid, PlanResourcesRequestValid, ReloadStoreRequestValid, } from "@cerbos/api/cerbos/request/v1/request_pb"; import { ListAuditLogEntriesRequest_Kind } from "@cerbos/api/cerbos/request/v1/request_pb"; import type { SchemaValid } from "@cerbos/api/cerbos/schema/v1/schema_pb"; import type { HealthCheckRequestValid } from "@cerbos/api/grpc/health/v1/health_pb"; import type { AddOrUpdatePoliciesRequest, AddOrUpdateSchemasRequest, AuditLogFilter, AuxData, CheckResourcesRequest, Condition, Constants, DeleteSchemasRequest, DerivedRoleDefinition, DerivedRoles, DisablePoliciesRequest, EnablePoliciesRequest, ExportConstants, ExportVariables, GetPoliciesRequest, GetSchemasRequest, HealthCheckRequest, InspectPoliciesRequest, JWT, ListAccessLogEntriesRequest, ListDecisionLogEntriesRequest, ListPoliciesRequest, Match, Matches, Output, OutputExpressions, PlanResourcesRequest, Policy, Principal, PrincipalPolicy, PrincipalRule, PrincipalRuleAction, ReloadStoreRequest, Resource, ResourceCheck, ResourcePolicy, ResourceQuery, ResourceRule, RolePolicy, RoleRule, SchemaDefinitionInput, SchemaInput, SchemaRef, SchemaRefs, Value, Variables, } from "../types/external.js"; import { Effect, SchemaDefinition, ScopePermissions, Service, auditLogFilterIsBetween, auditLogFilterIsSince, auditLogFilterIsTail, matchIsMatchAll, matchIsMatchAny, matchIsMatchExpr, matchIsMatchNone, policyIsDerivedRoles, policyIsExportConstants, policyIsExportVariables, policyIsPrincipalPolicy, policyIsResourcePolicy, policyIsRolePolicy, } from "../types/external.js"; const encoder = new TextEncoder(); export function addOrUpdatePoliciesRequestToProtobuf({ policies, }: AddOrUpdatePoliciesRequest): AddOrUpdatePolicyRequestValid { return { $typeName: "cerbos.request.v1.AddOrUpdatePolicyRequest", policies: policies.map(policyToProtobuf), }; } /** @internal */ export function policyToProtobuf(policy: Policy): PolicyValid { const { apiVersion = "api.cerbos.dev/v1", description = "", disabled = false, variables = {}, } = policy; return { $typeName: "cerbos.policy.v1.Policy", apiVersion, description, disabled, jsonSchema: "", policyType: policyTypeToProtobuf(policy), variables, }; } function policyTypeToProtobuf( policy: Policy, ): Exclude<PolicyValid["policyType"], undefined> { if (policyIsDerivedRoles(policy)) { return { case: "derivedRoles", value: derivedRolesToProtobuf(policy), }; } if (policyIsExportConstants(policy)) { return { case: "exportConstants", value: exportConstantsToProtobuf(policy), }; } if (policyIsExportVariables(policy)) { return { case: "exportVariables", value: exportVariablesToProtobuf(policy), }; } if (policyIsPrincipalPolicy(policy)) { return { case: "principalPolicy", value: principalPolicyToProtobuf(policy), }; } if (policyIsResourcePolicy(policy)) { return { case: "resourcePolicy", value: resourcePolicyToProtobuf(policy), }; } if (policyIsRolePolicy(policy)) { return { case: "rolePolicy", value: rolePolicyToProtobuf(policy), }; } throw new Error(`Unknown policy type: ${JSON.stringify(policy, null, 2)}`); } function derivedRolesToProtobuf({ derivedRoles: { name, definitions, constants, variables }, }: DerivedRoles): DerivedRolesValid { return { $typeName: "cerbos.policy.v1.DerivedRoles", name, definitions: definitions.map(derivedRoleDefinitionToProtobuf), constants: constants && constantsToProtobuf(constants), variables: variables && variablesToProtobuf(variables), }; } function derivedRoleDefinitionToProtobuf({ name, parentRoles, condition, }: DerivedRoleDefinition): RoleDefValid { return { $typeName: "cerbos.policy.v1.RoleDef", name, parentRoles, condition: condition && conditionToProtobuf(condition), }; } function conditionToProtobuf({ match }: Condition): ConditionValid { return { $typeName: "cerbos.policy.v1.Condition", condition: { case: "match", value: matchToProtobuf(match), }, }; } function matchToProtobuf(match: Match): MatchValid { if (matchIsMatchAll(match)) { return { $typeName: "cerbos.policy.v1.Match", op: { case: "all", value: matchesToProtobuf(match.all), }, }; } if (matchIsMatchAny(match)) { return { $typeName: "cerbos.policy.v1.Match", op: { case: "any", value: matchesToProtobuf(match.any), }, }; } if (matchIsMatchNone(match)) { return { $typeName: "cerbos.policy.v1.Match", op: { case: "none", value: matchesToProtobuf(match.none), }, }; } if (matchIsMatchExpr(match)) { return { $typeName: "cerbos.policy.v1.Match", op: { case: "expr", value: match.expr, }, }; } throw new Error(`Unknown match type: ${JSON.stringify(match, null, 2)}`); } function matchesToProtobuf({ of }: Matches): Match_ExprListValid { return { $typeName: "cerbos.policy.v1.Match.ExprList", of: of.map(matchToProtobuf), }; } function constantsToProtobuf({ import: imports = [], local = {}, }: Constants): ConstantsValid { return { $typeName: "cerbos.policy.v1.Constants", import: imports, local: valuesToProtobuf(local), }; } function exportConstantsToProtobuf({ exportConstants: { name, definitions }, }: ExportConstants): ExportConstantsValid { return { $typeName: "cerbos.policy.v1.ExportConstants", name, definitions: valuesToProtobuf(definitions), }; } function variablesToProtobuf({ import: imports = [], local = {}, }: Variables): VariablesValid { return { $typeName: "cerbos.policy.v1.Variables", import: imports, local, }; } function exportVariablesToProtobuf({ exportVariables: { name, definitions }, }: ExportVariables): ExportVariablesValid { return { $typeName: "cerbos.policy.v1.ExportVariables", name, definitions, }; } function principalPolicyToProtobuf({ principalPolicy: { principal, version, rules, scope = "", scopePermissions, constants, variables, }, }: PrincipalPolicy): PrincipalPolicyValid { return { $typeName: "cerbos.policy.v1.PrincipalPolicy", principal, version, rules: rules.map(principalRuleToProtobuf), scope, scopePermissions: scopePermissionsToProtobuf(scopePermissions), constants: constants && constantsToProtobuf(constants), variables: variables && variablesToProtobuf(variables), }; } function principalRuleToProtobuf({ resource, actions, }: PrincipalRule): PrincipalRuleValid { return { $typeName: "cerbos.policy.v1.PrincipalRule", resource, actions: actions.map(principalRuleActionToProtobuf), }; } function principalRuleActionToProtobuf({ action, effect, condition, name = "", output, }: PrincipalRuleAction): PrincipalRule_ActionValid { return { $typeName: "cerbos.policy.v1.PrincipalRule.Action", action, effect: effectToProtobuf(effect), condition: condition && conditionToProtobuf(condition), name, output: output && outputToProtobuf(output), }; } function scopePermissionsToProtobuf( scopePermissions: ScopePermissions | undefined, ): ScopePermissionsProtobuf { switch (scopePermissions) { case ScopePermissions.OVERRIDE_PARENT: return ScopePermissionsProtobuf.OVERRIDE_PARENT; case ScopePermissions.REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS: return ScopePermissionsProtobuf.REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS; default: return ScopePermissionsProtobuf.UNSPECIFIED; } } function effectToProtobuf(effect: Effect): EffectProtobuf { return effect === Effect.ALLOW ? EffectProtobuf.ALLOW : EffectProtobuf.DENY; } function outputToProtobuf({ expr = "", when }: Output): OutputValid { return { $typeName: "cerbos.policy.v1.Output", expr, when: when && outputExpressionsToProtobuf(when), }; } function outputExpressionsToProtobuf({ ruleActivated = "", conditionNotMet = "", }: OutputExpressions): Output_WhenValid { return { $typeName: "cerbos.policy.v1.Output.When", ruleActivated, conditionNotMet, }; } function resourcePolicyToProtobuf({ resourcePolicy: { resource, version, importDerivedRoles = [], rules, scope = "", scopePermissions, schemas, constants, variables, }, }: ResourcePolicy): ResourcePolicyValid { return { $typeName: "cerbos.policy.v1.ResourcePolicy", resource, version, importDerivedRoles, rules: rules.map(resourceRuleToProtobuf), scope, scopePermissions: scopePermissionsToProtobuf(scopePermissions), schemas: schemas && policySchemasToProtobuf(schemas), constants: constants && constantsToProtobuf(constants), variables: variables && variablesToProtobuf(variables), }; } function resourceRuleToProtobuf({ actions, effect, derivedRoles = [], roles = [], condition, name = "", output, }: ResourceRule): ResourceRuleValid { return { $typeName: "cerbos.policy.v1.ResourceRule", actions, effect: effectToProtobuf(effect), derivedRoles, roles, condition: condition && conditionToProtobuf(condition), name, output: output && outputToProtobuf(output), }; } function rolePolicyToProtobuf({ rolePolicy: { role, parentRoles, scope, rules }, }: RolePolicy): RolePolicyValid { return { $typeName: "cerbos.policy.v1.RolePolicy", policyType: { case: "role", value: role }, parentRoles: parentRoles ?? [], scope: scope ?? "", scopePermissions: ScopePermissionsProtobuf.UNSPECIFIED, rules: rules.map(roleRuleToProtobuf), }; } function roleRuleToProtobuf({ resource, allowActions, condition, }: RoleRule): RoleRuleValid { return { $typeName: "cerbos.policy.v1.RoleRule", resource, allowActions, condition: condition && conditionToProtobuf(condition), }; } function policySchemasToProtobuf({ principalSchema, resourceSchema, }: SchemaRefs): SchemasValid { return { $typeName: "cerbos.policy.v1.Schemas", principalSchema: principalSchema && policySchemaToProtobuf(principalSchema), resourceSchema: resourceSchema && policySchemaToProtobuf(resourceSchema), }; } function policySchemaToProtobuf({ ref, ignoreWhen, }: SchemaRef): Schemas_SchemaValid { return { $typeName: "cerbos.policy.v1.Schemas.Schema", ref, ignoreWhen: ignoreWhen && { $typeName: "cerbos.policy.v1.Schemas.IgnoreWhen", actions: ignoreWhen.actions, }, }; } export function addOrUpdateSchemasRequestToProtobuf({ schemas, }: AddOrUpdateSchemasRequest): AddOrUpdateSchemaRequestValid { return { $typeName: "cerbos.request.v1.AddOrUpdateSchemaRequest", schemas: schemas.map(schemaToProtobuf), }; } function schemaToProtobuf({ id, definition }: SchemaInput): SchemaValid { return { $typeName: "cerbos.schema.v1.Schema", id, definition: schemaDefinitionToProtobuf(definition), }; } function schemaDefinitionToProtobuf( definition: SchemaDefinitionInput, ): Uint8Array { if (definition instanceof Uint8Array) { return definition; } if (definition instanceof SchemaDefinition) { return definition.bytes; } if (typeof definition === "string") { return encoder.encode(definition); } return encoder.encode(JSON.stringify(definition)); } export function checkResourcesRequestToProtobuf({ principal, resources, auxData, includeMetadata = false, requestId = uuidv4(), }: CheckResourcesRequest): CheckResourcesRequestValid { return { $typeName: "cerbos.request.v1.CheckResourcesRequest", principal: principalToProtobuf(principal), resources: resources.map(resourceCheckToProtobuf), auxData: auxData && auxDataToProtobuf(auxData), includeMeta: includeMetadata, requestId, }; } function principalToProtobuf({ id, roles, attr = {}, attributes = {}, policyVersion = "", scope = "", }: Principal): PrincipalValid { return { $typeName: "cerbos.engine.v1.Principal", id, roles, attr: valuesToProtobuf({ ...attributes, ...attr, }), policyVersion, scope, }; } function resourceCheckToProtobuf({ resource, actions, }: ResourceCheck): CheckResourcesRequest_ResourceEntryValid { return { $typeName: "cerbos.request.v1.CheckResourcesRequest.ResourceEntry", resource: resourceToProtobuf(resource), actions, }; } function resourceToProtobuf({ kind, id, attr = {}, attributes = {}, policyVersion = "", scope = "", }: Resource): ResourceValid { return { $typeName: "cerbos.engine.v1.Resource", kind, id, attr: valuesToProtobuf({ ...attributes, ...attr, }), policyVersion, scope, }; } function auxDataToProtobuf({ jwt }: AuxData): AuxDataValid | undefined { if (!jwt) { return undefined; } return { $typeName: "cerbos.request.v1.AuxData", jwt: jwtToProtobuf(jwt), }; } function jwtToProtobuf({ token, keySetId = "" }: JWT): AuxData_JWTValid { return { $typeName: "cerbos.request.v1.AuxData.JWT", token, keySetId, }; } export function deleteSchemasRequestToProtobuf({ ids, }: DeleteSchemasRequest): DeleteSchemaRequestValid { return { $typeName: "cerbos.request.v1.DeleteSchemaRequest", id: ids, }; } export function disablePoliciesRequestToProtobuf({ ids, }: DisablePoliciesRequest): DisablePolicyRequestValid { return { $typeName: "cerbos.request.v1.DisablePolicyRequest", id: ids, }; } export function enablePoliciesRequestToProtobuf({ ids, }: EnablePoliciesRequest): EnablePolicyRequestValid { return { $typeName: "cerbos.request.v1.EnablePolicyRequest", id: ids, }; } export function getPoliciesRequestToProtobuf({ ids, }: GetPoliciesRequest): GetPolicyRequestValid { return { $typeName: "cerbos.request.v1.GetPolicyRequest", id: ids, }; } export function getSchemasRequestToProtobuf({ ids, }: GetSchemasRequest): GetSchemaRequestValid { return { $typeName: "cerbos.request.v1.GetSchemaRequest", id: ids, }; } export function healthCheckRequestToProtobuf({ service = Service.CERBOS, }: HealthCheckRequest): HealthCheckRequestValid { return { $typeName: "grpc.health.v1.HealthCheckRequest", service, }; } export function listAccessLogEntriesRequestToProtobuf({ filter, }: ListAccessLogEntriesRequest): ListAuditLogEntriesRequestValid { return { $typeName: "cerbos.request.v1.ListAuditLogEntriesRequest", kind: ListAuditLogEntriesRequest_Kind.ACCESS, filter: auditLogFilterToProtobuf(filter), }; } export function listDecisionLogEntriesRequestToProtobuf({ filter, }: ListDecisionLogEntriesRequest): ListAuditLogEntriesRequestValid { return { $typeName: "cerbos.request.v1.ListAuditLogEntriesRequest", kind: ListAuditLogEntriesRequest_Kind.DECISION, filter: auditLogFilterToProtobuf(filter), }; } function auditLogFilterToProtobuf( filter: AuditLogFilter, ): ListAuditLogEntriesRequestValid["filter"] { if (auditLogFilterIsBetween(filter)) { return { case: "between", value: { $typeName: "cerbos.request.v1.ListAuditLogEntriesRequest.TimeRange", start: timestampFromDate(filter.start), end: timestampFromDate(filter.end), }, }; } if (auditLogFilterIsSince(filter)) { return { case: "since", value: durationToProtobuf(filter.since), }; } if (auditLogFilterIsTail(filter)) { return { case: "tail", value: filter.tail, }; } return { case: undefined }; } function durationToProtobuf(duration: number): Duration { const [seconds, nanos] = duration.toFixed(9).split(".", 2) as [ string, string, ]; return { $typeName: "google.protobuf.Duration", seconds: BigInt(seconds), nanos: parseInt(nanos, 10), }; } export function inspectPoliciesRequestToProtobuf({ includeDisabled = false, ids = [], nameRegexp = "", scopeRegexp = "", versionRegexp = "", }: InspectPoliciesRequest): InspectPoliciesRequestValid { return { $typeName: "cerbos.request.v1.InspectPoliciesRequest", policyId: ids, includeDisabled, nameRegexp, scopeRegexp, versionRegexp, }; } export function listPoliciesRequestToProtobuf({ includeDisabled = false, ids = [], nameRegexp = "", scopeRegexp = "", versionRegexp = "", }: ListPoliciesRequest): ListPoliciesRequestValid { return { $typeName: "cerbos.request.v1.ListPoliciesRequest", policyId: ids, includeDisabled, nameRegexp, scopeRegexp, versionRegexp, }; } export function planResourcesRequestToProtobuf( request: PlanResourcesRequest, ): PlanResourcesRequestValid { const { principal, resource, auxData, includeMetadata = false, requestId = uuidv4(), } = request; return { $typeName: "cerbos.request.v1.PlanResourcesRequest", principal: principalToProtobuf(principal), resource: resourceQueryToProtobuf(resource), ...planResourcesActionsToProtobuf(request), auxData: auxData && auxDataToProtobuf(auxData), includeMeta: includeMetadata, requestId, }; } function planResourcesActionsToProtobuf(request: PlanResourcesRequest): { action: string; actions: string[]; } { if ("actions" in request) { return { action: "", actions: request.actions, }; } return { action: request.action, actions: [], }; } function resourceQueryToProtobuf({ kind, attr = {}, attributes = {}, policyVersion = "", scope = "", }: ResourceQuery): PlanResourcesInput_ResourceValid { return { $typeName: "cerbos.engine.v1.PlanResourcesInput.Resource", kind, attr: valuesToProtobuf({ ...attributes, ...attr, }), policyVersion, scope, }; } export function reloadStoreRequestToProtobuf({ wait, }: ReloadStoreRequest): ReloadStoreRequestValid { return { $typeName: "cerbos.request.v1.ReloadStoreRequest", wait, }; } /** @internal */ export function valuesToProtobuf( values: Record<string, Value>, ): Record<string, ValueProtobuf> { return Object.fromEntries( Object.entries(values).map(([key, value]) => [key, valueToProtobuf(value)]), ); } function valueToProtobuf(value: Value): ValueProtobuf { return fromJson(ValueSchema, value); }