diff --git a/README.md b/README.md index c9d6b59..64a8501 100644 --- a/README.md +++ b/README.md @@ -451,7 +451,7 @@ authClient ### Methods ```typescript // Is -// (method) Authorizer.Is(params: IsRequest, options?: CallOptions): Promise +// (method) Authorizer.Is(params: IsRequest, options?: CallOptions): Promise await authClient .Is({ identityContext: { diff --git a/src/authorizer/index.ts b/src/authorizer/index.ts index 07c10ee..f5ab282 100644 --- a/src/authorizer/index.ts +++ b/src/authorizer/index.ts @@ -3,7 +3,9 @@ import { readFileSync } from "fs"; import { Authorizer as AuthorizerClient, DecisionTreeRequestSchema, + file_aserto_authorizer_v2_authorizer, IsRequestSchema, + IsResponse, QueryRequestSchema, } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/authorizer_pb"; import { @@ -11,7 +13,17 @@ import { IsRequest as IsRequest$, QueryRequest as QueryRequest$, } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/authorizer_pb"; -import { create, JsonObject } from "@bufbuild/protobuf"; +import { + create, + DescEnum, + DescExtension, + DescFile, + DescMessage, + DescService, + JsonObject, + Registry, +} from "@bufbuild/protobuf"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; import { CallOptions, Client, @@ -21,8 +33,14 @@ import { import { createGrpcTransport } from "@connectrpc/connect-node"; import { handleError, setHeader, traceMessage } from "../util/connect"; +import { TopazRegistry } from "../util/serializer"; import { DecisionTreeRequest, + file_aserto_authorizer_v2_api_decision_logs, + file_aserto_authorizer_v2_api_identity_context, + file_aserto_authorizer_v2_api_module, + file_aserto_authorizer_v2_api_policy_context, + file_aserto_authorizer_v2_api_policy_instance, IsRequest, ListPoliciesRequest, Module, @@ -30,6 +48,15 @@ import { } from "./types"; type AuthorizerConfig = { + additionalDescriptors?: ( + | DescEnum + | DescExtension + | DescFile + | DescMessage + | DescService + | Registry + )[]; +} & { authorizerServiceUrl?: string; tenantId?: string; authorizerApiKey?: string; @@ -47,6 +74,7 @@ type Path = { }; export class Authorizer { AuthClient: Client; + registry: TopazRegistry; constructor(config: AuthorizerConfig) { const baseServiceHeaders: Interceptor = (next) => async (req) => { config.token && setHeader(req, "authorization", `${config.token}`); @@ -79,9 +107,19 @@ export class Authorizer { }); this.AuthClient = createClient(AuthorizerClient, baseGrpcTransport); + this.registry = new TopazRegistry( + file_aserto_authorizer_v2_api_decision_logs, + file_aserto_authorizer_v2_api_identity_context, + file_aserto_authorizer_v2_api_module, + file_aserto_authorizer_v2_api_policy_instance, + file_aserto_authorizer_v2_api_policy_context, + file_aserto_authorizer_v2_authorizer, + file_google_protobuf_timestamp, + ...(config.additionalDescriptors || []), + ); } - async Is(params: IsRequest, options?: CallOptions): Promise { + async Is(params: IsRequest, options?: CallOptions): Promise { try { const request: IsRequest$ = create(IsRequestSchema, { ...params, @@ -90,10 +128,9 @@ export class Authorizer { instanceLabel: params.policyInstance?.name, }, }); - const response = await this.AuthClient.is(request, options); - const allowed = response.decisions[0]?.is; - return !!allowed; + const response = await this.AuthClient.is(request, options); + return this.registry.serializeResponse(response); } catch (error) { throw handleError(error, "Is"); } diff --git a/src/authorizer/types.ts b/src/authorizer/types.ts index a876689..55a7e06 100644 --- a/src/authorizer/types.ts +++ b/src/authorizer/types.ts @@ -1,4 +1,5 @@ import { IdentityContext as IdentityContext$ } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/identity_context_pb"; +import { Module as Module$ } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/module_pb"; import { PolicyContext as PolicyContext$ } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/policy_context_pb"; import { PolicyInstance as PolicyInstance$ } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/policy_instance_pb"; import { @@ -20,12 +21,12 @@ export { DecisionPolicySchema, DecisionUser, DecisionUserSchema, + file_aserto_authorizer_v2_api_decision_logs, } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/decision_logs_pb"; export * from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/identity_context_pb"; export * from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/module_pb"; export * from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/policy_context_pb"; export * from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/policy_instance_pb"; -import { Module as Module$ } from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/api/module_pb"; export * from "@aserto/node-authorizer/src/gen/cjs/aserto/authorizer/v2/authorizer_pb"; export type DecisionTreeRequest = Omit< diff --git a/src/directory/index.ts b/src/directory/index.ts index 7bdd12a..4a8f92c 100644 --- a/src/directory/index.ts +++ b/src/directory/index.ts @@ -3,13 +3,16 @@ import { readFileSync } from "fs"; import { Exporter, ExportRequestSchema, + file_aserto_directory_exporter_v3_exporter, } from "@aserto/node-directory/src/gen/cjs/aserto/directory/exporter/v3/exporter_pb"; import { + file_aserto_directory_importer_v3_importer, Importer, ImportRequest, ImportRequestSchema, } from "@aserto/node-directory/src/gen/cjs/aserto/directory/importer/v3/importer_pb"; import { + file_aserto_directory_model_v3_model, MetadataSchema, Model, SetManifestRequestSchema, @@ -22,6 +25,7 @@ import { import { CheckRequestSchema, ChecksRequestSchema, + file_aserto_directory_reader_v3_reader, GetGraphRequestSchema, GetObjectManyRequestSchema, GetObjectRequestSchema, @@ -32,11 +36,13 @@ import { import { DeleteObjectRequestSchema, DeleteRelationRequestSchema, + file_aserto_directory_writer_v3_writer, SetObjectRequestSchema, SetRelationRequestSchema, Writer, } from "@aserto/node-directory/src/gen/cjs/aserto/directory/writer/v3/writer_pb"; import { create, JsonObject, Message } from "@bufbuild/protobuf"; +import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; import { CallOptions, Client, @@ -53,6 +59,7 @@ import { setHeader, traceMessage, } from "../util/connect"; +import { TopazRegistry } from "../util/serializer"; import { nullExporterProxy, nullImporterProxy, @@ -60,7 +67,6 @@ import { nullReaderProxy, nullWriterProxy, } from "./null"; -import { DsRegistry } from "./serializer"; import { CheckRequest, CheckResponse, @@ -74,6 +80,7 @@ import { DirectoryConfig, ExportOptions, ExportResponse, + file_aserto_directory_common_v3_common, GetGraphRequest, GetGraphResponse, GetManifestRequest, @@ -137,7 +144,7 @@ export class Directory { ImporterClient: Client; ExporterClient: Client; ModelClient: Client; - registry: DsRegistry; + registry: TopazRegistry; CreateTransport: ( config: ServiceConfig | undefined, @@ -282,7 +289,16 @@ export class Directory { : nullModelProxy(); this.CreateTransport = createTransport; - this.registry = new DsRegistry(...(config.additionalDescriptors || [])); + this.registry = new TopazRegistry( + file_aserto_directory_common_v3_common, + file_aserto_directory_reader_v3_reader, + file_aserto_directory_writer_v3_writer, + file_aserto_directory_exporter_v3_exporter, + file_aserto_directory_importer_v3_importer, + file_aserto_directory_model_v3_model, + file_google_protobuf_timestamp, + ...(config.additionalDescriptors || []), + ); } async check( @@ -653,7 +669,15 @@ export async function readAsyncIterable( export async function serializeAsyncIterable( gen: AsyncIterable, ): Promise { - const registry = new DsRegistry(); + const registry = new TopazRegistry( + file_aserto_directory_common_v3_common, + file_aserto_directory_reader_v3_reader, + file_aserto_directory_writer_v3_writer, + file_aserto_directory_exporter_v3_exporter, + file_aserto_directory_importer_v3_importer, + file_aserto_directory_model_v3_model, + file_google_protobuf_timestamp, + ); const out: T[] = []; for await (const x of gen) { out.push(registry.serializeResponse(x)); diff --git a/src/directory/serializer.ts b/src/directory/serializer.ts deleted file mode 100644 index dc675a9..0000000 --- a/src/directory/serializer.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { - DescEnum, - DescExtension, - DescFile, - DescMessage, - DescService, - Registry, -} from "@bufbuild/protobuf"; - -import { file_aserto_directory_common_v3_common } from "@aserto/node-directory/src/gen/cjs/aserto/directory/common/v3/common_pb"; -import { file_aserto_directory_exporter_v3_exporter } from "@aserto/node-directory/src/gen/cjs/aserto/directory/exporter/v3/exporter_pb"; -import { file_aserto_directory_importer_v3_importer } from "@aserto/node-directory/src/gen/cjs/aserto/directory/importer/v3/importer_pb"; -import { file_aserto_directory_model_v3_model } from "@aserto/node-directory/src/gen/cjs/aserto/directory/model/v3/model_pb"; -import { file_aserto_directory_reader_v3_reader } from "@aserto/node-directory/src/gen/cjs/aserto/directory/reader/v3/reader_pb"; -import { file_aserto_directory_writer_v3_writer } from "@aserto/node-directory/src/gen/cjs/aserto/directory/writer/v3/writer_pb"; -import { - createRegistry, - Message, - MessageShape, - toJson, -} from "@bufbuild/protobuf"; -import { GenMessage } from "@bufbuild/protobuf/codegenv1"; -import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt"; - -import { InvalidSchemaError } from "../util/errors"; - -class DsRegistry { - registry: Registry; - - constructor( - ...input: ( - | DescEnum - | DescExtension - | DescFile - | DescMessage - | DescService - | Registry - )[] - ) { - this.registry = createRegistry( - file_aserto_directory_common_v3_common, - file_aserto_directory_reader_v3_reader, - file_aserto_directory_writer_v3_writer, - file_aserto_directory_exporter_v3_exporter, - file_aserto_directory_importer_v3_importer, - file_aserto_directory_model_v3_model, - file_google_protobuf_timestamp, - ...input, - ); - } - - serializeResponse( - response: MessageShape>, - ): T { - const schema = this.registry.getMessage(response.$typeName); - if (!schema) { - throw new InvalidSchemaError( - `schema not registered for type: [${response.$typeName}]`, - ); - } - return toJson(schema, response, { - alwaysEmitImplicit: true, - registry: this.registry, - }) as unknown as T; - } -} - -export { DsRegistry }; diff --git a/src/index.ts b/src/index.ts index e8ee651..444f9ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,7 +6,6 @@ import { readAsyncIterable, serializeAsyncIterable, } from "./directory"; -import { DsRegistry } from "./directory/serializer"; import { Opcode } from "./directory/types"; import { handleError } from "./util/connect"; import { @@ -15,12 +14,12 @@ import { LOG_LEVELS, setLogEventEmitter, } from "./util/log"; +import { TopazRegistry } from "./util/serializer"; export { Authorizer, createImportRequest, Directory, - DsRegistry, getLogEventEmitter, handleError, ImportMsgCase, @@ -30,6 +29,7 @@ export { readAsyncIterable, serializeAsyncIterable, setLogEventEmitter, + TopazRegistry, }; export * from "./authorizer/types"; diff --git a/src/util/serializer.ts b/src/util/serializer.ts new file mode 100644 index 0000000..3db9ab3 --- /dev/null +++ b/src/util/serializer.ts @@ -0,0 +1,49 @@ +import { + createRegistry, + DescEnum, + DescExtension, + DescFile, + DescMessage, + DescService, + Message, + MessageShape, + Registry, + toJson, +} from "@bufbuild/protobuf"; +import { GenMessage } from "@bufbuild/protobuf/codegenv1"; + +import { InvalidSchemaError } from "../util/errors"; + +class TopazRegistry { + registry: Registry; + + constructor( + ...input: ( + | DescEnum + | DescExtension + | DescFile + | DescMessage + | DescService + | Registry + )[] + ) { + this.registry = createRegistry(...input); + } + + serializeResponse( + response: MessageShape>, + ): T { + const schema = this.registry.getMessage(response.$typeName); + if (!schema) { + throw new InvalidSchemaError( + `schema not registered for type: [${response.$typeName}]`, + ); + } + return toJson(schema, response, { + alwaysEmitImplicit: true, + registry: this.registry, + }) as unknown as T; + } +} + +export { TopazRegistry }; diff --git a/tests/authorizer/index.test.ts b/tests/authorizer/index.test.ts index 43c78a3..0826b5d 100644 --- a/tests/authorizer/index.test.ts +++ b/tests/authorizer/index.test.ts @@ -59,7 +59,7 @@ describe("Is", () => { options, ); - expect(result).toBe(true); + expect(result).toEqual({ decisions: [{ decision: "", is: true }] }); mock.mockReset(); }); @@ -103,7 +103,7 @@ describe("Is", () => { undefined, ); - expect(result).toBe(true); + expect(result).toEqual({ decisions: [{ decision: "", is: true }] }); mock.mockReset(); }); @@ -146,7 +146,7 @@ describe("Is", () => { }, undefined, ); - expect(result).toBe(true); + expect(result).toEqual({ decisions: [{ decision: "", is: true }] }); mock.mockReset(); }); @@ -187,7 +187,7 @@ describe("Is", () => { undefined, ); - expect(result).toBe(false); + expect(result).toEqual({ decisions: [{ decision: "", is: false }] }); mock.mockReset(); }); @@ -201,7 +201,7 @@ describe("Is", () => { const result = await authorizer.Is({}); - expect(result).toBe(false); + expect(result).toEqual({ decisions: [{ decision: "", is: false }] }); mock.mockRestore(); }); diff --git a/tests/integration/index.test.ts b/tests/integration/index.test.ts index e73129a..0a53ab2 100644 --- a/tests/integration/index.test.ts +++ b/tests/integration/index.test.ts @@ -764,7 +764,9 @@ types: }, }); - const expectedResult = true; + const expectedResult = { + decisions: [{ decision: "allowed", is: true }], + }; expect(response).toEqual(expectedResult); expect(JSON.parse(JSON.stringify(response))).toEqual(expectedResult); diff --git a/tests/directory/v3/serializer.test.ts b/tests/utils/serializer.test.ts similarity index 64% rename from tests/directory/v3/serializer.test.ts rename to tests/utils/serializer.test.ts index 9ffabbb..6c68111 100644 --- a/tests/directory/v3/serializer.test.ts +++ b/tests/utils/serializer.test.ts @@ -1,19 +1,22 @@ // Unit tests for: serializeResponse -import { GetObjectResponse } from "@aserto/node-directory/src/gen/cjs/aserto/directory/reader/v3/reader_pb"; +import { + file_aserto_directory_reader_v3_reader, + GetObjectResponse, +} from "@aserto/node-directory/src/gen/cjs/aserto/directory/reader/v3/reader_pb"; -import { DsRegistry, InvalidSchemaError } from "../../../src"; +import { InvalidSchemaError, TopazRegistry } from "../../src"; // Mock types type MockGenMessage = { $typeName: string; }; -describe("DsRegistry.serializeResponse()", () => { - let dsRegistry: DsRegistry; +describe("TopazRegistry.serializeResponse()", () => { + let topazRegistry: TopazRegistry; beforeEach(() => { - dsRegistry = new DsRegistry(); + topazRegistry = new TopazRegistry(file_aserto_directory_reader_v3_reader); }); it("serializes a valid response successfully", () => { @@ -29,7 +32,7 @@ describe("DsRegistry.serializeResponse()", () => { relations: [], }; - const result = dsRegistry.serializeResponse(mockResponse); + const result = topazRegistry.serializeResponse(mockResponse); expect(result).toEqual({ relations: [], @@ -47,7 +50,7 @@ describe("DsRegistry.serializeResponse()", () => { $typeName: "invalid.type.name", }; - expect(() => dsRegistry.serializeResponse(mockResponse)).toThrow( + expect(() => topazRegistry.serializeResponse(mockResponse)).toThrow( new InvalidSchemaError( "schema not registered for type: [invalid.type.name]", ),