UNPKG

@cerbos/core

Version:
1,439 lines (1,304 loc) 36 kB
/* eslint-disable @typescript-eslint/no-deprecated */ import type { AuditTrail as AuditTrailProtobuf, DecisionLogEntry as DecisionLogEntryProtobuf, DecisionLogEntry_CheckResources, DecisionLogEntry_PlanResources, MetaValues, Peer as PeerProtobuf, PolicySource as PolicySourceProtobuf, PolicySource_Blob, PolicySource_Database, PolicySource_Disk, PolicySource_EmbeddedPDP, PolicySource_Git, PolicySource_Hub, PolicySource_Hub_LocalBundle, } from "../protobuf/cerbos/audit/v1/audit"; import { PolicySource_Database_Driver } from "../protobuf/cerbos/audit/v1/audit"; import { Effect as EffectProtobuf } from "../protobuf/cerbos/effect/v1/effect"; import type { AuxData as AuxDataProtobuf, CheckInput as CheckInputProtobuf, CheckOutput as CheckOutputProtobuf, CheckOutput_ActionEffect, OutputEntry as OutputEntry, PlanResourcesFilter_Expression_Operand, PlanResourcesInput as PlanResourcesInputProtobuf, PlanResourcesInput_Resource, PlanResourcesOutput as PlanResourcesOutputProtobuf, Principal as PrincipalProtobuf, Resource as ResourceProtobuf, } from "../protobuf/cerbos/engine/v1/engine"; import { PlanResourcesFilter_Kind } from "../protobuf/cerbos/engine/v1/engine"; import type { Condition as ConditionProtobuf, Constants as ConstantsProtobuf, DerivedRoles as DerivedRolesProtobuf, ExportConstants as ExportConstantsProtobuf, ExportVariables as ExportVariablesProtobuf, Match as MatchProtobuf, Match_ExprList, Metadata, Output as OutputProtobuf, Policy as PolicyProtobuf, PrincipalPolicy as PrincipalPolicyProtobuf, PrincipalRule as PrincipalRuleProtobuf, PrincipalRule_Action, ResourcePolicy as ResourcePolicyProtobuf, ResourceRule as ResourceRuleProtobuf, RoleDef, RolePolicy as RolePolicyProtobuf, RoleRule as RoleRuleProtobuf, Schemas, Schemas_Schema, SourceAttributes as SourceAttributesProtobuf, Variables as VariablesProtobuf, } from "../protobuf/cerbos/policy/v1/policy"; import { ScopePermissions as ScopePermissionsProtobuf } from "../protobuf/cerbos/policy/v1/policy"; import type { CheckResourcesResponse as CheckResourcesResponseProtobuf, CheckResourcesResponse_ResultEntry, DeleteSchemaResponse, DisablePolicyResponse, EnablePolicyResponse, GetPolicyResponse, GetSchemaResponse, InspectPoliciesResponse as InspectPoliciesResponseProtobuf, InspectPoliciesResponse_Attribute, InspectPoliciesResponse_Constant, InspectPoliciesResponse_DerivedRole, InspectPoliciesResponse_Result, InspectPoliciesResponse_Variable, ListAuditLogEntriesResponse, ListPoliciesResponse as ListPoliciesResponseProtobuf, ListSchemasResponse as ListSchemasResponseProtobuf, PlanResourcesResponse as PlanResourcesResponseProtobuf, PlanResourcesResponse_Meta, } from "../protobuf/cerbos/response/v1/response"; import { InspectPoliciesResponse_Attribute_Kind, InspectPoliciesResponse_Constant_Kind, InspectPoliciesResponse_DerivedRole_Kind, InspectPoliciesResponse_Variable_Kind, } from "../protobuf/cerbos/response/v1/response"; import type { Schema as SchemaProtobuf, ValidationError as ValidationErrorProtobuf, } from "../protobuf/cerbos/schema/v1/schema"; import { ValidationError_Source } from "../protobuf/cerbos/schema/v1/schema"; import type { HealthCheckResponse as HealthCheckResponseProtobuf } from "../protobuf/grpc/health/v1/health"; import { HealthCheckResponse_ServingStatus } from "../protobuf/grpc/health/v1/health"; import type { AccessLogEntry, AuditTrail, CheckInput, CheckOutput, CheckOutputActionEffect, Condition, Constants, DecisionLogEntry, DecisionLogEntryCheckResources, DecisionLogEntryMethod, DecisionLogEntryPlanResources, DecodedAuxData, DeleteSchemasResponse, DerivedRoleDefinition, DerivedRoles, DisablePoliciesResponse, EnablePoliciesResponse, ExportConstants, ExportVariables, GetPoliciesResponse, GetSchemasResponse, HealthCheckResponse, InspectPoliciesResponse, InspectedAttribute, InspectedConstant, InspectedDerivedRole, InspectedPolicy, InspectedVariable, ListPoliciesResponse, ListSchemasResponse, LocalBundle, Match, Matches, Output, OutputResult, Peer, PlanExpressionOperand, PlanResourcesInput, PlanResourcesMetadata, PlanResourcesOutput, PlanResourcesOutputBase, PlanResourcesResponse, PlanResourcesResponseBase, Policy, PolicyBase, PolicyMetadata, PolicySource, PolicySourceBlob, PolicySourceDatabase, PolicySourceDisk, PolicySourceEmbeddedPDP, PolicySourceGit, PolicySourceHub, PolicySourceHubBase, PolicySourceHubDeployment, PolicySourceHubLabel, PolicySourceHubLocalBundle, PolicySourceHubPlayground, Principal, PrincipalPolicy, PrincipalRule, PrincipalRuleAction, Resource, ResourcePolicy, ResourceQuery, ResourceRule, RolePolicy, RoleRule, Schema, SchemaRef, SchemaRefs, SourceAttributes, ValidationError, Value, Variables, } from "../types/external"; import { CheckResourcesResponse, CheckResourcesResult, DatabaseDriver, Effect, InspectedAttributeKind, InspectedConstantKind, InspectedDerivedRoleKind, InspectedVariableKind, PlanExpression, PlanExpressionValue, PlanExpressionVariable, PlanKind, SchemaDefinition, ScopePermissions, ServiceStatus, ValidationErrorSource, } from "../types/external"; import type { OmitFromEach } from "../types/internal"; export function accessLogEntryFromProtobuf({ entry, }: ListAuditLogEntriesResponse): AccessLogEntry { requireOneOf("ListAuditLogEntriesResponse.entry", entry, "accessLogEntry"); const { callId, timestamp, peer, metadata, method, statusCode, oversized, policySource, } = entry.accessLogEntry; requireField("AccessLogEntry.timestamp", timestamp); requireField("AccessLogEntry.peer", peer); return { callId, timestamp, peer: peerFromProtobuf(peer), metadata: auditLogMetadataFromProtobuf(metadata), method, statusCode, oversized, policySource: policySource && policySourceFromProtobuf(policySource), }; } export function decisionLogEntryFromProtobuf({ entry, }: ListAuditLogEntriesResponse): DecisionLogEntry { requireOneOf("ListAuditLogEntriesResponse.entry", entry, "decisionLogEntry"); const { callId, timestamp, peer, metadata, auditTrail, method, oversized, policySource, } = entry.decisionLogEntry; requireField("DecisionLogEntry.timestamp", timestamp); requireField("DecisionLogEntry.peer", peer); return { callId, timestamp, peer: peerFromProtobuf(peer), metadata: auditLogMetadataFromProtobuf(metadata), auditTrail: auditTrailFromProtobuf(auditTrail), method: decisionLogEntryMethodFromProtobuf(method), oversized, policySource: policySource && policySourceFromProtobuf(policySource), }; } function peerFromProtobuf({ address, authInfo, userAgent, forwardedFor, }: PeerProtobuf): Peer { return { address, authInfo, userAgent, forwardedFor, }; } function auditLogMetadataFromProtobuf( metadata: Record<string, MetaValues>, ): Record<string, string[]> { return Object.fromEntries( Object.entries(metadata).map(([key, { values }]) => [key, values]), ); } function auditTrailFromProtobuf( { effectivePolicies }: AuditTrailProtobuf = { effectivePolicies: {} }, ): AuditTrail { return { effectivePolicies: Object.fromEntries( Object.entries(effectivePolicies).map(([policyId, sourceAttributes]) => [ policyId, sourceAttributesFromProtobuf(sourceAttributes), ]), ), }; } function policySourceFromProtobuf({ source, }: PolicySourceProtobuf): PolicySource { return transformOneOf("PolicySource.source", source, { blob: ({ blob }) => policySourceBlobFromProtobuf(blob), database: ({ database }) => policySourceDatabaseFromProtobuf(database), disk: ({ disk }) => policySourceDiskFromProtobuf(disk), embeddedPdp: ({ embeddedPdp }) => policySourceEmbeddedPDPFromProtobuf(embeddedPdp), git: ({ git }) => policySourceGitFromProtobuf(git), hub: ({ hub }) => policySourceHubFromProtobuf(hub), }); } function policySourceBlobFromProtobuf({ bucketUrl, prefix, }: PolicySource_Blob): PolicySourceBlob { return { kind: "blob", bucketUrl, prefix, }; } function policySourceDatabaseFromProtobuf({ driver, }: PolicySource_Database): PolicySourceDatabase { return { kind: "database", driver: translateEnum( "PolicySource.Database.Driver", PolicySource_Database_Driver, driver, { [PolicySource_Database_Driver.DRIVER_UNSPECIFIED]: unexpected, [PolicySource_Database_Driver.DRIVER_MYSQL]: DatabaseDriver.MYSQL, [PolicySource_Database_Driver.DRIVER_POSTGRES]: DatabaseDriver.POSTGRES, [PolicySource_Database_Driver.DRIVER_SQLITE3]: DatabaseDriver.SQLITE3, }, ), }; } function policySourceDiskFromProtobuf({ directory, }: PolicySource_Disk): PolicySourceDisk { return { kind: "disk", directory, }; } function policySourceEmbeddedPDPFromProtobuf({ url, commitHash, builtAt, }: PolicySource_EmbeddedPDP): PolicySourceEmbeddedPDP { return { kind: "embeddedPDP", url, commit: commitHash, builtAt, }; } function policySourceGitFromProtobuf({ repositoryUrl, branch, subdirectory, }: PolicySource_Git): PolicySourceGit { return { kind: "git", repositoryUrl, branch, subdirectory, }; } function policySourceHubFromProtobuf({ source, }: PolicySource_Hub): PolicySourceHub { return { kind: "hub", ...policySourceHubSourceFromProtobuf(source), }; } type OmitPolicySourceHubBase<T extends PolicySourceHub> = OmitFromEach< T, keyof PolicySourceHubBase >; function policySourceHubSourceFromProtobuf( source: PolicySource_Hub["source"], ): OmitPolicySourceHubBase<PolicySourceHub> { return transformOneOf("PolicySource.Hub.source", source, { label: ({ label }) => policySourceHubLabelFromProtobuf(label), deploymentId: ({ deploymentId }) => policySourceHubDeploymentFromProtobuf(deploymentId), playgroundId: ({ playgroundId }) => policySourceHubPlaygroundFromProtobuf(playgroundId), localBundle: ({ localBundle }) => policySourceHubLocalBundleFromProtobuf(localBundle), }); } function policySourceHubLabelFromProtobuf( label: string, ): OmitPolicySourceHubBase<PolicySourceHubLabel> { return { label }; } function policySourceHubDeploymentFromProtobuf( deploymentId: string, ): OmitPolicySourceHubBase<PolicySourceHubDeployment> { return { deploymentId }; } function policySourceHubPlaygroundFromProtobuf( playgroundId: string, ): OmitPolicySourceHubBase<PolicySourceHubPlayground> { return { playgroundId }; } function policySourceHubLocalBundleFromProtobuf( localBundle: PolicySource_Hub_LocalBundle, ): OmitPolicySourceHubBase<PolicySourceHubLocalBundle> { return { localBundle: localBundleFromProtobuf(localBundle) }; } function localBundleFromProtobuf({ path, }: PolicySource_Hub_LocalBundle): LocalBundle { return { path }; } function decisionLogEntryMethodFromProtobuf( method: DecisionLogEntryProtobuf["method"], ): DecisionLogEntryMethod { return transformOneOf("DecisionLogEntry.method", method, { checkResources: ({ checkResources }) => decisionLogEntryCheckResourcesFromProtobuf(checkResources), planResources: ({ planResources }) => decisionLogEntryPlanResourcesFromProtobuf(planResources), }); } function decisionLogEntryCheckResourcesFromProtobuf({ inputs, outputs, error, }: DecisionLogEntry_CheckResources): DecisionLogEntryCheckResources { return { name: "CheckResources", inputs: inputs.map(checkInputFromProtobuf), outputs: outputs.map(checkOutputFromProtobuf), error: error || undefined, }; } /** @internal */ export function checkInputFromProtobuf({ requestId, principal, resource, actions, auxData, }: CheckInputProtobuf): CheckInput { requireField("CheckInput.principal", principal); requireField("CheckInput.resource", resource); return { requestId, principal: principalFromProtobuf(principal), resource: resourceFromProtobuf(resource), actions, auxData: auxData && decodedAuxDataFromProtobuf(auxData), }; } function principalFromProtobuf({ id, roles, attr, policyVersion, scope, }: PrincipalProtobuf): Required<Omit<Principal, "attributes">> { return { id, roles, attr, policyVersion, scope, }; } function resourceFromProtobuf({ kind, id, attr, policyVersion, scope, }: ResourceProtobuf): Required<Omit<Resource, "attributes">> { return { kind, id, attr, policyVersion, scope, }; } function decodedAuxDataFromProtobuf({ jwt }: AuxDataProtobuf): DecodedAuxData { return { jwt }; } /** @internal */ export function checkOutputFromProtobuf({ requestId, resourceId, actions, effectiveDerivedRoles, validationErrors, outputs, }: CheckOutputProtobuf): CheckOutput { return { requestId, resourceId, actions: Object.fromEntries( Object.entries(actions).map(([action, effect]) => [ action, checkOutputActionEffectFromProtobuf(effect), ]), ), effectiveDerivedRoles, validationErrors: validationErrors.map(validationErrorFromProtobuf), outputs: outputs.map(outputResultFromProtobuf), }; } function checkOutputActionEffectFromProtobuf({ effect, policy, scope, }: CheckOutput_ActionEffect): CheckOutputActionEffect { return { effect: effectFromProtobuf(effect), policy, scope, }; } function decisionLogEntryPlanResourcesFromProtobuf({ input, output, error, }: DecisionLogEntry_PlanResources): DecisionLogEntryPlanResources { requireField("DecisionLogEntry.PlanResources.input", input); requireField("DecisionLogEntry.PlanResources.output", output); return { name: "PlanResources", input: planResourcesInputFromProtobuf(input), output: planResourcesOutputFromProtobuf(output), error: error || undefined, }; } function planResourcesInputFromProtobuf({ requestId, principal, resource, action, actions, auxData, }: PlanResourcesInputProtobuf): PlanResourcesInput { requireField("PlanResourcesInput.principal", principal); requireField("PlanResourcesInput.resource", resource); return { requestId, principal: principalFromProtobuf(principal), resource: resourceQueryFromProtobuf(resource), ...planResourcesActionsFromProtobuf({ action, actions }), auxData: auxData && decodedAuxDataFromProtobuf(auxData), }; } function resourceQueryFromProtobuf({ kind, attr, policyVersion, scope, }: PlanResourcesInput_Resource): Required<Omit<ResourceQuery, "attributes">> { return { kind, attr, policyVersion, scope, }; } function planResourcesOutputFromProtobuf({ requestId, filter, filterDebug, action, actions, policyVersion, scope, validationErrors, }: PlanResourcesOutputProtobuf): PlanResourcesOutput { const base: PlanResourcesOutputBase = { requestId, ...planResourcesActionsFromProtobuf({ action, actions }), policyVersion, scope, validationErrors: validationErrors.map(validationErrorFromProtobuf), }; requireField("PlanResourcesOutput.filter", filter); const kind = planKindFromProtobuf(filter.kind); if (kind !== PlanKind.CONDITIONAL) { return { ...base, kind }; } requireField("PlanResourcesFilter.condition", filter.condition); return { ...base, kind, condition: planOperandFromProtobuf(filter.condition), conditionString: filterDebug, }; } interface PlanResourcesActions { action: string; actions: string[]; } function planResourcesActionsFromProtobuf({ action, actions, }: PlanResourcesActions): PlanResourcesActions { return { action: actions.length === 1 ? actions[0]! : action, // eslint-disable-line @typescript-eslint/no-non-null-assertion actions: actions.length ? actions : [action], }; } export function checkResourcesResponseFromProtobuf({ cerbosCallId, requestId, results, }: CheckResourcesResponseProtobuf): CheckResourcesResponse { return new CheckResourcesResponse({ cerbosCallId, requestId, results: results.map(checkResourcesResultFromProtobuf), }); } function checkResourcesResultFromProtobuf({ resource, actions, validationErrors, meta, outputs, }: CheckResourcesResponse_ResultEntry): CheckResourcesResult { requireField("CheckResourcesResponse.ResultEntry.resource", resource); return new CheckResourcesResult({ resource, actions: actionsFromProtobuf(actions), validationErrors: validationErrors.map(validationErrorFromProtobuf), metadata: meta, outputs: outputs.map(outputResultFromProtobuf), }); } function actionsFromProtobuf( actions: Record<string, EffectProtobuf>, ): Record<string, Effect> { return Object.fromEntries( Object.entries(actions).map(([action, effect]) => [ action, effectFromProtobuf(effect), ]), ); } function effectFromProtobuf(effect: EffectProtobuf): Effect { return effect === EffectProtobuf.EFFECT_ALLOW ? Effect.ALLOW : Effect.DENY; } function validationErrorFromProtobuf({ path, message, source, }: ValidationErrorProtobuf): ValidationError { return { path, message, source: validationErrorSourceFromProtobuf(source), }; } function validationErrorSourceFromProtobuf( source: ValidationError_Source, ): ValidationErrorSource { return translateEnum( "ValidationError.Source", ValidationError_Source, source, { [ValidationError_Source.SOURCE_UNSPECIFIED]: unexpected, [ValidationError_Source.SOURCE_PRINCIPAL]: ValidationErrorSource.PRINCIPAL, [ValidationError_Source.SOURCE_RESOURCE]: ValidationErrorSource.RESOURCE, }, ); } function outputResultFromProtobuf({ src, val }: OutputEntry): OutputResult { return { source: src, value: val as Value | undefined, }; } export function deleteSchemasResponseFromProtobuf({ deletedSchemas, }: DeleteSchemaResponse): DeleteSchemasResponse { return { deletedSchemas }; } export function disablePoliciesResponseFromProtobuf({ disabledPolicies, }: DisablePolicyResponse): DisablePoliciesResponse { return { disabledPolicies }; } export function enablePoliciesResponseFromProtobuf({ enabledPolicies, }: EnablePolicyResponse): EnablePoliciesResponse { return { enabledPolicies }; } export function getPoliciesResponseFromProtobuf({ policies, }: GetPolicyResponse): GetPoliciesResponse { return { policies: policies.map(policyFromProtobuf) }; } export function healthCheckResponseFromProtobuf({ status, }: HealthCheckResponseProtobuf): HealthCheckResponse { return { status: status === HealthCheckResponse_ServingStatus.SERVING ? ServiceStatus.SERVING : ServiceStatus.NOT_SERVING, }; } /** @internal */ export function policyFromProtobuf({ apiVersion, description, disabled, metadata, variables, policyType, }: PolicyProtobuf): Policy { return { apiVersion, description, disabled, metadata: metadata && policyMetadataFromProtobuf(metadata), variables, ...policyTypeFromProtobuf(policyType), }; } function policyMetadataFromProtobuf({ annotations, hash, sourceAttributes, sourceFile, storeIdentifer, storeIdentifier, }: Metadata): PolicyMetadata { return { annotations, hash, sourceAttributes: sourceAttributesFromProtobuf(sourceAttributes), sourceFile, storeIdentifer: storeIdentifier || storeIdentifer, storeIdentifier: storeIdentifier || storeIdentifer, }; } function sourceAttributesFromProtobuf( { attributes }: SourceAttributesProtobuf = { attributes: {} }, ): SourceAttributes { return attributes; } type OmitPolicyBase<T extends Policy> = OmitFromEach<T, keyof PolicyBase>; function policyTypeFromProtobuf( policyType: PolicyProtobuf["policyType"], ): OmitPolicyBase<Policy> { return transformOneOf("Policy.policyType", policyType, { derivedRoles: ({ derivedRoles }) => derivedRolesFromProtobuf(derivedRoles), exportConstants: ({ exportConstants }) => exportConstantsFromProtobuf(exportConstants), exportVariables: ({ exportVariables }) => exportVariablesFromProtobuf(exportVariables), principalPolicy: ({ principalPolicy }) => principalPolicyFromProtobuf(principalPolicy), resourcePolicy: ({ resourcePolicy }) => resourcePolicyFromProtobuf(resourcePolicy), rolePolicy: ({ rolePolicy }) => rolePolicyFromProtobuf(rolePolicy), }); } function derivedRolesFromProtobuf({ name, definitions, constants, variables, }: DerivedRolesProtobuf): OmitPolicyBase<DerivedRoles> { return { derivedRoles: { name, definitions: definitions.map(derivedRoleDefinitionFromProtobuf), constants: constants && constantsFromProtobuf(constants), variables: variables && variablesFromProtobuf(variables), }, }; } function derivedRoleDefinitionFromProtobuf({ name, parentRoles, condition, }: RoleDef): DerivedRoleDefinition { return { name, parentRoles, condition: condition && conditionFromProtobuf(condition), }; } function conditionFromProtobuf({ condition }: ConditionProtobuf): Condition { requireOneOf("Condition.condition", condition, "match"); return { match: matchFromProtobuf(condition.match) }; } function matchFromProtobuf({ op }: MatchProtobuf): Match { return transformOneOf("Match.op", op, { all: ({ all }) => ({ all: matchesFromProtobuf(all) }), any: ({ any }) => ({ any: matchesFromProtobuf(any) }), none: ({ none }) => ({ none: matchesFromProtobuf(none) }), expr: ({ expr }) => ({ expr }), }); } function matchesFromProtobuf({ of }: Match_ExprList): Matches { return { of: of.map(matchFromProtobuf) }; } function constantsFromProtobuf({ import: imports, local, }: ConstantsProtobuf): Constants { return { import: imports, local, }; } function exportConstantsFromProtobuf({ name, definitions, }: ExportConstantsProtobuf): OmitPolicyBase<ExportConstants> { return { exportConstants: { name, definitions, }, }; } function variablesFromProtobuf({ import: imports, local, }: VariablesProtobuf): Variables { return { import: imports, local, }; } function exportVariablesFromProtobuf({ name, definitions, }: ExportVariablesProtobuf): OmitPolicyBase<ExportVariables> { return { exportVariables: { name, definitions, }, }; } function principalPolicyFromProtobuf({ principal, version, rules, scope, scopePermissions, constants, variables, }: PrincipalPolicyProtobuf): OmitPolicyBase<PrincipalPolicy> { return { principalPolicy: { principal, version, rules: rules.map(principalRuleFromProtobuf), scope, scopePermissions: scopePermissionsFromProtobuf(scopePermissions), constants: constants && constantsFromProtobuf(constants), variables: variables && variablesFromProtobuf(variables), }, }; } function principalRuleFromProtobuf({ resource, actions, }: PrincipalRuleProtobuf): PrincipalRule { return { resource, actions: actions.map(principalRuleActionFromProtobuf), }; } function principalRuleActionFromProtobuf({ action, effect, condition, name, output, }: PrincipalRule_Action): PrincipalRuleAction { return { action, effect: effectFromProtobuf(effect), condition: condition && conditionFromProtobuf(condition), name, output: output && outputFromProtobuf(output), }; } function outputFromProtobuf({ expr, when }: OutputProtobuf): Output { const output: Output = {}; if (expr) { output.expr = expr; } if (when) { output.when = when; } return output; } function resourcePolicyFromProtobuf({ resource, version, importDerivedRoles, rules, schemas, scope, scopePermissions, constants, variables, }: ResourcePolicyProtobuf): OmitPolicyBase<ResourcePolicy> { return { resourcePolicy: { resource, version, importDerivedRoles, rules: rules.map(resourceRuleFromProtobuf), schemas: schemas && schemaRefsFromProtobuf(schemas), scope, scopePermissions: scopePermissionsFromProtobuf(scopePermissions), constants: constants && constantsFromProtobuf(constants), variables: variables && variablesFromProtobuf(variables), }, }; } function resourceRuleFromProtobuf({ actions, effect, derivedRoles, roles, condition, name, output, }: ResourceRuleProtobuf): ResourceRule { return { actions, effect: effectFromProtobuf(effect), derivedRoles, roles, condition: condition && conditionFromProtobuf(condition), name, output: output && outputFromProtobuf(output), }; } function rolePolicyFromProtobuf({ policyType, parentRoles, scope, rules, }: RolePolicyProtobuf): RolePolicy { requireOneOf("RolePolicy.policyType", policyType, "role"); return { rolePolicy: { role: policyType.role, parentRoles: parentRoles, scope, rules: rules.map(roleRuleFromProtobuf), }, }; } function roleRuleFromProtobuf({ resource, allowActions, }: RoleRuleProtobuf): RoleRule { return { resource, allowActions, }; } function scopePermissionsFromProtobuf( scopePermissions: ScopePermissionsProtobuf, ): ScopePermissions | undefined { return translateEnum( "ScopePermissions", ScopePermissionsProtobuf, scopePermissions, { [ScopePermissionsProtobuf.SCOPE_PERMISSIONS_UNSPECIFIED]: undefined, [ScopePermissionsProtobuf.SCOPE_PERMISSIONS_OVERRIDE_PARENT]: ScopePermissions.OVERRIDE_PARENT, [ScopePermissionsProtobuf.SCOPE_PERMISSIONS_REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS]: ScopePermissions.REQUIRE_PARENTAL_CONSENT_FOR_ALLOWS, }, ); } function schemaRefsFromProtobuf({ principalSchema, resourceSchema, }: Schemas): SchemaRefs { return { principalSchema: principalSchema && schemaRefFromProtobuf(principalSchema), resourceSchema: resourceSchema && schemaRefFromProtobuf(resourceSchema), }; } function schemaRefFromProtobuf({ ref, ignoreWhen }: Schemas_Schema): SchemaRef { return { ref, ignoreWhen: ignoreWhen && { actions: ignoreWhen.actions }, }; } export function getSchemasResponseFromProtobuf({ schemas, }: GetSchemaResponse): GetSchemasResponse { return { schemas: schemas.map(schemaFromProtobuf) }; } function schemaFromProtobuf({ id, definition }: SchemaProtobuf): Schema { return { id, definition: new SchemaDefinition(definition), }; } export function inspectPoliciesResponseFromProtobuf({ results, }: InspectPoliciesResponseProtobuf): InspectPoliciesResponse { return { policies: Object.fromEntries( Object.entries(results).map(([id, result]) => [ id, inspectedPolicyFromProtobuf(result), ]), ), }; } function inspectedPolicyFromProtobuf({ policyId, actions, attributes, constants, derivedRoles, variables, }: InspectPoliciesResponse_Result): InspectedPolicy { return { id: policyId, actions, attributes: attributes.map(inspectedAttributeFromProtobuf), constants: constants.map(inspectedConstantFromProtobuf), derivedRoles: derivedRoles.map(inspectedDerivedRoleFromProtobuf), variables: variables.map(inspectedVariableFromProtobuf), }; } function inspectedAttributeFromProtobuf({ kind, name, }: InspectPoliciesResponse_Attribute): InspectedAttribute { return { kind: inspectedAttributeKindFromProtobuf(kind), name, }; } function inspectedAttributeKindFromProtobuf( kind: InspectPoliciesResponse_Attribute_Kind, ): InspectedAttributeKind { return translateEnum( "InspectPoliciesResponse.Attribute.Kind", InspectPoliciesResponse_Attribute_Kind, kind, { [InspectPoliciesResponse_Attribute_Kind.KIND_UNSPECIFIED]: unexpected, [InspectPoliciesResponse_Attribute_Kind.KIND_PRINCIPAL_ATTRIBUTE]: InspectedAttributeKind.PRINCIPAL, [InspectPoliciesResponse_Attribute_Kind.KIND_RESOURCE_ATTRIBUTE]: InspectedAttributeKind.RESOURCE, }, ); } function inspectedConstantFromProtobuf({ kind, name, value, source, used, }: InspectPoliciesResponse_Constant): InspectedConstant { return { kind: inspectedConstantKindFromProtobuf(kind), name, value: value as Value, source: source || undefined, used, }; } function inspectedConstantKindFromProtobuf( kind: InspectPoliciesResponse_Constant_Kind, ): InspectedConstantKind { return translateEnum( "InspectPoliciesResponse.Constant.Kind", InspectPoliciesResponse_Constant_Kind, kind, { [InspectPoliciesResponse_Constant_Kind.KIND_UNSPECIFIED]: unexpected, [InspectPoliciesResponse_Constant_Kind.KIND_EXPORTED]: InspectedConstantKind.EXPORTED, [InspectPoliciesResponse_Constant_Kind.KIND_IMPORTED]: InspectedConstantKind.IMPORTED, [InspectPoliciesResponse_Constant_Kind.KIND_LOCAL]: InspectedConstantKind.LOCAL, [InspectPoliciesResponse_Constant_Kind.KIND_UNDEFINED]: InspectedConstantKind.UNDEFINED, [InspectPoliciesResponse_Constant_Kind.KIND_UNKNOWN]: InspectedConstantKind.UNKNOWN, }, ); } function inspectedDerivedRoleFromProtobuf({ kind, name, source, }: InspectPoliciesResponse_DerivedRole): InspectedDerivedRole { return { kind: inspectedDerivedRoleKindFromProtobuf(kind), name, source: source || undefined, }; } function inspectedDerivedRoleKindFromProtobuf( kind: InspectPoliciesResponse_DerivedRole_Kind, ): InspectedDerivedRoleKind { return translateEnum( "InspectPoliciesResponse.DerivedRole.Kind", InspectPoliciesResponse_DerivedRole_Kind, kind, { [InspectPoliciesResponse_DerivedRole_Kind.KIND_UNSPECIFIED]: unexpected, [InspectPoliciesResponse_DerivedRole_Kind.KIND_EXPORTED]: InspectedDerivedRoleKind.EXPORTED, [InspectPoliciesResponse_DerivedRole_Kind.KIND_IMPORTED]: InspectedDerivedRoleKind.IMPORTED, [InspectPoliciesResponse_DerivedRole_Kind.KIND_UNDEFINED]: InspectedDerivedRoleKind.UNDEFINED, }, ); } function inspectedVariableFromProtobuf({ kind, name, value, source, used, }: InspectPoliciesResponse_Variable): InspectedVariable { return { kind: inspectedVariableKindFromProtobuf(kind), name, definition: value || undefined, source: source || undefined, used, }; } function inspectedVariableKindFromProtobuf( kind: InspectPoliciesResponse_Variable_Kind, ): InspectedVariableKind { return translateEnum( "InspectPoliciesResponse.Variable.Kind", InspectPoliciesResponse_Variable_Kind, kind, { [InspectPoliciesResponse_Variable_Kind.KIND_UNSPECIFIED]: unexpected, [InspectPoliciesResponse_Variable_Kind.KIND_EXPORTED]: InspectedVariableKind.EXPORTED, [InspectPoliciesResponse_Variable_Kind.KIND_IMPORTED]: InspectedVariableKind.IMPORTED, [InspectPoliciesResponse_Variable_Kind.KIND_LOCAL]: InspectedVariableKind.LOCAL, [InspectPoliciesResponse_Variable_Kind.KIND_UNDEFINED]: InspectedVariableKind.UNDEFINED, [InspectPoliciesResponse_Variable_Kind.KIND_UNKNOWN]: InspectedVariableKind.UNKNOWN, }, ); } export function listPoliciesResponseFromProtobuf({ policyIds, }: ListPoliciesResponseProtobuf): ListPoliciesResponse { return { ids: policyIds }; } export function listSchemasResponseFromProtobuf({ schemaIds, }: ListSchemasResponseProtobuf): ListSchemasResponse { return { ids: schemaIds }; } export function planResourcesResponseFromProtobuf({ cerbosCallId, requestId, filter, validationErrors, meta, }: PlanResourcesResponseProtobuf): PlanResourcesResponse { const base: PlanResourcesResponseBase = { cerbosCallId, requestId, validationErrors: validationErrors.map(validationErrorFromProtobuf), metadata: meta && planResourcesMetadataFromProtobuf(meta), }; requireField("PlanResourcesResponse.filter", filter); const kind = planKindFromProtobuf(filter.kind); if (kind !== PlanKind.CONDITIONAL) { return { ...base, kind }; } requireField("PlanResourcesFilter.condition", filter.condition); return { ...base, kind, condition: planOperandFromProtobuf(filter.condition), }; } function planKindFromProtobuf(kind: PlanResourcesFilter_Kind): PlanKind { return translateEnum( "PlanResourcesFilter.Kind", PlanResourcesFilter_Kind, kind, { [PlanResourcesFilter_Kind.KIND_UNSPECIFIED]: unexpected, [PlanResourcesFilter_Kind.KIND_ALWAYS_ALLOWED]: PlanKind.ALWAYS_ALLOWED, [PlanResourcesFilter_Kind.KIND_ALWAYS_DENIED]: PlanKind.ALWAYS_DENIED, [PlanResourcesFilter_Kind.KIND_CONDITIONAL]: PlanKind.CONDITIONAL, }, ); } function planOperandFromProtobuf({ node, }: PlanResourcesFilter_Expression_Operand): PlanExpressionOperand { return transformOneOf("PlanResourcesFilter.Expression.Operand.node", node, { expression: ({ expression }) => new PlanExpression( expression.operator, expression.operands.map(planOperandFromProtobuf), ), value: ({ value }) => new PlanExpressionValue((value ?? null) as Value), variable: ({ variable }) => new PlanExpressionVariable(variable), }); } function planResourcesMetadataFromProtobuf({ filterDebug, matchedScope, matchedScopes, }: PlanResourcesResponse_Meta): PlanResourcesMetadata { return { conditionString: filterDebug, matchedScope, matchedScopes, }; } /** @internal */ export const unexpected = Symbol("unexpected"); type Unexpected = typeof unexpected; function isUnexpected(value: unknown): value is Unexpected { return value === unexpected; } /** @internal */ export function translateEnum< Enum extends Record<string | number, number | string>, Result, >( descriptor: string, source: Enum, value: Enum[keyof Enum], translate: Record<Enum[keyof Enum], Result | Unexpected>, ): Result { if (value in translate) { const result = translate[value] as Result | Unexpected; if (!isUnexpected(result)) { return result; } } const wanted = Object.entries(source) .filter( ([, value]) => typeof value === "number" && !isUnexpected(translate[value as Enum[keyof Enum]]), ) .map(([key, value]) => `${key}:${value}`) .join("|"); const got = source[value] ? `${source[value]}:${value}` : value; throw new Error(`Unexpected ${descriptor}: wanted ${wanted}, got ${got}`); } function transformOneOf<OneOf extends { $case: string }, Result>( descriptor: string, oneOf: OneOf | undefined, transforms: { [Case in OneOf["$case"]]: | ((oneOf: Extract<OneOf, { $case: Case }>) => Result) | Unexpected; }, ): Result { requireField(descriptor, oneOf); const transform = transforms[oneOf.$case as OneOf["$case"]] as | ((oneOf: OneOf) => Result) | Unexpected | undefined; if (!transform || isUnexpected(transform)) { throw new Error( `Unexpected ${descriptor}: wanted ${Object.keys(transforms).join("|")}, got ${oneOf.$case}`, ); } return transform(oneOf); } function requireOneOf< OneOf extends { $case: string }, Case extends OneOf["$case"], >( descriptor: string, oneOf: OneOf | undefined, $case: Case, ): asserts oneOf is Extract<OneOf, { $case: Case }> { requireField(descriptor, oneOf); if (oneOf.$case !== $case) { throw new Error( `Unexpected ${descriptor}: wanted ${$case}, got ${oneOf.$case}`, ); } } /** @internal */ export function requireField<T>( descriptor: string, value: T | undefined, ): asserts value is T { if (value === undefined) { throw new Error(`Missing ${descriptor}`); } }