Skip to content

Commit

Permalink
feat(keto-cli): create CLI to interact with Keto API
Browse files Browse the repository at this point in the history
  • Loading branch information
getlarge committed Jan 30, 2024
1 parent b776b5d commit 35ff1a6
Show file tree
Hide file tree
Showing 15 changed files with 562 additions and 80 deletions.
360 changes: 280 additions & 80 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
"dependencies": {
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.0.2",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.0.2",
"@nestjs/platform-express": "^10.0.2",
"@ory/client": "^1.4.9",
"defekt": "^9.3.1",
"lodash.get": "^4.4.2",
"nest-commander": "^3.12.5",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.0",
"tslib": "^2.3.0"
Expand Down
38 changes: 38 additions & 0 deletions packages/keto-cli/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": [
"error",
{
"buildTargets": ["build"],
"checkMissingDependencies": true,
"checkObsoleteDependencies": true,
"checkVersionMismatches": true,
"includeTransitiveDependencies": false,
"ignoredDependencies": [
"@getlarge/base-client-wrapper",
"@nestjs/testing"
]
}
]
}
}
]
}
11 changes: 11 additions & 0 deletions packages/keto-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# keto-cli

This library was generated with [Nx](https://nx.dev).

## Building

Run `nx build keto-cli` to build the library.

## Running unit tests

Run `nx test keto-cli` to execute the unit tests via [Jest](https://jestjs.io).
11 changes: 11 additions & 0 deletions packages/keto-cli/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'keto-cli',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/packages/keto-cli',
};
10 changes: 10 additions & 0 deletions packages/keto-cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "@getlarge/keto-cli",
"version": "0.0.1",
"dependencies": {
"tslib": "^2.3.0"
},
"type": "commonjs",
"main": "./src/index.js",
"typings": "./src/index.d.ts"
}
35 changes: 35 additions & 0 deletions packages/keto-cli/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "keto-cli",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/keto-cli/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/keto-cli",
"tsConfig": "packages/keto-cli/tsconfig.lib.json",
"packageJson": "packages/keto-cli/package.json",
"main": "packages/keto-cli/src/index.ts",
"assets": ["packages/keto-cli/*.md"]
}
},
"publish": {
"command": "node tools/scripts/publish.mjs keto-cli {args.ver} {args.tag}",
"dependsOn": ["build"]
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "packages/keto-cli/jest.config.ts"
}
}
},
"tags": []
}
32 changes: 32 additions & 0 deletions packages/keto-cli/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
OryPermissionsModule,
OryRelationshipsModule,
} from '@getlarge/keto-client-wrapper';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';

import { CheckPermissionCommand } from './check-permission.command';

@Module({
imports: [
ConfigModule.forRoot({}),
OryPermissionsModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
basePath: configService.get(
'ORY_KETO_PUBLIC_URL',
'http://localhost:4466'
),
}),
}),
OryRelationshipsModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
accessToken: configService.get('ORY_KETO_API_KEY', ''),
basePath: configService.get('ORY_KETO_ADMIN_URL', ''),
}),
}),
],
providers: [CheckPermissionCommand],
})
export class AppModule {}
60 changes: 60 additions & 0 deletions packages/keto-cli/src/app/check-permission.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { OryPermissionsService } from '@getlarge/keto-client-wrapper';
import {
createPermissionCheckQuery,
parseRelationTuple,
} from '@getlarge/keto-relations-parser';
import { Logger } from '@nestjs/common';
import {
Configuration,
type PermissionApiCheckPermissionRequest,
} from '@ory/client';
import { Command, CommandRunner, Option } from 'nest-commander';

interface CommandOptions
extends Pick<Configuration, 'basePath' | 'accessToken'> {
tuple: PermissionApiCheckPermissionRequest;
}

@Command({ name: 'check', description: 'Check permission on Ory Keto' })
export class CheckPermissionCommand extends CommandRunner {
readonly logger = new Logger(CheckPermissionCommand.name);

constructor(private readonly oryPermissionsService: OryPermissionsService) {
super();
}

async run(passedParams: string[], options: CommandOptions): Promise<void> {
const { tuple } = options;
if (options.accessToken || options.basePath) {
this.oryPermissionsService.config = new Configuration({
...this.oryPermissionsService.config,
...options,
});
}
const isAllowed = await this.oryPermissionsService.checkPermission(tuple);
this.logger.log(`Permission ${isAllowed ? 'granted' : 'denied'}`);
}

@Option({
flags: '-t, --tuple [string]',
description:
'Relationship tuple to check permission from, using Zanzibar notation',
required: true,
})
parseRelationTuple(val: string): PermissionApiCheckPermissionRequest {
const res = parseRelationTuple(val);
if (res.hasError()) {
throw res.error;
}
return createPermissionCheckQuery(res.value).unwrapOrThrow();
}

@Option({
flags: '-b, --base-path [string]',
description: 'Ory Keto Admin URL',
required: false,
})
parseBasePath(val: string): string {
return val;
}
}
19 changes: 19 additions & 0 deletions packages/keto-cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { CommandFactory } from 'nest-commander';

import { AppModule } from './app/app.module';

async function bootstrap(): Promise<void> {
await CommandFactory.run(AppModule, {
logger: ['log', 'error', 'warn', 'debug', 'verbose'],
enablePositionalOptions: true,
enablePassThroughOptions: true,
cliName: 'keto-cli',
version: '0.0.1',
usePlugins: true,
});
}

bootstrap().catch((err) => {
console.error(err);
process.exit(1);
});
22 changes: 22 additions & 0 deletions packages/keto-cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
16 changes: 16 additions & 0 deletions packages/keto-cli/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"declaration": true,
"types": ["node"],
"target": "es2021",
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src/**/*.ts"],
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
}
14 changes: 14 additions & 0 deletions packages/keto-cli/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,16 @@ export class OryPermissionsService extends PermissionApi {
options.basePath,
baseService.axios as ConstructorParameters<typeof PermissionApi>[2]
);
this.configuration ??= new Configuration({
basePath: options.basePath,
});
}

get config(): Configuration {
return this.configuration;
}

set config(config: Configuration) {
this.configuration = config;
}
}
1 change: 1 addition & 0 deletions tsconfig.base.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@getlarge/base-client-wrapper": [
"packages/base-client-wrapper/src/index.ts"
],
"@getlarge/keto-cli": ["packages/keto-cli/src/index.ts"],
"@getlarge/keto-client-wrapper": [
"packages/keto-client-wrapper/src/index.ts"
],
Expand Down

0 comments on commit 35ff1a6

Please sign in to comment.