├── .github └── workflows │ ├── ci.yaml │ └── release.yaml ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── HttpClient.ts ├── WarrantClient.ts ├── constants.ts ├── index.d.ts ├── index.ts ├── modules │ ├── Authorization.ts │ ├── Feature.ts │ ├── ObjectModule.ts │ ├── Permission.ts │ ├── PricingTier.ts │ ├── Role.ts │ ├── Session.ts │ ├── Tenant.ts │ ├── User.ts │ └── WarrantModule.ts └── types │ ├── ApiError.ts │ ├── Check.ts │ ├── Config.ts │ ├── Feature.ts │ ├── List.ts │ ├── Object.ts │ ├── ObjectType.ts │ ├── Permission.ts │ ├── PricingTier.ts │ ├── Query.ts │ ├── Role.ts │ ├── Session.ts │ ├── Tenant.ts │ ├── User.ts │ ├── Warrant.ts │ ├── WarrantRequestOptions.ts │ └── index.ts ├── test ├── LiveTest.spec.ts └── WarrantClientTest.spec.ts └── tsconfig.json /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Warrant Node 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 2 16 | - name: Setup Node Env 17 | uses: actions/setup-node@v3 18 | - name: Build 19 | run: | 20 | npm ci 21 | npm run build 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Warrant Node 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+.[0-9]+' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | - name: Setup Node Environment 15 | uses: actions/setup-node@v3 16 | with: 17 | registry-url: 'https://registry.npmjs.org' 18 | - name: Build, Test, and Publish 19 | run: | 20 | npm ci 21 | npm run build 22 | npm test 23 | npm publish 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Warrant (Forerunner Labs, Inc.) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Warrant Node.js Library 2 | 3 | Use [Warrant](https://warrant.dev/) in server-side Node.js projects. 4 | 5 | [![npm](https://img.shields.io/npm/v/@warrantdev/warrant-node)](https://www.npmjs.com/package/@warrantdev/warrant-node) 6 | 7 | ## Installation 8 | 9 | Use `npm` to install the Warrant module: 10 | 11 | ```sh 12 | npm install @warrantdev/warrant-node 13 | ``` 14 | 15 | ## Usage 16 | 17 | Import the Warrant client and pass your API key to the constructor to get started: 18 | 19 | ```js 20 | const Warrant = require("@warrantdev/warrant-node"); 21 | const warrantClient = new Warrant.WarrantClient({ 22 | apiKey: "api_test_f5dsKVeYnVSLHGje44zAygqgqXiLJBICbFzCiAg1E=", 23 | }); 24 | ``` 25 | 26 | Or using ES modules: 27 | 28 | ```js 29 | import { WarrantClient } from "@warrantdev/warrant-node"; 30 | const warrantClient = new WarrantClient({ 31 | apiKey: "api_test_f5dsKVeYnVSLHGje44zAygqgqXiLJBICbFzCiAg1E=", 32 | }); 33 | ``` 34 | 35 | --- 36 | 37 | This method creates a user in Warrant with the provided `userId`. Provide an optional `email` to make it easier to identify users in the Warrant dashboard. 38 | 39 | ```js 40 | const Warrant = require("@warrantdev/warrant-node"); 41 | const warrantClient = new Warrant.WarrantClient({ 42 | apiKey: "api_test_f5dsKVeYnVSLHGje44zAygqgqXiLJBICbFzCiAg1E=", 43 | }); 44 | 45 | // Creates a user with user.id as the userId 46 | warrantClient.User 47 | .create({ userId: user.id, email: user.email }) 48 | .then((newUser) => console.log(newUser)) 49 | .catch((error) => console.log(error)); 50 | ``` 51 | 52 | Or using ES modules and async/await: 53 | 54 | ```js 55 | import { WarrantClient } from "@warrantdev/warrant-node"; 56 | const warrantClient = new WarrantClient({ 57 | apiKey: "api_test_f5dsKVeYnVSLHGje44zAygqgqXiLJBICbFzCiAg1E=", 58 | }); 59 | 60 | // Creates a user with user.id as the userId and 61 | // assigns the new user the "store_owner" role 62 | const newUser = await warrantClient.User.create({ 63 | userId: user.id, 64 | email: user.email, 65 | }); 66 | ``` 67 | 68 | ## Configuring the API Endpoint 69 | --- 70 | The API endpoint the SDK makes requests to is configurable via the `endpoint` attribute when initializing the client: 71 | 72 | ```js 73 | import { WarrantClient } from "@warrantdev/warrant-node"; 74 | 75 | // Set api and authorize endpoints to http://localhost:8000 76 | const warrantClient = new WarrantClient({ 77 | apiKey: "api_test_f5dsKVeYnVSLHGje44zAygqgqXiLJBICbFzCiAg1E=", 78 | endpoint: "http://localhost:8000", 79 | }); 80 | ``` 81 | 82 | ## Authorization 83 | 84 | All access checks are performed based on an `object`, `relation` and `subject`. You can pass your own defined objects to the check methods by implementing the `WarrantObject` interface. 85 | 86 | ``` 87 | interface WarrantObject { 88 | getObjectType(): string; 89 | getObjectId(): string; 90 | } 91 | ``` 92 | 93 | ### `check(Check)` 94 | 95 | This method returns a `Promise` that resolves with `true` if the `subject` has the specified `relation` to the `object` and `false` otherwise. 96 | 97 | ```js 98 | const Warrant = require("@warrantdev/warrant-node"); 99 | 100 | const warrantClient = new Warrant.WarrantClient({ 101 | apiKey: "api_test_f5dsKVeYnVSLHGje44zAygqgqXiLJBICbFzCiAg1E=", 102 | }); 103 | 104 | // Store class implements WarrantObject 105 | class Store { 106 | private id: number; 107 | 108 | public getObjectType(): string { 109 | return "store"; 110 | } 111 | 112 | public getObjectId(): string { 113 | return this.id.toString(); 114 | } 115 | } 116 | 117 | // 118 | // Example Scenario: 119 | // An e-commerce website where Store Owners can edit store info 120 | // 121 | const myStore = new Store('my-store'); 122 | warrantClient.Authorization 123 | .check({ 124 | object: myStore, 125 | relation: "edit", 126 | subject: { 127 | objectType: "user", 128 | objectId: user.id, 129 | }, 130 | }) 131 | .then((isAuthorized) => { 132 | if (isAuthorized) { 133 | // Carry out logic to allow user to edit a Store 134 | } 135 | }) 136 | .catch((error) => console.log(error)); 137 | ``` 138 | 139 | Or using ES modules and async/await: 140 | 141 | ```js 142 | import { WarrantClient } from "@warrantdev/warrant-node"; 143 | 144 | const warrantClient = new WarrantClient({ 145 | apiKey: "api_test_f5dsKVeYnVSLHGje44zAygqgqXiLJBICbFzCiAg1E=", 146 | }); 147 | 148 | // 149 | // Example Scenario: 150 | // An e-commerce website where Store Owners can edit store info 151 | // 152 | const myStore = new Store('my-store'); 153 | if ( 154 | await warrantClient.Authorization.check({ 155 | object: myStore, 156 | relation: "edit", 157 | subject: { 158 | objectType: "user", 159 | objectId: user.id, 160 | }, 161 | }) 162 | ) { 163 | // Carry out logic to allow user to edit a Store 164 | } 165 | ``` 166 | 167 | We’ve used a random API key in these code examples. Replace it with your 168 | [actual publishable API keys](https://app.warrant.dev) to 169 | test this code through your own Warrant account. 170 | 171 | For more information on how to use the Warrant API and usage examples for all methods, please refer to the 172 | [Warrant API reference](https://docs.warrant.dev). 173 | 174 | Note that we may release new [minor and patch](https://semver.org/) versions of 175 | `@warrantdev/warrant-node` with small but backwards-incompatible fixes to the type 176 | declarations. These changes will not affect Warrant itself. 177 | 178 | ## TypeScript support 179 | 180 | This package includes TypeScript declarations for Warrant. 181 | 182 | ## Warrant Documentation 183 | 184 | - [Warrant Docs](https://docs.warrant.dev/) 185 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@warrantdev/warrant-node", 3 | "version": "6.3.0", 4 | "description": "Warrant API Wrapper for Node.js clients", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "files": [ 8 | "/dist/**/*", 9 | "README.md" 10 | ], 11 | "scripts": { 12 | "build": "tsc", 13 | "build-local": "npm run build && npm pack", 14 | "prepare": "npm run build", 15 | "test": "npm run build && mocha --require ts-node/register test/**/*.spec.ts" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/warrant-dev/warrant-node.git" 20 | }, 21 | "keywords": [ 22 | "warrant", 23 | "authorization", 24 | "rbac", 25 | "role based access control", 26 | "access control" 27 | ], 28 | "author": "Warrant (https://warrant.dev)", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/warrant-dev/warrant-node/issues" 32 | }, 33 | "homepage": "https://github.com/warrant-dev/warrant-node#readme", 34 | "engines": { 35 | "node": ">=18.13.0" 36 | }, 37 | "devDependencies": { 38 | "@types/chai": "^4.3.9", 39 | "@types/mocha": "^10.0.3", 40 | "@types/node": "^18.13.0", 41 | "chai": "^4.3.10", 42 | "mocha": "^10.2.0", 43 | "ts-node": "^10.9.1", 44 | "typescript": "^4.3.2", 45 | "undici": "^6.0.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/HttpClient.ts: -------------------------------------------------------------------------------- 1 | import ApiError from "./types/ApiError"; 2 | import { WarrantRequestOptions } from "./types/WarrantRequestOptions"; 3 | 4 | const { version } = require("../package.json"); 5 | 6 | interface HttpClient { 7 | get(requestOptions: HttpClientRequestOptions): Promise; 8 | delete(requestOptions: HttpClientRequestOptions): Promise; 9 | post(requestOptions: HttpClientRequestOptions): Promise; 10 | put(requestOptions: HttpClientRequestOptions): Promise; 11 | } 12 | 13 | export interface HttpClientConfig { 14 | apiKey: string; 15 | baseUrl: string; 16 | } 17 | 18 | export interface HttpClientRequestOptions { 19 | apiKey?: string; 20 | baseUrl?: string; 21 | data?: any; 22 | params?: any; 23 | url: string; 24 | options?: WarrantRequestOptions; 25 | } 26 | 27 | interface RequestHeaders { 28 | [header: string]: string; 29 | } 30 | 31 | interface FetchRequestOptions { 32 | method: "GET" | "POST" | "PUT" | "DELETE"; 33 | headers: RequestHeaders; 34 | body?: string; 35 | } 36 | 37 | const MAX_RETRY_ATTEMPTS = 3; 38 | const BACKOFF_MULTIPLIER = 1.5; 39 | const MINIMUM_SLEEP_TIME = 500; 40 | const RETRY_STATUS_CODES = [500, 502, 504]; 41 | 42 | const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) 43 | 44 | export default class ApiClient implements HttpClient { 45 | private config: HttpClientConfig; 46 | 47 | constructor(config: HttpClientConfig) { 48 | this.config = config; 49 | } 50 | 51 | public async get(requestOptions: HttpClientRequestOptions): Promise { 52 | const [requestUrl, fetchRequestOptions] = this.buildRequestUrlAndOptions("GET", requestOptions); 53 | 54 | const response = await this.fetchWithRetry(requestUrl, fetchRequestOptions); 55 | 56 | return this.parseResponse(response); 57 | } 58 | 59 | public async delete(requestOptions: HttpClientRequestOptions): Promise { 60 | const [requestUrl, fetchRequestOptions] = this.buildRequestUrlAndOptions("DELETE", requestOptions); 61 | 62 | const response = await this.fetchWithRetry(requestUrl, fetchRequestOptions); 63 | 64 | return this.parseResponse(response); 65 | } 66 | 67 | public async post(requestOptions: HttpClientRequestOptions): Promise { 68 | const [requestUrl, fetchRequestOptions] = this.buildRequestUrlAndOptions("POST", requestOptions); 69 | 70 | const response = await this.fetchWithRetry(requestUrl, fetchRequestOptions); 71 | 72 | return this.parseResponse(response); 73 | } 74 | 75 | public async put(requestOptions: HttpClientRequestOptions): Promise { 76 | const [requestUrl, fetchRequestOptions] = this.buildRequestUrlAndOptions("PUT", requestOptions); 77 | 78 | const response = await this.fetchWithRetry(requestUrl, fetchRequestOptions); 79 | 80 | return this.parseResponse(response); 81 | } 82 | 83 | private async fetchWithRetry(requestUrl: string, fetchRequestOptions: FetchRequestOptions): Promise { 84 | let response: any = null; 85 | let requestError: any = null; 86 | let retryAttempts = 1; 87 | 88 | const makeRequest = async (): Promise => { 89 | try { 90 | response = await fetch(requestUrl, fetchRequestOptions); 91 | } catch (e) { 92 | requestError = e; 93 | } 94 | 95 | if (this.shouldRetryRequest(response, requestError, retryAttempts)) { 96 | retryAttempts++; 97 | await sleep(this.getSleepTime(retryAttempts)); 98 | return makeRequest(); 99 | } 100 | 101 | if (!response.ok) { 102 | throw this.buildError(await response.json()); 103 | } 104 | 105 | return response; 106 | } 107 | 108 | return makeRequest(); 109 | } 110 | 111 | private shouldRetryRequest(response: any, requestError: any, retryAttempt: number): boolean { 112 | if (retryAttempt > MAX_RETRY_ATTEMPTS) { 113 | return false; 114 | } 115 | 116 | if (requestError != null && requestError instanceof TypeError) { 117 | return true; 118 | } 119 | 120 | if (response != null && RETRY_STATUS_CODES.includes(response.status)) { 121 | return true; 122 | } 123 | 124 | return false; 125 | } 126 | 127 | private getSleepTime(retryAttempt: number): number { 128 | let sleepTime = MINIMUM_SLEEP_TIME * Math.pow(BACKOFF_MULTIPLIER, retryAttempt); 129 | const jitter = Math.random() + 0.5; 130 | return sleepTime * jitter; 131 | } 132 | 133 | private buildRequestUrlAndOptions(method: FetchRequestOptions["method"], requestOptions?: HttpClientRequestOptions): [string, FetchRequestOptions] { 134 | let baseUrl = this.config.baseUrl; 135 | const fetchRequestOptions: FetchRequestOptions = { 136 | method, 137 | headers: { 138 | 'User-Agent': `warrant-node/${version}`, 139 | 'Content-Type': "application/json" 140 | }, 141 | }; 142 | 143 | if (requestOptions?.apiKey) { 144 | fetchRequestOptions.headers['Authorization'] = `ApiKey ${requestOptions.apiKey}`; 145 | } else if (this.config.apiKey) { 146 | fetchRequestOptions.headers['Authorization'] = `ApiKey ${this.config.apiKey}`; 147 | } 148 | 149 | if (requestOptions?.baseUrl) { 150 | baseUrl = requestOptions.baseUrl; 151 | } 152 | 153 | if (requestOptions?.options?.warrantToken) { 154 | fetchRequestOptions.headers['Warrant-Token'] = requestOptions.options.warrantToken; 155 | } 156 | 157 | let requestUrl = `${baseUrl}${requestOptions.url}`; 158 | if (requestOptions?.params) { 159 | const queryParams = new URLSearchParams(requestOptions.params); 160 | requestUrl += `?${queryParams}`; 161 | } 162 | 163 | if (method !== "GET" && requestOptions.data) { 164 | if (Object.keys(requestOptions.data).length === 0) { 165 | fetchRequestOptions.body = "{}"; 166 | } else { 167 | fetchRequestOptions.body = JSON.stringify(requestOptions.data); 168 | } 169 | } 170 | 171 | return [requestUrl, fetchRequestOptions]; 172 | } 173 | 174 | private async parseResponse(response: any): Promise { 175 | const resJson = await response.text(); 176 | const warrantToken = response.headers.get("Warrant-Token"); 177 | if (resJson) { 178 | const parsedRes = JSON.parse(resJson); 179 | if (warrantToken != null) { 180 | if (Array.isArray(parsedRes)) { 181 | for (const res of parsedRes) { 182 | res.warrantToken = warrantToken; 183 | } 184 | } else { 185 | parsedRes.warrantToken = warrantToken; 186 | } 187 | } 188 | 189 | return parsedRes; 190 | } 191 | 192 | if (warrantToken != null) { 193 | return {warrantToken: warrantToken}; 194 | } else { 195 | return resJson; 196 | } 197 | } 198 | 199 | private buildError(errorResponse: any): Error { 200 | return new ApiError(errorResponse.code, errorResponse.message); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/WarrantClient.ts: -------------------------------------------------------------------------------- 1 | import { API_URL_BASE } from "./constants"; 2 | import ApiClient from "./HttpClient"; 3 | import Authorization from "./modules/Authorization"; 4 | import Feature from "./modules/Feature"; 5 | import Object from "./modules/ObjectModule"; 6 | import Permission from "./modules/Permission"; 7 | import PricingTier from "./modules/PricingTier"; 8 | import Role from "./modules/Role"; 9 | import Session from "./modules/Session"; 10 | import Tenant from "./modules/Tenant"; 11 | import User from "./modules/User"; 12 | import Warrant from "./modules/WarrantModule"; 13 | import Config from "./types/Config"; 14 | 15 | export default class WarrantClient { 16 | static config: Config; 17 | static httpClient: ApiClient; 18 | 19 | public Authorization: typeof Authorization = Authorization; 20 | public Feature: typeof Feature = Feature; 21 | public Object: typeof Object = Object; 22 | public Permission: typeof Permission = Permission; 23 | public PricingTier: typeof PricingTier = PricingTier; 24 | public Role: typeof Role = Role; 25 | public Session: typeof Session = Session; 26 | public Tenant: typeof Tenant = Tenant; 27 | public User: typeof User = User; 28 | public Warrant: typeof Warrant = Warrant; 29 | 30 | constructor(config: Config) { 31 | WarrantClient.config = config; 32 | WarrantClient.httpClient = new ApiClient({ 33 | apiKey: config.apiKey, 34 | baseUrl: config.endpoint || API_URL_BASE, 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const API_URL_BASE = "https://api.warrant.dev"; 2 | export const SELF_SERVICE_DASH_URL_BASE = "https://self-serve.warrant.dev"; 3 | export const API_VERSION = "v2"; 4 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | import Config from "./types/Config"; 2 | import Authorization from "./modules/Authorization"; 3 | import Feature from "./modules/Feature"; 4 | import Permission from "./modules/Permission"; 5 | import PricingTier from "./modules/PricingTier"; 6 | import Role from "./modules/Role"; 7 | import Session from "./modules/Session"; 8 | import Tenant from "./modules/Tenant"; 9 | import User from "./modules/User"; 10 | import Warrant from "./modules/WarrantModule"; 11 | 12 | declare module 'warrant' { 13 | export class WarrantClient { 14 | static WarrantClient: typeof WarrantClient; 15 | 16 | constructor(config: Config); 17 | 18 | Authorization: typeof Authorization; 19 | Feature: typeof Feature; 20 | Permission: typeof Permission; 21 | PricingTier: typeof PricingTier; 22 | Role: typeof Role; 23 | Session: typeof Session; 24 | Tenant: typeof Tenant; 25 | User: typeof User; 26 | Warrant: typeof Warrant; 27 | } 28 | 29 | export default WarrantClient; 30 | } 31 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./types"; 2 | export { default as Feature } from "./modules/Feature"; 3 | export { default as Object } from "./modules/ObjectModule"; 4 | export { default as Permission } from "./modules/Permission"; 5 | export { default as PricingTier } from "./modules/PricingTier"; 6 | export { default as Role } from "./modules/Role"; 7 | export { default as Tenant } from "./modules/Tenant"; 8 | export { default as User } from "./modules/User"; 9 | export { default as Warrant } from "./modules/WarrantModule"; 10 | export { default as WarrantClient } from "./WarrantClient"; 11 | -------------------------------------------------------------------------------- /src/modules/Authorization.ts: -------------------------------------------------------------------------------- 1 | import { 2 | BatchCheckParams, 3 | Check, 4 | CheckOp, 5 | CheckParams, 6 | CheckManyParams, 7 | CheckResult, 8 | FeatureCheckParams, 9 | PermissionCheckParams, 10 | checkWarrantParamsToCheckWarrant, 11 | } from "../types/Check"; 12 | import { ObjectType } from "../types/ObjectType"; 13 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 14 | import WarrantClient from "../WarrantClient"; 15 | import { API_VERSION } from "../constants"; 16 | 17 | export default class Authorization { 18 | public static async check(checkParams: CheckParams, options: WarrantRequestOptions = {}): Promise { 19 | const check: Check = { 20 | warrants: [checkWarrantParamsToCheckWarrant(checkParams)], 21 | debug: checkParams.debug 22 | }; 23 | 24 | if (WarrantClient.config.authorizeEndpoint && options.warrantToken != "latest") { 25 | return this.makeEdgeCheckRequest(check, options); 26 | } 27 | 28 | return this.makeCheckRequest(check, options); 29 | } 30 | 31 | public static async checkMany(checkParams: CheckManyParams, options: WarrantRequestOptions = {}): Promise { 32 | const check: Check = { 33 | op: checkParams.op, 34 | warrants: checkParams.warrants.map((checkWarrantParams) => checkWarrantParamsToCheckWarrant(checkWarrantParams)), 35 | debug: checkParams.debug 36 | }; 37 | 38 | if (WarrantClient.config.authorizeEndpoint && options.warrantToken != "latest") { 39 | return this.makeEdgeCheckRequest(check, options); 40 | } 41 | 42 | return this.makeCheckRequest(check, options); 43 | } 44 | 45 | public static async batchCheck(checkParams: BatchCheckParams, options: WarrantRequestOptions = {}): Promise { 46 | const check: Check = { 47 | op: CheckOp.Batch, 48 | warrants: checkParams.warrants.map((checkWarrantParams) => checkWarrantParamsToCheckWarrant(checkWarrantParams)), 49 | debug: checkParams.debug 50 | }; 51 | 52 | return this.makeBatchCheckRequest(check, options); 53 | } 54 | 55 | public static async hasFeature(featureCheckParams: FeatureCheckParams, options: WarrantRequestOptions = {}): Promise { 56 | return this.check({ 57 | object: { 58 | objectType: ObjectType.Feature, 59 | objectId: featureCheckParams.featureId, 60 | }, 61 | relation: "member", 62 | subject: featureCheckParams.subject, 63 | context: featureCheckParams.context, 64 | debug: featureCheckParams.debug 65 | }, options); 66 | } 67 | 68 | public static async hasPermission(permissionCheckParams: PermissionCheckParams, options: WarrantRequestOptions = {}): Promise { 69 | return this.check({ 70 | object: { 71 | objectType: ObjectType.Permission, 72 | objectId: permissionCheckParams.permissionId, 73 | }, 74 | relation: "member", 75 | subject: permissionCheckParams.subject, 76 | context: permissionCheckParams.context, 77 | debug: permissionCheckParams.debug 78 | }, options); 79 | } 80 | 81 | // Private methods 82 | private static async makeCheckRequest(check: Check, options: WarrantRequestOptions = {}): Promise { 83 | const response = await WarrantClient.httpClient.post({ 84 | url: `/${API_VERSION}/check`, 85 | data: check, 86 | options, 87 | }); 88 | 89 | return response.code === 200; 90 | } 91 | 92 | private static async makeEdgeCheckRequest(check: Check, options: WarrantRequestOptions = {}): Promise { 93 | try { 94 | const response = await WarrantClient.httpClient.post({ 95 | baseUrl: WarrantClient.config.authorizeEndpoint, 96 | url: `/${API_VERSION}/check`, 97 | data: check, 98 | options, 99 | }); 100 | 101 | return response.code === 200; 102 | } catch (e) { 103 | if (e.code === "cache_not_ready") { 104 | return this.makeCheckRequest(check); 105 | } 106 | 107 | throw e; 108 | } 109 | } 110 | 111 | private static async makeBatchCheckRequest(check: Check, options: WarrantRequestOptions = {}): Promise { 112 | const response = await WarrantClient.httpClient.post({ 113 | url: `/${API_VERSION}/check`, 114 | data: check, 115 | options, 116 | }); 117 | 118 | return response.map((checkResult: CheckResult) => checkResult.code === 200); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/modules/Feature.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateFeatureParams, 3 | ListFeatureParams, 4 | UpdateFeatureParams, 5 | ListResponse, 6 | QueryResult, 7 | Warrant, 8 | WarrantObject, 9 | BaseWarrantObject, 10 | } from "../types"; 11 | import { ObjectType } from "../types/ObjectType"; 12 | import ObjectModule from "./ObjectModule"; 13 | import WarrantModule from "./WarrantModule"; 14 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 15 | 16 | export default class Feature implements WarrantObject { 17 | featureId: string; 18 | meta?: { [key: string]: any } 19 | 20 | constructor(featureId: string, meta: { [key: string]: any }) { 21 | this.featureId = featureId; 22 | this.meta = meta; 23 | } 24 | 25 | // 26 | // Static methods 27 | // 28 | public static async create(params: CreateFeatureParams = {}, options: WarrantRequestOptions = {}): Promise { 29 | const response = await ObjectModule.create({ 30 | object: { 31 | objectType: ObjectType.Feature, 32 | objectId: params.featureId, 33 | }, 34 | meta: params.meta, 35 | }, options); 36 | 37 | return new Feature(response.objectId, response.meta); 38 | } 39 | 40 | public static async get(featureId: string, options: WarrantRequestOptions = {}): Promise { 41 | const response = await ObjectModule.get({ 42 | objectType: ObjectType.Feature, 43 | objectId: featureId, 44 | }, options); 45 | return new Feature(response.objectId, response.meta); 46 | } 47 | 48 | public static async update(featureId: string, params: UpdateFeatureParams, options: WarrantRequestOptions = {}): Promise { 49 | const response = await ObjectModule.update({ 50 | objectType: ObjectType.Feature, 51 | objectId: featureId, 52 | }, { meta: params.meta }, options); 53 | 54 | return new Feature(response.objectId, response.meta); 55 | } 56 | 57 | public static async delete(featureId: string, options: WarrantRequestOptions = {}): Promise { 58 | return ObjectModule.delete({ 59 | objectType: ObjectType.Feature, 60 | objectId: featureId, 61 | }, options); 62 | } 63 | 64 | public static async listFeatures(listParams: ListFeatureParams = {}, options: WarrantRequestOptions = {}): Promise> { 65 | try { 66 | const response = await ObjectModule.list({ 67 | objectType: ObjectType.Feature, 68 | ...listParams, 69 | }, options); 70 | 71 | const features: Feature[] = response.results.map((object: BaseWarrantObject) => new Feature(object.objectId, object.meta)); 72 | return { 73 | ...response, 74 | results: features, 75 | }; 76 | } catch (e) { 77 | throw e; 78 | } 79 | } 80 | 81 | public static async listFeaturesForPricingTier(pricingTierId: string, listParams: ListFeatureParams = {}, options: WarrantRequestOptions = {}): Promise> { 82 | try { 83 | const queryResponse = await WarrantModule.query(`select feature where pricing-tier:${pricingTierId} is *`, listParams, options); 84 | const features: Feature[] = queryResponse.results.map((queryResult: QueryResult) => new Feature(queryResult.objectId, queryResult.meta)); 85 | return { 86 | ...queryResponse, 87 | results: features, 88 | }; 89 | } catch (e) { 90 | throw e; 91 | } 92 | } 93 | 94 | public static async assignFeatureToPricingTier(pricingTierId: string, featureId: string, options: WarrantRequestOptions = {}): Promise { 95 | return WarrantModule.create({ 96 | object: { 97 | objectType: ObjectType.Feature, 98 | objectId: featureId, 99 | }, 100 | relation: "member", 101 | subject: { 102 | objectType: ObjectType.PricingTier, 103 | objectId: pricingTierId, 104 | } 105 | }, options); 106 | } 107 | 108 | public static async removeFeatureFromPricingTier(pricingTierId: string, featureId: string, options: WarrantRequestOptions = {}): Promise { 109 | return WarrantModule.delete({ 110 | object: { 111 | objectType: ObjectType.Feature, 112 | objectId: featureId, 113 | }, 114 | relation: "member", 115 | subject: { 116 | objectType: ObjectType.PricingTier, 117 | objectId: pricingTierId, 118 | } 119 | }, options); 120 | } 121 | 122 | public static async listFeaturesForTenant(tenantId: string, listOptions: ListFeatureParams = {}, options: WarrantRequestOptions = {}): Promise> { 123 | try { 124 | const queryResponse = await WarrantModule.query(`select feature where tenant:${tenantId} is *`, listOptions, options); 125 | const features: Feature[] = queryResponse.results.map((queryResult: QueryResult) => new Feature(queryResult.objectId, queryResult.meta)); 126 | return { 127 | ...queryResponse, 128 | results: features, 129 | }; 130 | } catch (e) { 131 | throw e; 132 | } 133 | } 134 | 135 | public static async assignFeatureToTenant(tenantId: string, featureId: string, options: WarrantRequestOptions = {}): Promise { 136 | return WarrantModule.create({ 137 | object: { 138 | objectType: ObjectType.Feature, 139 | objectId: featureId, 140 | }, 141 | relation: "member", 142 | subject: { 143 | objectType: ObjectType.Tenant, 144 | objectId: tenantId, 145 | } 146 | }, options); 147 | } 148 | 149 | public static async removeFeatureFromTenant(tenantId: string, featureId: string, options: WarrantRequestOptions = {}): Promise { 150 | return WarrantModule.delete({ 151 | object: { 152 | objectType: ObjectType.Feature, 153 | objectId: featureId, 154 | }, 155 | relation: "member", 156 | subject: { 157 | objectType: ObjectType.Tenant, 158 | objectId: tenantId, 159 | } 160 | }, options); 161 | } 162 | 163 | public static async listFeaturesForUser(userId: string, listOptions: ListFeatureParams = {}, options: WarrantRequestOptions = {}): Promise> { 164 | try { 165 | const queryResponse = await WarrantModule.query(`select feature where user:${userId} is *`, listOptions, options); 166 | const features: Feature[] = queryResponse.results.map((queryResult: QueryResult) => new Feature(queryResult.objectId, queryResult.meta)); 167 | return { 168 | ...queryResponse, 169 | results: features, 170 | }; 171 | } catch (e) { 172 | throw e; 173 | } 174 | } 175 | 176 | public static async assignFeatureToUser(userId: string, featureId: string, options: WarrantRequestOptions = {}): Promise { 177 | return WarrantModule.create({ 178 | object: { 179 | objectType: ObjectType.Feature, 180 | objectId: featureId, 181 | }, 182 | relation: "member", 183 | subject: { 184 | objectType: ObjectType.User, 185 | objectId: userId, 186 | } 187 | }, options); 188 | } 189 | 190 | public static async removeFeatureFromUser(userId: string, featureId: string, options: WarrantRequestOptions = {}): Promise { 191 | return WarrantModule.delete({ 192 | object: { 193 | objectType: ObjectType.Feature, 194 | objectId: featureId, 195 | }, 196 | relation: "member", 197 | subject: { 198 | objectType: ObjectType.User, 199 | objectId: userId, 200 | } 201 | }, options); 202 | } 203 | 204 | // WarrantObject methods 205 | public getObjectType(): string { 206 | return ObjectType.Feature; 207 | } 208 | 209 | public getObjectId(): string { 210 | return this.featureId; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/modules/ObjectModule.ts: -------------------------------------------------------------------------------- 1 | import WarrantClient from "../WarrantClient"; 2 | import { API_VERSION } from "../constants"; 3 | import { ListResponse } from "../types"; 4 | import { 5 | isWarrantObject, 6 | CreateObjectParams, 7 | ListObjectParams, 8 | UpdateObjectParams, 9 | DeleteObjectParams, 10 | BaseWarrantObject, 11 | WarrantObject, 12 | WarrantObjectLiteral, 13 | } from "../types/Object"; 14 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 15 | 16 | 17 | export default class ObjectModule { 18 | // 19 | // Static methods 20 | // 21 | public static async create(params: CreateObjectParams, options: WarrantRequestOptions = {}): Promise { 22 | return await WarrantClient.httpClient.post({ 23 | url: `/${API_VERSION}/objects`, 24 | data: { 25 | objectType: isWarrantObject(params.object) ? params.object.getObjectType() : params.object.objectType, 26 | objectId: isWarrantObject(params.object) ? params.object.getObjectId() : params.object.objectId, 27 | meta: params.meta, 28 | }, 29 | options, 30 | }); 31 | } 32 | 33 | public static async batchCreate(params: CreateObjectParams[], options: WarrantRequestOptions = {}): Promise { 34 | return await WarrantClient.httpClient.post({ 35 | url: `/${API_VERSION}/objects`, 36 | data: params.map((objectParams: CreateObjectParams) => ({ 37 | objectType: isWarrantObject(objectParams.object) ? objectParams.object.getObjectType() : objectParams.object.objectType, 38 | objectId: isWarrantObject(objectParams.object) ? objectParams.object.getObjectId() : objectParams.object.objectId, 39 | meta: objectParams.meta, 40 | })), 41 | options, 42 | }); 43 | } 44 | 45 | public static async get(object: WarrantObject | WarrantObjectLiteral, options: WarrantRequestOptions = {}): Promise { 46 | const objectType = isWarrantObject(object) ? object.getObjectType() : object.objectType; 47 | const objectId = isWarrantObject(object) ? object.getObjectId() : object.objectId; 48 | 49 | return await WarrantClient.httpClient.get({ 50 | url: `/${API_VERSION}/objects/${objectType}/${objectId}`, 51 | options, 52 | }); 53 | } 54 | 55 | public static async list(listParams: ListObjectParams, options: WarrantRequestOptions = {}): Promise> { 56 | return await WarrantClient.httpClient.get({ 57 | url: `/${API_VERSION}/objects`, 58 | params: listParams, 59 | options, 60 | }); 61 | } 62 | 63 | public static async update(object: WarrantObject | WarrantObjectLiteral, params: UpdateObjectParams, options: WarrantRequestOptions = {}): Promise { 64 | const objectType = isWarrantObject(object) ? object.getObjectType() : object.objectType; 65 | const objectId = isWarrantObject(object) ? object.getObjectId() : object.objectId; 66 | 67 | return await WarrantClient.httpClient.put({ 68 | url: `/${API_VERSION}/objects/${objectType}/${objectId}`, 69 | data: { 70 | meta: params.meta, 71 | }, 72 | options, 73 | }); 74 | } 75 | 76 | public static async delete(object: WarrantObject | WarrantObjectLiteral, options: WarrantRequestOptions = {}): Promise { 77 | const objectType = isWarrantObject(object) ? object.getObjectType() : object.objectType; 78 | const objectId = isWarrantObject(object) ? object.getObjectId() : object.objectId; 79 | 80 | const response = await WarrantClient.httpClient.delete({ 81 | url: `/${API_VERSION}/objects/${objectType}/${objectId}`, 82 | options, 83 | }); 84 | 85 | return response.warrantToken; 86 | } 87 | 88 | public static async batchDelete(params: DeleteObjectParams[], options: WarrantRequestOptions = {}): Promise { 89 | const response = await WarrantClient.httpClient.delete({ 90 | url: `/${API_VERSION}/objects`, 91 | data: params.map((objectParams: CreateObjectParams) => ({ 92 | objectType: isWarrantObject(objectParams.object) ? objectParams.object.getObjectType() : objectParams.object.objectType, 93 | objectId: isWarrantObject(objectParams.object) ? objectParams.object.getObjectId() : objectParams.object.objectId, 94 | })), 95 | options, 96 | }); 97 | 98 | return response.warrantToken; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/modules/Permission.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreatePermissionParams, 3 | ListPermissionParams, 4 | UpdatePermissionParams, 5 | ListResponse, 6 | WarrantObject, 7 | BaseWarrantObject, 8 | Warrant, 9 | QueryResult 10 | } from "../types"; 11 | import ObjectModule from "./ObjectModule"; 12 | import WarrantModule from "./WarrantModule"; 13 | import { ObjectType } from "../types/ObjectType"; 14 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 15 | 16 | 17 | export default class Permission implements WarrantObject { 18 | permissionId: string; 19 | meta?: { [key: string]: any } 20 | 21 | constructor(permissionId: string, meta: { [key: string]: any }) { 22 | this.permissionId = permissionId; 23 | this.meta = meta; 24 | } 25 | 26 | // 27 | // Static methods 28 | // 29 | public static async create(permission: CreatePermissionParams = {}, options: WarrantRequestOptions = {}): Promise { 30 | try { 31 | const response = await ObjectModule.create({ 32 | object: { 33 | objectType: ObjectType.Permission, 34 | objectId: permission.permissionId, 35 | }, 36 | meta: permission.meta, 37 | }, options); 38 | 39 | return new Permission(response.objectId, response.meta); 40 | } catch (e) { 41 | throw e; 42 | } 43 | } 44 | 45 | public static async get(permissionId: string, options: WarrantRequestOptions = {}): Promise { 46 | try { 47 | const response = await ObjectModule.get({ 48 | objectType: ObjectType.Permission, 49 | objectId: permissionId, 50 | }, options); 51 | 52 | return new Permission(response.objectId, response.meta); 53 | } catch (e) { 54 | throw e; 55 | } 56 | } 57 | 58 | public static async update(permissionId: string, params: UpdatePermissionParams, options: WarrantRequestOptions = {}): Promise { 59 | try { 60 | const response = await ObjectModule.update({ 61 | objectType: ObjectType.Permission, 62 | objectId: permissionId, 63 | }, { meta: params.meta }, options); 64 | 65 | return new Permission(response.objectId, response.meta); 66 | } catch (e) { 67 | throw e; 68 | } 69 | } 70 | 71 | public static async delete(permissionId: string, options: WarrantRequestOptions = {}): Promise { 72 | return ObjectModule.delete({ 73 | objectType: ObjectType.Permission, 74 | objectId: permissionId, 75 | }, options); 76 | } 77 | 78 | public static async listPermissions(listParams: ListPermissionParams = {}, options: WarrantRequestOptions = {}): Promise> { 79 | try { 80 | const response = await ObjectModule.list({ 81 | objectType: ObjectType.Permission, 82 | ...listParams, 83 | }, options); 84 | 85 | const permissions: Permission[] = response.results.map((object: BaseWarrantObject) => new Permission(object.objectId, object.meta)); 86 | return { 87 | ...response, 88 | results: permissions, 89 | }; 90 | } catch (e) { 91 | throw e; 92 | } 93 | } 94 | 95 | public static async listPermissionsForUser(userId: string, listParams: ListPermissionParams = {}, options: WarrantRequestOptions = {}): Promise> { 96 | try { 97 | const queryResponse = await WarrantModule.query(`select permission where user:${userId} is *`, listParams, options); 98 | const permissions: Permission[] = queryResponse.results.map((queryResult: QueryResult) => new Permission(queryResult.objectId, queryResult.meta)); 99 | 100 | return { 101 | ...queryResponse, 102 | results: permissions, 103 | }; 104 | } catch (e) { 105 | throw e; 106 | } 107 | } 108 | 109 | public static async assignPermissionToUser(userId: string, permissionId: string, options: WarrantRequestOptions = {}): Promise { 110 | return WarrantModule.create({ 111 | object: { 112 | objectType: ObjectType.Permission, 113 | objectId: permissionId, 114 | }, 115 | relation: "member", 116 | subject: { 117 | objectType: ObjectType.User, 118 | objectId: userId, 119 | } 120 | }, options); 121 | } 122 | 123 | public static async removePermissionFromUser(userId: string, permissionId: string, options: WarrantRequestOptions = {}): Promise { 124 | return WarrantModule.delete({ 125 | object: { 126 | objectType: ObjectType.Permission, 127 | objectId: permissionId, 128 | }, 129 | relation: "member", 130 | subject: { 131 | objectType: ObjectType.User, 132 | objectId: userId, 133 | } 134 | }, options); 135 | } 136 | 137 | public static async listPermissionsForRole(roleId: string, listParams: ListPermissionParams = {}, options: WarrantRequestOptions = {}): Promise> { 138 | try { 139 | const queryResponse = await WarrantModule.query(`select permission where role:${roleId} is *`, listParams, options); 140 | const permissions: Permission[] = queryResponse.results.map((queryResult: QueryResult) => new Permission(queryResult.objectId, queryResult.meta)); 141 | 142 | return { 143 | ...queryResponse, 144 | results: permissions, 145 | }; 146 | } catch (e) { 147 | throw e; 148 | } 149 | } 150 | 151 | public static async assignPermissionToRole(roleId: string, permissionId: string, options: WarrantRequestOptions = {}): Promise { 152 | return WarrantModule.create({ 153 | object: { 154 | objectType: ObjectType.Permission, 155 | objectId: permissionId, 156 | }, 157 | relation: "member", 158 | subject: { 159 | objectType: ObjectType.Role, 160 | objectId: roleId, 161 | } 162 | }, options); 163 | } 164 | 165 | public static async removePermissionFromRole(roleId: string, permissionId: string, options: WarrantRequestOptions = {}): Promise { 166 | return WarrantModule.delete({ 167 | object: { 168 | objectType: ObjectType.Permission, 169 | objectId: permissionId, 170 | }, 171 | relation: "member", 172 | subject: { 173 | objectType: ObjectType.Role, 174 | objectId: roleId, 175 | } 176 | }, options); 177 | } 178 | 179 | // WarrantObject methods 180 | public getObjectType(): string { 181 | return ObjectType.Permission; 182 | } 183 | 184 | public getObjectId(): string { 185 | return this.permissionId; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/modules/PricingTier.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreatePricingTierParams, 3 | ListPricingTierParams, 4 | ListFeatureParams, 5 | ListResponse, 6 | UpdatePricingTierParams, 7 | QueryResult, 8 | Warrant, 9 | FeatureCheckParams, 10 | WarrantObject, 11 | BaseWarrantObject, 12 | } from "../types"; 13 | import Authorization from "./Authorization"; 14 | import Feature from "./Feature"; 15 | import ObjectModule from "./ObjectModule"; 16 | import WarrantModule from "./WarrantModule"; 17 | import { ObjectType } from "../types/ObjectType"; 18 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 19 | 20 | 21 | export default class PricingTier implements WarrantObject { 22 | pricingTierId: string; 23 | meta?: { [key: string]: any } 24 | 25 | constructor(pricingTierId: string, meta: { [key: string]: any }) { 26 | this.pricingTierId = pricingTierId; 27 | this.meta = meta; 28 | } 29 | 30 | // 31 | // Static methods 32 | // 33 | public static async create(pricingTier: CreatePricingTierParams = {}, options: WarrantRequestOptions = {}): Promise { 34 | const response = await ObjectModule.create({ 35 | object: { 36 | objectType: ObjectType.PricingTier, 37 | objectId: pricingTier.pricingTierId, 38 | }, 39 | meta: pricingTier.meta, 40 | }, options); 41 | 42 | return new PricingTier(response.objectId, response.meta); 43 | } 44 | 45 | public static async get(pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 46 | const response = await ObjectModule.get({ 47 | objectType: ObjectType.PricingTier, 48 | objectId: pricingTierId, 49 | }, options); 50 | 51 | return new PricingTier(response.objectId, response.meta); 52 | } 53 | 54 | public static async update(pricingTierId: string, params: UpdatePricingTierParams, options: WarrantRequestOptions = {}): Promise { 55 | const response = await ObjectModule.update({ 56 | objectType: ObjectType.PricingTier, 57 | objectId: pricingTierId, 58 | }, { meta: params.meta }, options); 59 | 60 | return new PricingTier(response.objectId, response.meta); 61 | } 62 | 63 | public static async delete(pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 64 | return ObjectModule.delete({ 65 | objectType: ObjectType.PricingTier, 66 | objectId: pricingTierId, 67 | }, options); 68 | } 69 | 70 | public static async listPricingTiers(listParams: ListPricingTierParams = {}, options: WarrantRequestOptions = {}): Promise> { 71 | try { 72 | const response = await ObjectModule.list({ 73 | objectType: ObjectType.PricingTier, 74 | ...listParams, 75 | }, options); 76 | 77 | const pricingTiers: PricingTier[] = response.results.map((object: BaseWarrantObject) => new PricingTier(object.objectId, object.meta)); 78 | return { 79 | ...response, 80 | results: pricingTiers, 81 | }; 82 | } catch (e) { 83 | throw e; 84 | } 85 | } 86 | 87 | public static async listPricingTiersForTenant(tenantId: string, listParams: ListPricingTierParams = {}, options: WarrantRequestOptions = {}): Promise> { 88 | try { 89 | const queryResponse = await WarrantModule.query(`select pricing-tier where tenant:${tenantId} is *`, listParams, options); 90 | const pricingTiers: PricingTier[] = queryResponse.results.map((queryResult: QueryResult) => new PricingTier(queryResult.objectId, queryResult.meta)); 91 | return { 92 | ...queryResponse, 93 | results: pricingTiers, 94 | }; 95 | } catch (e) { 96 | throw e; 97 | } 98 | } 99 | 100 | public static async assignPricingTierToTenant(tenantId: string, pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 101 | return WarrantModule.create({ 102 | object: { 103 | objectType: ObjectType.PricingTier, 104 | objectId: pricingTierId, 105 | }, 106 | relation: "member", 107 | subject: { 108 | objectType: ObjectType.Tenant, 109 | objectId: tenantId, 110 | } 111 | }, options); 112 | } 113 | 114 | public static async removePricingTierFromTenant(tenantId: string, pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 115 | return WarrantModule.delete({ 116 | object: { 117 | objectType: ObjectType.PricingTier, 118 | objectId: pricingTierId, 119 | }, 120 | relation: "member", 121 | subject: { 122 | objectType: ObjectType.Tenant, 123 | objectId: tenantId, 124 | } 125 | }, options); 126 | } 127 | 128 | public static async listPricingTiersForUser(userId: string, listParams: ListPricingTierParams = {}, options: WarrantRequestOptions = {}): Promise> { 129 | try { 130 | const queryResponse = await WarrantModule.query(`select pricing-tier where user:${userId} is *`, listParams, options); 131 | const pricingTiers: PricingTier[] = queryResponse.results.map((queryResult: QueryResult) => new PricingTier(queryResult.objectId, queryResult.meta)); 132 | return { 133 | ...queryResponse, 134 | results: pricingTiers, 135 | }; 136 | } catch (e) { 137 | throw e; 138 | } 139 | } 140 | 141 | public static async assignPricingTierToUser(userId: string, pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 142 | return WarrantModule.create({ 143 | object: { 144 | objectType: ObjectType.PricingTier, 145 | objectId: pricingTierId, 146 | }, 147 | relation: "member", 148 | subject: { 149 | objectType: ObjectType.User, 150 | objectId: userId, 151 | } 152 | }, options); 153 | } 154 | 155 | public static async removePricingTierFromUser(userId: string, pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 156 | return WarrantModule.delete({ 157 | object: { 158 | objectType: ObjectType.PricingTier, 159 | objectId: pricingTierId, 160 | }, 161 | relation: "member", 162 | subject: { 163 | objectType: ObjectType.User, 164 | objectId: userId, 165 | } 166 | }, options); 167 | } 168 | 169 | // Instance methods 170 | public async listFeatures(listParams: ListFeatureParams = {}, options: WarrantRequestOptions = {}): Promise> { 171 | return Feature.listFeaturesForPricingTier(this.pricingTierId, listParams, options); 172 | } 173 | 174 | public async assignFeature(featureId: string, options: WarrantRequestOptions = {}): Promise { 175 | return Feature.assignFeatureToPricingTier(this.pricingTierId, featureId, options); 176 | } 177 | 178 | public async removeFeature(featureId: string, options: WarrantRequestOptions = {}): Promise { 179 | return Feature.removeFeatureFromPricingTier(this.pricingTierId, featureId, options); 180 | } 181 | 182 | public async hasFeature(params: FeatureCheckParams, options: WarrantRequestOptions = {}): Promise { 183 | return Authorization.hasFeature({ featureId: params.featureId, subject: { objectType: ObjectType.PricingTier, objectId: this.pricingTierId }, context: params.context }, options); 184 | } 185 | 186 | // WarrantObject methods 187 | public getObjectType(): string { 188 | return ObjectType.PricingTier; 189 | } 190 | 191 | public getObjectId(): string { 192 | return this.pricingTierId; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/modules/Role.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListResponse, 3 | WarrantObject, 4 | BaseWarrantObject, 5 | ListPermissionParams, 6 | QueryResult, 7 | CreateRoleParams, 8 | ListRoleParams, 9 | UpdateRoleParams, 10 | Warrant, 11 | PermissionCheckParams, 12 | } from "../types"; 13 | import Authorization from "./Authorization"; 14 | import ObjectModule from "./ObjectModule"; 15 | import Permission from "./Permission"; 16 | import WarrantModule from "./WarrantModule"; 17 | import { ObjectType } from "../types/ObjectType"; 18 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 19 | 20 | 21 | export default class Role implements WarrantObject { 22 | roleId: string; 23 | meta?: { [key: string]: any } 24 | 25 | constructor(roleId: string, meta: { [key: string]: any }) { 26 | this.roleId = roleId; 27 | this.meta = meta; 28 | } 29 | 30 | // 31 | // Static methods 32 | // 33 | public static async create(role: CreateRoleParams = {}, options: WarrantRequestOptions = {}): Promise { 34 | const response = await ObjectModule.create({ 35 | object: { 36 | objectType: ObjectType.Role, 37 | objectId: role.roleId, 38 | }, 39 | meta: role.meta, 40 | }, options) 41 | 42 | return new Role(response.objectId, response.meta); 43 | } 44 | 45 | public static async get(roleId: string, options: WarrantRequestOptions = {}): Promise { 46 | const response = await ObjectModule.get({ 47 | objectType: ObjectType.Role, 48 | objectId: roleId, 49 | }, options); 50 | 51 | return new Role(response.objectId, response.meta); 52 | } 53 | 54 | public static async update(roleId: string, params: UpdateRoleParams, options: WarrantRequestOptions = {}): Promise { 55 | const response = await ObjectModule.update({ 56 | objectType: ObjectType.Role, 57 | objectId: roleId, 58 | }, { meta: params.meta }, options); 59 | 60 | return new Role(response.objectId, response.meta); 61 | } 62 | 63 | public static async delete(roleId: string, options: WarrantRequestOptions = {}): Promise { 64 | return ObjectModule.delete({ 65 | objectType: ObjectType.Role, 66 | objectId: roleId, 67 | }, options); 68 | } 69 | 70 | public static async listRoles(listParams: ListRoleParams = {}, options: WarrantRequestOptions = {}): Promise> { 71 | const response = await ObjectModule.list({ 72 | objectType: ObjectType.Role, 73 | ...listParams, 74 | }, options); 75 | 76 | const roles: Role[] = response.results.map((object: BaseWarrantObject) => new Role(object.objectId, object.meta)); 77 | return { 78 | ...response, 79 | results: roles, 80 | }; 81 | } 82 | 83 | public static async listRolesForUser(userId: string, listParams: ListRoleParams = {}, options: WarrantRequestOptions = {}): Promise> { 84 | const queryResponse = await WarrantModule.query(`select role where user:${userId} is *`, listParams, options); 85 | const roles: Role[] = queryResponse.results.map((queryResult: QueryResult) => new Role(queryResult.objectId, queryResult.meta)); 86 | 87 | return { 88 | ...queryResponse, 89 | results: roles, 90 | }; 91 | } 92 | 93 | public static async assignRoleToUser(userId: string, roleId: string, options: WarrantRequestOptions = {}): Promise { 94 | return WarrantModule.create({ 95 | object: { 96 | objectType: ObjectType.Role, 97 | objectId: roleId, 98 | }, 99 | relation: "member", 100 | subject: { 101 | objectType: ObjectType.User, 102 | objectId: userId, 103 | } 104 | }, options); 105 | } 106 | 107 | public static async removeRoleFromUser(userId: string, roleId: string, options: WarrantRequestOptions = {}): Promise { 108 | return WarrantModule.delete({ 109 | object: { 110 | objectType: ObjectType.Role, 111 | objectId: roleId, 112 | }, 113 | relation: "member", 114 | subject: { 115 | objectType: ObjectType.User, 116 | objectId: userId, 117 | } 118 | }, options); 119 | } 120 | 121 | // Instance methods 122 | public async listPermissions(listParams: ListPermissionParams = {}, options: WarrantRequestOptions = {}): Promise> { 123 | return Permission.listPermissionsForRole(this.roleId, listParams, options); 124 | } 125 | 126 | public async assignPermission(permissionId: string, options: WarrantRequestOptions = {}): Promise { 127 | return Permission.assignPermissionToRole(this.roleId, permissionId, options); 128 | } 129 | 130 | public async removePermission(permissionId: string, options: WarrantRequestOptions = {}): Promise { 131 | return Permission.removePermissionFromRole(this.roleId, permissionId, options); 132 | } 133 | 134 | public async hasPermission(params: PermissionCheckParams, options: WarrantRequestOptions = {}): Promise { 135 | return Authorization.hasPermission({ permissionId: params.permissionId, subject: { objectType: ObjectType.Role, objectId: this.roleId }, context: params.context }, options); 136 | } 137 | 138 | // WarrantObject methods 139 | public getObjectType(): string { 140 | return ObjectType.Role; 141 | } 142 | 143 | public getObjectId(): string { 144 | return this.roleId; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/modules/Session.ts: -------------------------------------------------------------------------------- 1 | import WarrantClient from "../WarrantClient"; 2 | import { API_VERSION, SELF_SERVICE_DASH_URL_BASE } from "../constants"; 3 | import { SelfServiceSessionParams, SessionParams } from "../types"; 4 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 5 | 6 | export default class Session { 7 | /** 8 | * Creates an authorization session in Warrant for the specified userId and returns a session token that can be used to 9 | * make authorization requests to the Warrant API scoped to the specified userId and tenantId. 10 | * 11 | * @param session A session object containing the userId, redirectUrl, and optional tenantId for which the authorization session should be created. 12 | * @returns A session token that can be passed to any of the Warrant client-side SDKs to allow the SDK to make client-side authorization checks for the specified user. 13 | */ 14 | public static async createAuthorizationSession(session: SessionParams, options: WarrantRequestOptions = {}): Promise { 15 | try { 16 | const sess = await WarrantClient.httpClient.post({ 17 | url: `/${API_VERSION}/sessions`, 18 | data: { 19 | ...session, 20 | type: "sess", 21 | }, 22 | options, 23 | }); 24 | 25 | return sess.token; 26 | } catch (e) { 27 | throw e; 28 | } 29 | } 30 | 31 | /** 32 | * Creates a self-service session in Warrant for the user with the specified userId and returns a session token that can be used to 33 | * make authorized requests to the Warrant API scoped to the specified userId and tenantId. 34 | * 35 | * @param session A session object containing the userId, redirectUrl, and optional tenantId for which the self service session should be created. 36 | * @returns A url pointing to the self-service dashboard that will allow the specified user to make changes to the roles and permissions of users in their tenant. 37 | */ 38 | public static async createSelfServiceSession(session: SelfServiceSessionParams, redirectUrl: string, options: WarrantRequestOptions = {}): Promise { 39 | try { 40 | const sess = await WarrantClient.httpClient.post({ 41 | url: `/${API_VERSION}/sessions`, 42 | data: { 43 | ...session, 44 | type: "ssdash", 45 | }, 46 | options, 47 | }); 48 | 49 | return `${SELF_SERVICE_DASH_URL_BASE}/${sess.token}?redirectUrl=${redirectUrl}`; 50 | } catch (e) { 51 | throw e; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/modules/Tenant.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListFeatureParams, 3 | ListResponse, 4 | WarrantObject, 5 | BaseWarrantObject, 6 | ListPricingTierParams, 7 | QueryResult, 8 | CreateTenantParams, 9 | DeleteTenantParams, 10 | ListTenantParams, 11 | UpdateTenantParams, 12 | ListUserParams, 13 | Warrant, 14 | FeatureCheckParams, 15 | } from "../types"; 16 | import Authorization from "./Authorization"; 17 | import Feature from "./Feature"; 18 | import ObjectModule from "./ObjectModule"; 19 | import PricingTier from "./PricingTier"; 20 | import User from "./User"; 21 | import WarrantModule from "./WarrantModule"; 22 | import { ObjectType } from "../types/ObjectType"; 23 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 24 | 25 | 26 | export default class Tenant implements WarrantObject { 27 | tenantId: string; 28 | meta?: { [key: string]: any } 29 | 30 | constructor(tenantId: string, meta: { [key: string]: any }) { 31 | this.tenantId = tenantId; 32 | this.meta = meta; 33 | } 34 | 35 | // 36 | // Static methods 37 | // 38 | public static async create(params: CreateTenantParams = {}, options: WarrantRequestOptions = {}): Promise { 39 | const response = await ObjectModule.create({ 40 | object: { 41 | objectType: ObjectType.Tenant, 42 | objectId: params.tenantId, 43 | }, 44 | meta: params.meta, 45 | }, options); 46 | 47 | return new Tenant(response.objectId, response.meta); 48 | } 49 | 50 | public static async batchCreate(params: CreateTenantParams[], options: WarrantRequestOptions = {}): Promise { 51 | const response = await ObjectModule.batchCreate(params.map((tenant: CreateTenantParams) => ({ 52 | object: { 53 | objectType: ObjectType.Tenant, 54 | objectId: tenant.tenantId, 55 | }, 56 | meta: tenant.meta, 57 | })), options); 58 | 59 | return response.map((object: BaseWarrantObject) => new Tenant(object.objectId, object.meta)); 60 | } 61 | 62 | public static async get(tenantId: string, options: WarrantRequestOptions = {}): Promise { 63 | const response = await ObjectModule.get({ 64 | objectType: ObjectType.Tenant, 65 | objectId: tenantId, 66 | }, options); 67 | 68 | return new Tenant(response.objectId, response.meta); 69 | } 70 | 71 | public static async update(tenantId: string, params: UpdateTenantParams, options: WarrantRequestOptions = {}): Promise { 72 | const response = await ObjectModule.update({ 73 | objectType: ObjectType.Tenant, 74 | objectId: tenantId, 75 | }, { meta: params.meta }, options); 76 | 77 | return new Tenant(response.objectId, response.meta); 78 | } 79 | 80 | public static async delete(tenantId: string, options: WarrantRequestOptions = {}): Promise { 81 | return ObjectModule.delete({ 82 | objectType: ObjectType.Tenant, 83 | objectId: tenantId, 84 | }, options); 85 | } 86 | 87 | public static async batchDelete(params: DeleteTenantParams[], options: WarrantRequestOptions = {}): Promise { 88 | return await ObjectModule.batchDelete(params.map((tenant: DeleteTenantParams) => ({ 89 | object: { 90 | objectType: ObjectType.Tenant, 91 | objectId: tenant.tenantId, 92 | }, 93 | })), options); 94 | } 95 | 96 | public static async listTenants(listParams: ListTenantParams = {}, options: WarrantRequestOptions = {}): Promise> { 97 | const response = await ObjectModule.list({ 98 | objectType: ObjectType.Tenant, 99 | ...listParams, 100 | }, options); 101 | const tenants: Tenant[] = response.results.map((object: BaseWarrantObject) => new Tenant(object.objectId, object.meta)); 102 | 103 | return { 104 | ...response, 105 | results: tenants, 106 | }; 107 | } 108 | 109 | public static async listTenantsForUser(userId: string, listParams: ListTenantParams = {}, options: WarrantRequestOptions = {}): Promise> { 110 | const queryResponse = await WarrantModule.query(`select tenant where user:${userId} is *`, listParams, options); 111 | const tenants: Tenant[] = queryResponse.results.map((queryResult: QueryResult) => new Tenant(queryResult.objectId, queryResult.meta)); 112 | 113 | return { 114 | ...queryResponse, 115 | results: tenants, 116 | }; 117 | } 118 | 119 | // 120 | // Instance methods 121 | // 122 | public async listUsers(listParams: ListUserParams = {}, options: WarrantRequestOptions = {}): Promise> { 123 | return User.listUsersForTenant(this.tenantId, listParams, options); 124 | } 125 | 126 | public async assignUser(userId: string, role: string, options: WarrantRequestOptions = {}): Promise { 127 | return User.assignUserToTenant(this.tenantId, userId, role, options); 128 | } 129 | 130 | public async removeUser(userId: string, role: string, options: WarrantRequestOptions = {}): Promise { 131 | return User.removeUserFromTenant(this.tenantId, userId, role, options); 132 | } 133 | 134 | public async listPricingTiers(listParams: ListPricingTierParams = {}, options: WarrantRequestOptions = {}): Promise> { 135 | return PricingTier.listPricingTiersForTenant(this.tenantId, listParams, options); 136 | } 137 | 138 | public async assignPricingTier(pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 139 | return PricingTier.assignPricingTierToTenant(this.tenantId, pricingTierId, options); 140 | } 141 | 142 | public async removePricingTier(pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 143 | return PricingTier.removePricingTierFromTenant(this.tenantId, pricingTierId, options); 144 | } 145 | 146 | public async listFeatures(listParams: ListFeatureParams = {}, options: WarrantRequestOptions = {}): Promise> { 147 | return Feature.listFeaturesForTenant(this.tenantId, listParams, options); 148 | } 149 | 150 | public async assignFeature(featureId: string, options: WarrantRequestOptions = {}): Promise { 151 | return Feature.assignFeatureToTenant(this.tenantId, featureId, options); 152 | } 153 | 154 | public async removeFeature(featureId: string, options: WarrantRequestOptions = {}): Promise { 155 | return Feature.removeFeatureFromTenant(this.tenantId, featureId, options); 156 | } 157 | 158 | public async hasFeature(params: FeatureCheckParams, options: WarrantRequestOptions = {}): Promise { 159 | return Authorization.hasFeature({ featureId: params.featureId, subject: { objectType: ObjectType.Tenant, objectId: this.tenantId }, context: params.context }, options); 160 | } 161 | 162 | // WarrantObject methods 163 | public getObjectType(): string { 164 | return ObjectType.Tenant; 165 | } 166 | 167 | public getObjectId(): string { 168 | return this.tenantId; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/modules/User.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ListFeatureParams, 3 | ListResponse, 4 | WarrantObject, 5 | BaseWarrantObject, 6 | ListPermissionParams, 7 | ListPricingTierParams, 8 | QueryResult, 9 | ListRoleParams, 10 | CreateUserParams, 11 | DeleteUserParams, 12 | ListUserParams, 13 | UpdateUserParams, 14 | ListTenantParams, 15 | Warrant, 16 | PermissionCheckParams, 17 | FeatureCheckParams, 18 | } from "../types"; 19 | import Authorization from "./Authorization"; 20 | import Feature from "./Feature"; 21 | import ObjectModule from "./ObjectModule"; 22 | import Permission from "./Permission"; 23 | import PricingTier from "./PricingTier"; 24 | import Role from "./Role"; 25 | import Tenant from "./Tenant"; 26 | import WarrantModule from "./WarrantModule"; 27 | import { ObjectType } from "../types/ObjectType"; 28 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 29 | 30 | 31 | export default class User implements WarrantObject { 32 | userId: string; 33 | meta?: { [key: string]: any } 34 | 35 | constructor(userId: string, meta: { [key: string]: any }) { 36 | this.userId = userId; 37 | this.meta = meta; 38 | } 39 | 40 | // 41 | // Static methods 42 | // 43 | public static async create(params: CreateUserParams = {}, options: WarrantRequestOptions = {}): Promise { 44 | const response = await ObjectModule.create({ 45 | object: { 46 | objectType: ObjectType.User, 47 | objectId: params.userId, 48 | }, 49 | meta: params.meta, 50 | }, options); 51 | 52 | return new User(response.objectId, response.meta); 53 | } 54 | 55 | public static async batchCreate(params: CreateUserParams[], options: WarrantRequestOptions = {}): Promise { 56 | const response = await ObjectModule.batchCreate(params.map((user: CreateUserParams) => ({ 57 | object: { 58 | objectType: ObjectType.User, 59 | objectId: user.userId, 60 | }, 61 | meta: user.meta, 62 | } )), options); 63 | 64 | return response.map((object: BaseWarrantObject) => new User(object.objectId, object.meta)); 65 | } 66 | 67 | public static async get(userId: string, options: WarrantRequestOptions = {}): Promise { 68 | const response = await ObjectModule.get({ 69 | objectType: ObjectType.User, 70 | objectId: userId, 71 | }, options) 72 | 73 | return new User(response.objectId, response.meta); 74 | } 75 | 76 | public static async update(userId: string, params: UpdateUserParams, options: WarrantRequestOptions = {}): Promise { 77 | const response = await ObjectModule.update({ 78 | objectType: ObjectType.User, 79 | objectId: userId, 80 | }, { meta: params.meta }, options); 81 | 82 | return new User(response.objectId, response.meta); 83 | } 84 | 85 | public static async delete(userId: string, options: WarrantRequestOptions = {}): Promise { 86 | return ObjectModule.delete({ 87 | objectType: ObjectType.User, 88 | objectId: userId, 89 | }, options); 90 | } 91 | 92 | public static async batchDelete(params: DeleteUserParams[], options: WarrantRequestOptions = {}): Promise { 93 | return await ObjectModule.batchDelete(params.map((user: DeleteUserParams) => ({ 94 | object: { 95 | objectType: ObjectType.User, 96 | objectId: user.userId, 97 | }, 98 | })), options); 99 | } 100 | 101 | public static async listUsers(listParams: ListUserParams = {}, options: WarrantRequestOptions = {}): Promise> { 102 | const response = await ObjectModule.list({ 103 | objectType: ObjectType.User, 104 | ...listParams, 105 | }, options); 106 | 107 | const users: User[] = response.results.map((object: BaseWarrantObject) => new User(object.objectId, object.meta)); 108 | return { 109 | ...response, 110 | results: users, 111 | }; 112 | } 113 | 114 | public static async listUsersForTenant(tenantId: string, listParams: ListUserParams = {}, options: WarrantRequestOptions = {}): Promise> { 115 | const queryResponse = await WarrantModule.query(`select * of type user for tenant:${tenantId}`, listParams, options); 116 | const users: User[] = queryResponse.results.map((queryResult: QueryResult) => new User(queryResult.objectId, queryResult.meta)); 117 | 118 | return { 119 | ...queryResponse, 120 | results: users, 121 | }; 122 | } 123 | 124 | public static async assignUserToTenant(tenantId: string, userId: string, role: string, options: WarrantRequestOptions = {}): Promise { 125 | return WarrantModule.create({ 126 | object: { 127 | objectType: ObjectType.Tenant, 128 | objectId: tenantId, 129 | }, 130 | relation: role, 131 | subject: { 132 | objectType: ObjectType.User, 133 | objectId: userId, 134 | } 135 | }, options); 136 | } 137 | 138 | public static async removeUserFromTenant(tenantId: string, userId: string, role: string, options: WarrantRequestOptions = {}): Promise { 139 | return WarrantModule.delete({ 140 | object: { 141 | objectType: ObjectType.Tenant, 142 | objectId: tenantId, 143 | }, 144 | relation: role, 145 | subject: { 146 | objectType: ObjectType.User, 147 | objectId: userId, 148 | } 149 | }, options); 150 | } 151 | 152 | // 153 | // Instance methods 154 | // 155 | public async listTenants(listParams: ListTenantParams = {}, options: WarrantRequestOptions = {}): Promise> { 156 | return Tenant.listTenantsForUser(this.userId, listParams, options); 157 | } 158 | 159 | public async listRoles(listParams: ListRoleParams = {}, options: WarrantRequestOptions = {}): Promise> { 160 | return Role.listRolesForUser(this.userId, listParams, options); 161 | } 162 | 163 | public async assignRole(roleId: string, options: WarrantRequestOptions = {}): Promise { 164 | return Role.assignRoleToUser(this.userId, roleId, options); 165 | } 166 | 167 | public async removeRole(roleId: string, options: WarrantRequestOptions = {}): Promise { 168 | return Role.removeRoleFromUser(this.userId, roleId, options); 169 | } 170 | 171 | public async listPermissions(listParams: ListPermissionParams = {}, options: WarrantRequestOptions = {}): Promise> { 172 | return Permission.listPermissionsForUser(this.userId, listParams, options); 173 | } 174 | 175 | public async assignPermission(permissionId: string, options: WarrantRequestOptions = {}): Promise { 176 | return Permission.assignPermissionToUser(this.userId, permissionId, options); 177 | } 178 | 179 | public async removePermission(permissionId: string, options: WarrantRequestOptions = {}): Promise { 180 | return Permission.removePermissionFromUser(this.userId, permissionId, options); 181 | } 182 | 183 | public async hasPermission(params: PermissionCheckParams, options: WarrantRequestOptions = {}): Promise { 184 | return Authorization.hasPermission({ permissionId: params.permissionId, subject: { objectType: ObjectType.User, objectId: this.userId }, context: params.context }, options); 185 | } 186 | 187 | public async listPricingTiers(listParams: ListPricingTierParams = {}, options: WarrantRequestOptions = {}): Promise> { 188 | return PricingTier.listPricingTiersForUser(this.userId, listParams, options); 189 | } 190 | 191 | public async assignPricingTier(pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 192 | return PricingTier.assignPricingTierToUser(this.userId, pricingTierId, options); 193 | } 194 | 195 | public async removePricingTier(pricingTierId: string, options: WarrantRequestOptions = {}): Promise { 196 | return PricingTier.removePricingTierFromUser(this.userId, pricingTierId, options); 197 | } 198 | 199 | public async listFeatures(listParams: ListFeatureParams = {}, options: WarrantRequestOptions = {}): Promise> { 200 | return Feature.listFeaturesForUser(this.userId, listParams, options); 201 | } 202 | 203 | public async assignFeature(featureId: string, options: WarrantRequestOptions = {}): Promise { 204 | return Feature.assignFeatureToUser(this.userId, featureId, options); 205 | } 206 | 207 | public async removeFeature(featureId: string, options: WarrantRequestOptions = {}): Promise { 208 | return Feature.removeFeatureFromUser(this.userId, featureId, options); 209 | } 210 | 211 | public async hasFeature(params: FeatureCheckParams, options: WarrantRequestOptions = {}): Promise { 212 | return Authorization.hasFeature({ featureId: params.featureId, subject: { objectType: ObjectType.User, objectId: this.userId }, context: params.context }, options); 213 | } 214 | 215 | // WarrantObject methods 216 | public getObjectType(): string { 217 | return ObjectType.User; 218 | } 219 | 220 | public getObjectId(): string { 221 | return this.userId; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/modules/WarrantModule.ts: -------------------------------------------------------------------------------- 1 | import WarrantClient from "../WarrantClient"; 2 | import { API_VERSION } from "../constants"; 3 | import { 4 | ListResponse, 5 | ListWarrantParams, 6 | QueryParams, 7 | QueryListParams, 8 | QueryResult, 9 | Warrant, 10 | WarrantParams, 11 | } from "../types"; 12 | import { isWarrantObject } from "../types/Object"; 13 | import { isSubject } from "../types/Warrant"; 14 | import { WarrantRequestOptions } from "../types/WarrantRequestOptions"; 15 | 16 | export default class WarrantModule { 17 | public static async create(params: WarrantParams, options: WarrantRequestOptions = {}): Promise { 18 | return await WarrantClient.httpClient.post({ 19 | url: `/${API_VERSION}/warrants`, 20 | data: { 21 | objectType: isWarrantObject(params.object) ? params.object.getObjectType() : params.object.objectType, 22 | objectId: isWarrantObject(params.object) ? params.object.getObjectId() : params.object.objectId, 23 | relation: params.relation, 24 | subject: isSubject(params.subject) ? params.subject : { objectType: params.subject.getObjectType(), objectId: params.subject.getObjectId() }, 25 | policy: params.policy 26 | }, 27 | options, 28 | }); 29 | } 30 | 31 | public static async batchCreate(params: WarrantParams[], options: WarrantRequestOptions = {}): Promise { 32 | return await WarrantClient.httpClient.post({ 33 | url: `/${API_VERSION}/warrants`, 34 | data: params.map((warrant: WarrantParams) => ({ 35 | objectType: isWarrantObject(warrant.object) ? warrant.object.getObjectType() : warrant.object.objectType, 36 | objectId: isWarrantObject(warrant.object) ? warrant.object.getObjectId() : warrant.object.objectId, 37 | relation: warrant.relation, 38 | subject: isSubject(warrant.subject) ? warrant.subject : { objectType: warrant.subject.getObjectType(), objectId: warrant.subject.getObjectId() }, 39 | policy: warrant.policy, 40 | })), 41 | options, 42 | }); 43 | } 44 | 45 | public static async list(params: ListWarrantParams = {}, options: WarrantRequestOptions = {}): Promise> { 46 | return await WarrantClient.httpClient.get({ 47 | url: `/${API_VERSION}/warrants`, 48 | params, 49 | options, 50 | }); 51 | } 52 | 53 | public static async query(query: string | QueryParams, listParams: QueryListParams = {}, options: WarrantRequestOptions = {}): Promise> { 54 | const params: any = { 55 | ...listParams, 56 | }; 57 | 58 | // preserve backwards compatibility 59 | if (typeof query === "string") { 60 | params.q = query; 61 | } else { 62 | params.q = query.query; 63 | if (query.context) { 64 | params.context = JSON.stringify(query.context); 65 | } 66 | } 67 | 68 | return await WarrantClient.httpClient.get({ 69 | url: `/${API_VERSION}/query`, 70 | params: params, 71 | options, 72 | }); 73 | } 74 | 75 | public static async delete(params: WarrantParams, options: WarrantRequestOptions = {}): Promise { 76 | const response = await WarrantClient.httpClient.delete({ 77 | url: `/${API_VERSION}/warrants`, 78 | data: { 79 | objectType: isWarrantObject(params.object) ? params.object.getObjectType() : params.object.objectType, 80 | objectId: isWarrantObject(params.object) ? params.object.getObjectId() : params.object.objectId, 81 | relation: params.relation, 82 | subject: isSubject(params.subject) ? params.subject : { objectType: params.subject.getObjectType(), objectId: params.subject.getObjectId() }, 83 | policy: params.policy 84 | }, 85 | options, 86 | }); 87 | return response.warrantToken; 88 | } 89 | 90 | public static async batchDelete(params: WarrantParams[], options: WarrantRequestOptions = {}): Promise { 91 | const response = await WarrantClient.httpClient.delete({ 92 | url: `/${API_VERSION}/warrants`, 93 | data: params.map((warrant: WarrantParams) => ({ 94 | objectType: isWarrantObject(warrant.object) ? warrant.object.getObjectType() : warrant.object.objectType, 95 | objectId: isWarrantObject(warrant.object) ? warrant.object.getObjectId() : warrant.object.objectId, 96 | relation: warrant.relation, 97 | subject: isSubject(warrant.subject) ? warrant.subject : { objectType: warrant.subject.getObjectType(), objectId: warrant.subject.getObjectId() }, 98 | policy: warrant.policy, 99 | })), 100 | options, 101 | }); 102 | return response.warrantToken; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/types/ApiError.ts: -------------------------------------------------------------------------------- 1 | export enum ErrorCode { 2 | GenericError = "generic_error", 3 | InternalError = "internal_error", 4 | InvalidArgument = "invalid_argument", 5 | InvalidParameter = "invalid_parameter", 6 | InvalidRequest = "invalid_request", 7 | MissingRequiredParameter = "missing_required_parameter", 8 | NotFound = "not_found", 9 | DuplicateRecord = "duplicate_record", 10 | Unauthorized = "unauthorized", 11 | } 12 | 13 | export default class ApiError extends Error { 14 | public code: string; 15 | public message: string; 16 | public name: string; 17 | 18 | constructor(code: string, message: string) { 19 | super(message); 20 | this.code = code; 21 | this.message = message; 22 | this.name = ApiError.codeToName(code); 23 | } 24 | 25 | public toString(): string { 26 | return `${this.name} [${this.code}]: ${this.message}`; 27 | } 28 | 29 | private static codeToName(code: string): string { 30 | switch (code) { 31 | case ErrorCode.DuplicateRecord: 32 | return "DuplicateRecordError"; 33 | case ErrorCode.InternalError: 34 | return "InternalError"; 35 | case ErrorCode.InvalidRequest: 36 | return "InvalidRequestError"; 37 | case ErrorCode.InvalidParameter: 38 | return "InvalidParameterError"; 39 | case ErrorCode.MissingRequiredParameter: 40 | return "MissingRequiredParameterError"; 41 | case ErrorCode.NotFound: 42 | return "NotFoundError"; 43 | case ErrorCode.Unauthorized: 44 | return "UnauthorizedError"; 45 | default: 46 | return "ApiError"; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/types/Check.ts: -------------------------------------------------------------------------------- 1 | import { 2 | isWarrantObject, 3 | WarrantObject, 4 | WarrantObjectLiteral, 5 | } from "./Object"; 6 | import { 7 | isSubject, 8 | PolicyContext, 9 | Subject, 10 | } from "./Warrant"; 11 | 12 | export enum CheckOp { 13 | AllOf = "allOf", 14 | AnyOf = "anyOf", 15 | Batch = "batch", 16 | } 17 | 18 | export interface CheckWarrantParams { 19 | object: WarrantObject | WarrantObjectLiteral; 20 | relation: string; 21 | subject: WarrantObject | Subject; 22 | context?: PolicyContext; 23 | } 24 | 25 | export interface CheckParams extends CheckWarrantParams { 26 | debug?: boolean; 27 | } 28 | 29 | export interface CheckManyParams { 30 | op?: CheckOp; 31 | warrants: CheckWarrantParams[]; 32 | debug?: boolean; 33 | } 34 | 35 | export interface BatchCheckParams { 36 | warrants: CheckWarrantParams[]; 37 | debug?: boolean; 38 | } 39 | 40 | export interface FeatureCheckParams { 41 | featureId: string; 42 | subject: WarrantObject | Subject; 43 | context?: PolicyContext; 44 | debug?: boolean; 45 | } 46 | 47 | export interface PermissionCheckParams { 48 | permissionId: string; 49 | subject: WarrantObject | Subject; 50 | context?: PolicyContext; 51 | debug?: boolean; 52 | } 53 | 54 | export interface CheckWarrant { 55 | objectType: string; 56 | objectId: string; 57 | relation: string; 58 | subject: Subject; 59 | context: PolicyContext; 60 | } 61 | 62 | export interface Check { 63 | op?: CheckOp; 64 | warrants: CheckWarrant[]; 65 | debug?: boolean; 66 | } 67 | 68 | export function checkWarrantParamsToCheckWarrant(checkWarrantParams: CheckWarrantParams): CheckWarrant { 69 | return { 70 | objectType: isWarrantObject(checkWarrantParams.object) ? checkWarrantParams.object.getObjectType() : checkWarrantParams.object.objectType, 71 | objectId: isWarrantObject(checkWarrantParams.object) ? checkWarrantParams.object.getObjectId() : checkWarrantParams.object.objectId, 72 | relation: checkWarrantParams.relation, 73 | subject: isSubject(checkWarrantParams.subject) ? checkWarrantParams.subject : { objectType: checkWarrantParams.subject.getObjectType(), objectId: checkWarrantParams.subject.getObjectId() }, 74 | context: checkWarrantParams.context 75 | } 76 | } 77 | 78 | export interface CheckResult { 79 | code?: number; 80 | result: string; 81 | isImplicit: boolean; 82 | } 83 | -------------------------------------------------------------------------------- /src/types/Config.ts: -------------------------------------------------------------------------------- 1 | export default interface Config { 2 | apiKey: string; 3 | endpoint?: string; 4 | authorizeEndpoint?: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/types/Feature.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | 3 | export interface CreateFeatureParams { 4 | featureId?: string; 5 | meta?: { [key: string]: any }; 6 | } 7 | 8 | export interface ListFeatureParams extends ListParams {} 9 | 10 | export interface UpdateFeatureParams { 11 | meta?: { [key: string]: any }; 12 | } 13 | -------------------------------------------------------------------------------- /src/types/List.ts: -------------------------------------------------------------------------------- 1 | export interface ListParams { 2 | prevCursor?: string; 3 | nextCursor?: string; 4 | sortBy?: string; 5 | sortOrder?: string; 6 | limit?: number; 7 | } 8 | 9 | export interface ListResponse { 10 | results: T[]; 11 | prevCursor?: string; 12 | nextCursor?: string; 13 | } 14 | -------------------------------------------------------------------------------- /src/types/Object.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | 3 | export interface WarrantObject { 4 | getObjectType(): string; 5 | getObjectId(): string; 6 | } 7 | 8 | export interface WarrantObjectLiteral { 9 | objectType: string; 10 | objectId?: string; 11 | } 12 | 13 | export function isWarrantObject(object: any): object is WarrantObject { 14 | return object.getObjectType !== undefined && object.getObjectId !== undefined; 15 | } 16 | 17 | export interface CreateObjectParams { 18 | object: WarrantObject | WarrantObjectLiteral; 19 | meta?: { [key: string]: any }; 20 | } 21 | 22 | export interface GetObjectParams { 23 | object: WarrantObject | WarrantObjectLiteral; 24 | } 25 | 26 | export interface ListObjectParams extends ListParams { 27 | objectType?: string; 28 | q?: string; 29 | } 30 | 31 | export interface UpdateObjectParams { 32 | meta?: { [key: string]: any }; 33 | } 34 | 35 | export interface DeleteObjectParams { 36 | object: WarrantObject | WarrantObjectLiteral; 37 | } 38 | 39 | export interface BaseWarrantObject { 40 | objectType: string; 41 | objectId: string; 42 | meta?: { [key: string]: any }; 43 | } 44 | -------------------------------------------------------------------------------- /src/types/ObjectType.ts: -------------------------------------------------------------------------------- 1 | export enum ObjectType { 2 | Feature = "feature", 3 | Permission = "permission", 4 | PricingTier = "pricing-tier", 5 | Role = "role", 6 | Tenant = "tenant", 7 | User = "user", 8 | } 9 | -------------------------------------------------------------------------------- /src/types/Permission.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | 3 | export interface ListPermissionParams extends ListParams { } 4 | 5 | export interface CreatePermissionParams { 6 | permissionId?: string; 7 | meta?: { [key: string]: any }; 8 | } 9 | 10 | export interface ListPermissionParams extends ListParams {} 11 | 12 | export interface UpdatePermissionParams { 13 | meta?: { [key: string]: any }; 14 | } 15 | -------------------------------------------------------------------------------- /src/types/PricingTier.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | 3 | export interface ListPricingTierParams extends ListParams { } 4 | 5 | export interface CreatePricingTierParams { 6 | pricingTierId?: string; 7 | meta?: { [key: string]: any }; 8 | } 9 | 10 | export interface ListPricingTierParams extends ListParams {} 11 | 12 | export interface UpdatePricingTierParams { 13 | meta?: { [key: string]: any }; 14 | } 15 | -------------------------------------------------------------------------------- /src/types/Query.ts: -------------------------------------------------------------------------------- 1 | import { Warrant, PolicyContext } from "./Warrant"; 2 | import { ListParams } from "./List"; 3 | 4 | export interface QueryParams { 5 | query: string; 6 | context?: PolicyContext; 7 | } 8 | 9 | export interface QueryResult { 10 | objectType: string; 11 | objectId: string; 12 | warrant: Warrant; 13 | isImplicit: boolean; 14 | meta?: { [key: string]: any; }; 15 | } 16 | 17 | export interface QueryListParams extends ListParams {} 18 | -------------------------------------------------------------------------------- /src/types/Role.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | 3 | export interface ListRoleParams extends ListParams { } 4 | 5 | export interface CreateRoleParams { 6 | roleId?: string; 7 | meta?: { [key: string]: any }; 8 | } 9 | 10 | export interface ListRoleParams extends ListParams {} 11 | 12 | export interface UpdateRoleParams { 13 | meta?: { [key: string]: any }; 14 | } 15 | -------------------------------------------------------------------------------- /src/types/Session.ts: -------------------------------------------------------------------------------- 1 | import { PolicyContext } from "./Warrant"; 2 | 3 | export interface SessionParams { 4 | userId: string; 5 | ttl?: number; 6 | context?: PolicyContext; 7 | } 8 | 9 | export interface SelfServiceSessionParams extends SessionParams { 10 | tenantId: string; 11 | selfServiceStrategy: SelfServiceStrategy; 12 | } 13 | 14 | export enum SelfServiceStrategy { 15 | RBAC = "rbac", 16 | FGAC = "fgac", 17 | } 18 | -------------------------------------------------------------------------------- /src/types/Tenant.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | 3 | export interface ListTenantParams extends ListParams { } 4 | 5 | export interface CreateTenantParams { 6 | tenantId?: string; 7 | meta?: { [key: string]: any }; 8 | } 9 | 10 | export interface ListTenantParams extends ListParams {} 11 | 12 | export interface UpdateTenantParams { 13 | meta?: { [key: string]: any }; 14 | } 15 | 16 | export interface DeleteTenantParams { 17 | tenantId: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/types/User.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | 3 | export interface ListUserParams extends ListParams { } 4 | 5 | export interface CreateUserParams { 6 | userId?: string; 7 | meta?: { [key: string]: any }; 8 | } 9 | 10 | export interface ListUserParams extends ListParams {} 11 | 12 | export interface UpdateUserParams { 13 | meta?: { [key: string]: any }; 14 | } 15 | 16 | export interface DeleteUserParams { 17 | userId: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/types/Warrant.ts: -------------------------------------------------------------------------------- 1 | import { ListParams } from "./List"; 2 | import { WarrantObject, WarrantObjectLiteral } from "./Object"; 3 | 4 | export interface ListWarrantParams extends ListParams { 5 | objectType?: string; 6 | objectId?: string; 7 | relation?: string; 8 | subjectType?: string; 9 | subjectId?: string; 10 | } 11 | 12 | export interface PolicyContext { 13 | [key: string]: any; 14 | } 15 | 16 | export interface Subject { 17 | objectType: string; 18 | objectId: string; 19 | relation?: string; 20 | } 21 | 22 | export function isSubject(object: any): object is Subject { 23 | return Object.prototype.hasOwnProperty.call(object, "objectType") 24 | && Object.prototype.hasOwnProperty.call(object, "objectId") 25 | } 26 | 27 | export interface Warrant { 28 | objectType: string; 29 | objectId: string; 30 | relation: string; 31 | subject: Subject; 32 | policy?: string; 33 | warrantToken?: string; 34 | } 35 | 36 | export interface WarrantParams { 37 | object: WarrantObject | WarrantObjectLiteral; 38 | relation: string; 39 | subject: WarrantObject | Subject; 40 | policy?: string; 41 | } 42 | -------------------------------------------------------------------------------- /src/types/WarrantRequestOptions.ts: -------------------------------------------------------------------------------- 1 | export interface WarrantRequestOptions { 2 | warrantToken?: string; 3 | } 4 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ApiError } from "./ApiError"; 2 | export { default as Config } from "./Config"; 3 | export { 4 | ListParams, ListResponse 5 | } from "./List"; 6 | export { 7 | BaseWarrantObject, 8 | WarrantObject, 9 | WarrantObjectLiteral, 10 | CreateObjectParams, 11 | GetObjectParams, 12 | ListObjectParams, 13 | UpdateObjectParams, 14 | DeleteObjectParams, 15 | } from "./Object"; 16 | export { 17 | CreateFeatureParams, 18 | ListFeatureParams, 19 | UpdateFeatureParams, 20 | } from "./Feature"; 21 | export { 22 | CreatePermissionParams, 23 | ListPermissionParams, 24 | UpdatePermissionParams, 25 | } from "./Permission"; 26 | export { 27 | CreatePricingTierParams, 28 | ListPricingTierParams, 29 | UpdatePricingTierParams, 30 | } from "./PricingTier"; 31 | export { 32 | CreateRoleParams, 33 | ListRoleParams, 34 | UpdateRoleParams, 35 | } from "./Role"; 36 | export { 37 | SessionParams, 38 | SelfServiceSessionParams, 39 | SelfServiceStrategy, 40 | } from "./Session"; 41 | export { 42 | CreateTenantParams, 43 | ListTenantParams, 44 | UpdateTenantParams, 45 | DeleteTenantParams, 46 | } from "./Tenant"; 47 | export { 48 | CreateUserParams, 49 | ListUserParams, 50 | UpdateUserParams, 51 | DeleteUserParams, 52 | } from "./User"; 53 | export { 54 | Warrant, 55 | WarrantParams, 56 | ListWarrantParams, 57 | PolicyContext, 58 | Subject, 59 | } from "./Warrant"; 60 | export { 61 | QueryParams, 62 | QueryResult, 63 | QueryListParams, 64 | } from "./Query"; 65 | export { 66 | Check, 67 | CheckParams, 68 | CheckManyParams, 69 | CheckOp, 70 | CheckWarrant, 71 | CheckWarrantParams, 72 | FeatureCheckParams, 73 | PermissionCheckParams, 74 | } from "./Check"; 75 | -------------------------------------------------------------------------------- /test/LiveTest.spec.ts: -------------------------------------------------------------------------------- 1 | import WarrantClient from "../src/WarrantClient"; 2 | import { SelfServiceStrategy } from "../src/types"; 3 | import { assert } from "chai"; 4 | 5 | // Remove .skip and add your API_KEY to run tests 6 | describe.skip('Live Test', function () { 7 | before(function () { 8 | this.warrant = new WarrantClient({ apiKey: "", endpoint: "https://api.warrant.dev" }); 9 | }); 10 | 11 | it('CRUD users', async function () { 12 | const user1 = await this.warrant.User.create(); 13 | assert(user1.userId); 14 | assert.strictEqual(user1.meta, undefined); 15 | 16 | let user2 = await this.warrant.User.create({ userId: "zz_some_id", meta: { email: "test@email.com" } }); 17 | let refetchedUser = await this.warrant.User.get(user2.userId, { warrantToken: "latest" }); 18 | assert.strictEqual(user2.userId, refetchedUser.userId); 19 | assert.deepStrictEqual(user2.meta, { email: "test@email.com" }); 20 | 21 | user2 = await this.warrant.User.update("zz_some_id", { meta: { email: "updated@email.com" } }); 22 | refetchedUser = await this.warrant.User.get("zz_some_id", { warrantToken: "latest" }); 23 | assert.strictEqual(refetchedUser.userId, "zz_some_id"); 24 | assert.deepStrictEqual(refetchedUser.meta, { email: "updated@email.com" }); 25 | 26 | let usersList = await this.warrant.User.listUsers({ limit: 10 }, { warrantToken: "latest" }); 27 | assert.strictEqual(usersList.results.length, 2); 28 | assert.strictEqual(usersList.results[0].userId, user1.userId); 29 | assert.strictEqual(usersList.results[1].userId, refetchedUser.userId); 30 | 31 | let warrantToken = await this.warrant.User.delete(user1.userId); 32 | assert(warrantToken); 33 | warrantToken = await this.warrant.User.delete(user2.userId); 34 | assert(warrantToken); 35 | usersList = await this.warrant.User.listUsers({ limit: 10 }, { warrantToken: "latest" }); 36 | assert.strictEqual(usersList.results.length, 0); 37 | }); 38 | 39 | it('batch create/delete users', async function() { 40 | const users = await this.warrant.User.batchCreate([ 41 | { userId: "user-a", meta: { name: "User A" }}, 42 | { userId: "user-b" }, 43 | { userId: "user-c", meta: { email: "user-c@email.com" }}, 44 | ]) 45 | assert.strictEqual(users.length, 3); 46 | 47 | let fetchedUsers = await this.warrant.User.listUsers({ limit: 10 }, { warrantToken: "latest" }); 48 | assert.strictEqual(fetchedUsers.results.length, 3); 49 | assert.strictEqual(fetchedUsers.results[0].userId, "user-a"); 50 | assert.deepStrictEqual(fetchedUsers.results[0].meta, { name: "User A" }); 51 | assert.strictEqual(fetchedUsers.results[1].userId, "user-b"); 52 | assert.strictEqual(fetchedUsers.results[2].userId, "user-c"); 53 | assert.deepStrictEqual(fetchedUsers.results[2].meta, { email: "user-c@email.com" }); 54 | 55 | let warrantToken = await this.warrant.User.batchDelete([ 56 | { userId: "user-a" }, 57 | { userId: "user-b" }, 58 | { userId: "user-c" }, 59 | ]); 60 | assert(warrantToken); 61 | 62 | fetchedUsers = await this.warrant.User.listUsers({ limit: 10 }, { warrantToken: "latest" }); 63 | assert.strictEqual(fetchedUsers.results.length, 0); 64 | }); 65 | 66 | it('CRUD tenants', async function () { 67 | const tenant1 = await this.warrant.Tenant.create(); 68 | assert(tenant1.tenantId); 69 | assert.strictEqual(tenant1.meta, undefined); 70 | 71 | let tenant2 = await this.warrant.Tenant.create({ tenantId: "zz_some_tenant_id", meta: { name: "new_name" } }); 72 | let refetchedTenant = await this.warrant.Tenant.get(tenant2.tenantId, { warrantToken: "latest" }); 73 | assert.strictEqual(tenant2.tenantId, refetchedTenant.tenantId); 74 | assert.deepStrictEqual(tenant2.meta, { name: "new_name" }); 75 | 76 | tenant2 = await this.warrant.Tenant.update("zz_some_tenant_id", { meta: { name: "updated_name" } }); 77 | refetchedTenant = await this.warrant.Tenant.get("zz_some_tenant_id", { warrantToken: "latest" }); 78 | assert.strictEqual(refetchedTenant.tenantId, "zz_some_tenant_id"); 79 | assert.deepStrictEqual(refetchedTenant.meta, { name: "updated_name" }); 80 | 81 | let tenantsList = await this.warrant.Tenant.listTenants({ limit: 10 }, { warrantToken: "latest" }); 82 | assert.strictEqual(tenantsList.results.length, 2); 83 | assert.strictEqual(tenantsList.results[0].tenantId, tenant1.tenantId); 84 | assert.strictEqual(tenantsList.results[1].tenantId, refetchedTenant.tenantId); 85 | 86 | let warrantToken = await this.warrant.Tenant.delete(tenant1.tenantId); 87 | assert(warrantToken); 88 | warrantToken = await this.warrant.Tenant.delete(tenant2.tenantId); 89 | assert(warrantToken); 90 | tenantsList = await this.warrant.Tenant.listTenants({ limit: 10 }, { warrantToken: "latest" }); 91 | assert.strictEqual(tenantsList.results.length, 0); 92 | }); 93 | 94 | it('batch create/delete tenants', async function() { 95 | const tenants = await this.warrant.Tenant.batchCreate([ 96 | { tenantId: "tenant-a", meta: { name: "Tenant A" }}, 97 | { tenantId: "tenant-b" }, 98 | { tenantId: "tenant-c", meta: { description: "Company C" }}, 99 | ]) 100 | assert.strictEqual(tenants.length, 3); 101 | 102 | let fetchedTenants = await this.warrant.Tenant.listTenants({ limit: 10 }, { warrantToken: "latest" }); 103 | assert.strictEqual(fetchedTenants.results.length, 3); 104 | assert.strictEqual(fetchedTenants.results[0].tenantId, "tenant-a"); 105 | assert.deepStrictEqual(fetchedTenants.results[0].meta, { name: "Tenant A" }); 106 | assert.strictEqual(fetchedTenants.results[1].tenantId, "tenant-b"); 107 | assert.strictEqual(fetchedTenants.results[2].tenantId, "tenant-c"); 108 | assert.deepStrictEqual(fetchedTenants.results[2].meta, { description: "Company C" }); 109 | 110 | let warrantToken = await this.warrant.Tenant.batchDelete([ 111 | { tenantId: "tenant-a" }, 112 | { tenantId: "tenant-b" }, 113 | { tenantId: "tenant-c" }, 114 | ]); 115 | assert(warrantToken); 116 | 117 | fetchedTenants = await this.warrant.Tenant.listTenants({ limit: 10 }, { warrantToken: "latest" }); 118 | assert.strictEqual(fetchedTenants.results.length, 0); 119 | }); 120 | 121 | it('CRUD roles', async function () { 122 | const adminRole = await this.warrant.Role.create({ roleId: "admin", meta: { name: "Admin", description: "The admin role" } }); 123 | assert.strictEqual(adminRole.roleId, "admin"); 124 | assert.deepStrictEqual(adminRole.meta, { name: "Admin", description: "The admin role" }); 125 | 126 | let viewerRole = await this.warrant.Role.create({ roleId: "viewer", meta: { name: "Viewer", description: "The viewer role" } }); 127 | let refetchedRole = await this.warrant.Role.get(viewerRole.roleId, { warrantToken: "latest" }); 128 | assert.strictEqual(viewerRole.roleId, refetchedRole.roleId); 129 | assert.deepStrictEqual(viewerRole.meta, refetchedRole.meta); 130 | 131 | viewerRole = await this.warrant.Role.update("viewer", { meta: { name: "Viewer Updated", description: "Updated desc" } }); 132 | refetchedRole = await this.warrant.Role.get("viewer", { warrantToken: "latest" }); 133 | assert.strictEqual(refetchedRole.roleId, "viewer"); 134 | assert.deepStrictEqual(refetchedRole.meta, { name: "Viewer Updated", description: "Updated desc" }); 135 | 136 | let rolesList = await this.warrant.Role.listRoles({ limit: 10 }, { warrantToken: "latest" }); 137 | assert.strictEqual(rolesList.results.length, 2); 138 | assert.strictEqual(rolesList.results[0].roleId, "admin"); 139 | assert.strictEqual(rolesList.results[1].roleId, "viewer"); 140 | 141 | let warrantToken = await this.warrant.Role.delete(adminRole.roleId); 142 | assert(warrantToken); 143 | warrantToken = await this.warrant.Role.delete(viewerRole.roleId); 144 | assert(warrantToken); 145 | rolesList = await this.warrant.Role.listRoles({ limit: 10 }, { warrantToken: "latest" }); 146 | assert.strictEqual(rolesList.results.length, 0); 147 | }); 148 | 149 | it('CRUD permissions', async function () { 150 | const permission1 = await this.warrant.Permission.create({ permissionId: "perm1", meta: { name: "Permission 1", description: "Permission with id 1" } }); 151 | assert.strictEqual(permission1.permissionId, "perm1"); 152 | assert.deepStrictEqual(permission1.meta, { name: "Permission 1", description: "Permission with id 1" }); 153 | 154 | let permission2 = await this.warrant.Permission.create({ permissionId: "perm2", name: "Permission 2", description: "Permission with id 2" }); 155 | let refetchedPermission = await this.warrant.Permission.get(permission2.permissionId, { warrantToken: "latest" }); 156 | assert.strictEqual(permission2.permissionId, refetchedPermission.permissionId); 157 | assert.deepStrictEqual(permission2.meta, refetchedPermission.meta); 158 | 159 | permission2 = await this.warrant.Permission.update("perm2", { meta: { name: "Permission 2 Updated", description: "Updated desc" } }); 160 | refetchedPermission = await this.warrant.Permission.get("perm2", { warrantToken: "latest" }); 161 | assert.strictEqual(refetchedPermission.permissionId, "perm2"); 162 | assert.deepStrictEqual(refetchedPermission.meta, { name: "Permission 2 Updated", description: "Updated desc" }); 163 | 164 | let permissionsList = await this.warrant.Permission.listPermissions({ limit: 10 }, { warrantToken: "latest" }); 165 | assert.strictEqual(permissionsList.results.length, 2); 166 | assert.strictEqual(permissionsList.results[0].permissionId, "perm1"); 167 | assert.strictEqual(permissionsList.results[1].permissionId, "perm2"); 168 | 169 | let warrantToken = await this.warrant.Permission.delete(permission1.permissionId); 170 | assert(warrantToken); 171 | warrantToken = await this.warrant.Permission.delete(permission2.permissionId); 172 | assert(warrantToken); 173 | permissionsList = await this.warrant.Permission.listPermissions({ limit: 10 }, { warrantToken: "latest" }); 174 | assert.strictEqual(permissionsList.results.length, 0); 175 | }); 176 | 177 | it('CRUD features', async function () { 178 | const feature1 = await this.warrant.Feature.create({ featureId: "new-feature", meta: { name: "New Feature" } }); 179 | assert.strictEqual(feature1.featureId, "new-feature"); 180 | assert.deepStrictEqual(feature1.meta, { name: "New Feature" }); 181 | 182 | let feature2 = await this.warrant.Feature.create({ featureId: "feature-2" }); 183 | let refetchedFeature = await this.warrant.Feature.get(feature2.featureId, { warrantToken: "latest" }); 184 | assert.strictEqual(feature2.featureId, refetchedFeature.featureId); 185 | assert.deepStrictEqual(feature2.meta, refetchedFeature.meta); 186 | 187 | feature2 = await this.warrant.Feature.update("feature-2", { meta: { name: "Feature 2", description: "Second feature" } }); 188 | refetchedFeature = await this.warrant.Feature.get(feature2.featureId, { warrantToken: "latest" }); 189 | assert.strictEqual(refetchedFeature.featureId, "feature-2"); 190 | assert.deepStrictEqual(refetchedFeature.meta, { name: "Feature 2", description: "Second feature" }); 191 | 192 | let featuresList = await this.warrant.Feature.listFeatures({ limit: 10 }, { warrantToken: "latest" }); 193 | assert.strictEqual(featuresList.results.length, 2); 194 | assert.strictEqual(featuresList.results[0].featureId, refetchedFeature.featureId); 195 | assert.strictEqual(featuresList.results[1].featureId, feature1.featureId); 196 | 197 | let warrantToken = await this.warrant.Feature.delete(feature1.featureId); 198 | assert(warrantToken); 199 | warrantToken = await this.warrant.Feature.delete(feature2.featureId); 200 | assert(warrantToken); 201 | featuresList = await this.warrant.Feature.listFeatures({ limit: 10 }, { warrantToken: "latest" }); 202 | assert.strictEqual(featuresList.results.length, 0); 203 | }); 204 | 205 | it('CRUD pricing tiers', async function () { 206 | const tier1 = await this.warrant.PricingTier.create({ pricingTierId: "new-tier-1", meta: { name: "New Pricing Tier" } }); 207 | assert.strictEqual(tier1.pricingTierId, "new-tier-1"); 208 | assert.deepStrictEqual(tier1.meta, { name: "New Pricing Tier" }); 209 | 210 | let tier2 = await this.warrant.PricingTier.create({ pricingTierId: "tier-2" }); 211 | let refetchedTier = await this.warrant.PricingTier.get(tier2.pricingTierId, { warrantToken: "latest" }); 212 | assert.strictEqual(tier2.pricingTierId, refetchedTier.pricingTierId); 213 | assert.deepStrictEqual(tier2.meta, refetchedTier.meta); 214 | 215 | tier2 = await this.warrant.PricingTier.update("tier-2", { meta: { name: "Tier 2", description: "New pricing tier" } }); 216 | refetchedTier = await this.warrant.PricingTier.get(tier2.pricingTierId, { warrantToken: "latest" }); 217 | assert.strictEqual(refetchedTier.pricingTierId, "tier-2"); 218 | assert.deepStrictEqual(refetchedTier.meta, { name: "Tier 2", description: "New pricing tier" }); 219 | 220 | let tiersList = await this.warrant.PricingTier.listPricingTiers({ limit: 10 }, { warrantToken: "latest" }); 221 | assert.strictEqual(tiersList.results.length, 2); 222 | assert.strictEqual(tiersList.results[0].pricingTierId, tier1.pricingTierId); 223 | assert.strictEqual(tiersList.results[1].pricingTierId, tier2.pricingTierId); 224 | 225 | let warrantToken = await this.warrant.PricingTier.delete(tier1.pricingTierId); 226 | assert(warrantToken); 227 | warrantToken = await this.warrant.PricingTier.delete(tier2.pricingTierId); 228 | assert(warrantToken); 229 | tiersList = await this.warrant.PricingTier.listPricingTiers({ limit: 10 }, { warrantToken: "latest" }); 230 | assert.strictEqual(tiersList.results.length, 0); 231 | }); 232 | 233 | it('CRUD objects', async function () { 234 | const object1 = await this.warrant.Object.create({ object: { objectType: "document" }}); 235 | assert.strictEqual(object1.objectType, "document"); 236 | assert(object1.objectId); 237 | assert.strictEqual(object1.meta, undefined); 238 | 239 | let object2 = await this.warrant.Object.create({ object: { objectType: "folder", objectId: "planning" }}); 240 | let refetchedObject = await this.warrant.Object.get(object2, { warrantToken: "latest" }); 241 | assert.strictEqual(refetchedObject.objectType, object2.objectType); 242 | assert.strictEqual(refetchedObject.objectId, object2.objectId); 243 | assert.deepStrictEqual(refetchedObject.meta, object2.meta); 244 | 245 | object2 = await this.warrant.Object.update(object2, { meta: { description: "Second document" } } ); 246 | refetchedObject = await this.warrant.Object.get(object2, { warrantToken: "latest" }); 247 | assert.strictEqual(refetchedObject.objectType, object2.objectType); 248 | assert.strictEqual(refetchedObject.objectId, object2.objectId); 249 | assert.deepStrictEqual(refetchedObject.meta, object2.meta); 250 | 251 | let objectsList = await this.warrant.Object.list({ sortBy: "createdAt", limit: 10 }, { warrantToken: "latest" }); 252 | assert.strictEqual(objectsList.results.length, 2); 253 | assert.strictEqual(objectsList.results[0].objectType, object1.objectType); 254 | assert.strictEqual(objectsList.results[0].objectId, object1.objectId); 255 | assert.strictEqual(objectsList.results[1].objectType, object2.objectType); 256 | assert.strictEqual(objectsList.results[1].objectId, object2.objectId); 257 | 258 | objectsList = await this.warrant.Object.list({ sortBy: "createdAt", limit: 10, q: "planning" }, { warrantToken: "latest" }); 259 | assert.strictEqual(objectsList.results.length, 1); 260 | assert.strictEqual(objectsList.results[0].objectType, object2.objectType); 261 | assert.strictEqual(objectsList.results[0].objectId, object2.objectId); 262 | 263 | let warrantToken = await this.warrant.Object.delete(object1); 264 | assert(warrantToken); 265 | warrantToken = await this.warrant.Object.delete(object2); 266 | assert(warrantToken); 267 | objectsList = await this.warrant.Object.list({ sortBy: "createdAt", limit: 10 }, { warrantToken: "latest" }); 268 | assert.strictEqual(objectsList.results.length, 0); 269 | }); 270 | 271 | it('batch create/delete objects', async function () { 272 | const objects = await this.warrant.Object.batchCreate([ 273 | { object: { objectType: "document", objectId: "document-a" }}, 274 | { object: { objectType: "document", objectId: "document-b" }}, 275 | { object: { objectType: "folder", objectId: "resources" }, meta: { description: "Helpful documents" }}, 276 | ]); 277 | assert.strictEqual(objects.length, 3); 278 | 279 | let fetchedObjects = await this.warrant.Object.list({ limit: 10 }, { warrantToken: "latest" }); 280 | assert.strictEqual(fetchedObjects.results.length, 3); 281 | assert.strictEqual(fetchedObjects.results[0].objectType, "document"); 282 | assert.strictEqual(fetchedObjects.results[0].objectId, "document-a"); 283 | assert.strictEqual(fetchedObjects.results[1].objectType, "document"); 284 | assert.strictEqual(fetchedObjects.results[1].objectId, "document-b"); 285 | assert.strictEqual(fetchedObjects.results[2].objectType, "folder"); 286 | assert.strictEqual(fetchedObjects.results[2].objectId, "resources"); 287 | assert.deepStrictEqual(fetchedObjects.results[2].meta, { description: "Helpful documents" }); 288 | 289 | const folderResource = await this.warrant.Object.get({ 290 | objectType: "folder", 291 | objectId: "resources" 292 | }, { warrantToken: "latest" }); 293 | assert.strictEqual(folderResource.objectType, "folder"); 294 | assert.strictEqual(folderResource.objectId, "resources"); 295 | 296 | let warrantToken = await this.warrant.Object.batchDelete([ 297 | { object: { objectType: "document", objectId: "document-a" } }, 298 | { object: { objectType: "document", objectId: "document-b" } }, 299 | { object: folderResource }, 300 | ]); 301 | assert(warrantToken); 302 | fetchedObjects = await this.warrant.Object.list({ limit: 10 }, { warrantToken: "latest" }); 303 | assert.strictEqual(fetchedObjects.results.length, 0); 304 | }); 305 | 306 | it('multi-tenancy example', async function () { 307 | // Create users 308 | const user1 = await this.warrant.User.create(); 309 | const user2 = await this.warrant.User.create(); 310 | 311 | // Create tenants 312 | const tenant1 = await this.warrant.Tenant.create({ tenantId: "tenant-1", meta: { name: "Tenant 1" }}); 313 | const tenant2 = await this.warrant.Tenant.create({ tenantId: "tenant-2", meta: { name: "Tenant 2" }}); 314 | 315 | let user1TenantsList = await this.warrant.Tenant.listTenantsForUser(user1.userId, { limit: 100 }, { warrantToken: "latest" }); 316 | let tenant1UsersList = await this.warrant.User.listUsersForTenant("tenant-1", { limit: 100 }, { warrantToken: "latest" }); 317 | assert.strictEqual(user1TenantsList.results.length, 0); 318 | assert.strictEqual(tenant1UsersList.results.length, 0); 319 | 320 | // Assign user1 -> tenant1 321 | await this.warrant.User.assignUserToTenant(tenant1.tenantId, user1.userId, "member"); 322 | 323 | user1TenantsList = await this.warrant.Tenant.listTenantsForUser(user1.userId, { limit: 100 }, { warrantToken: "latest" }); 324 | assert.strictEqual(user1TenantsList.results.length, 1); 325 | assert.strictEqual(user1TenantsList.results[0].tenantId, "tenant-1"); 326 | assert.deepStrictEqual(user1TenantsList.results[0].meta, { name: "Tenant 1" }); 327 | 328 | tenant1UsersList = await this.warrant.User.listUsersForTenant("tenant-1", { limit: 100 }, { warrantToken: "latest" }); 329 | assert.strictEqual(tenant1UsersList.results.length, 1); 330 | assert.strictEqual(tenant1UsersList.results[0].userId, user1.userId); 331 | assert.strictEqual(tenant1UsersList.results[0].meta, undefined); 332 | 333 | // Remove user1 -> tenant1 334 | await this.warrant.User.removeUserFromTenant(tenant1.tenantId, user1.userId, "member"); 335 | 336 | user1TenantsList = await this.warrant.Tenant.listTenantsForUser(user1.userId, { limit: 100 }, { warrantToken: "latest" }); 337 | assert.strictEqual(user1TenantsList.results.length, 0); 338 | 339 | tenant1UsersList = await this.warrant.User.listUsersForTenant(tenant1.tenantId, { limit: 100 }, { warrantToken: "latest" }); 340 | assert.strictEqual(tenant1UsersList.results.length, 0); 341 | 342 | // Clean up 343 | let warrantToken = await this.warrant.User.delete(user1.userId); 344 | assert(warrantToken); 345 | warrantToken = await this.warrant.User.delete(user2.userId); 346 | assert(warrantToken); 347 | warrantToken = await this.warrant.Tenant.delete(tenant1.tenantId); 348 | assert(warrantToken); 349 | warrantToken = await this.warrant.Tenant.delete(tenant2.tenantId); 350 | assert(warrantToken); 351 | }); 352 | 353 | it('RBAC example', async function () { 354 | // Create users 355 | const adminUser = await this.warrant.User.create(); 356 | const viewerUser = await this.warrant.User.create(); 357 | 358 | // Create roles 359 | const adminRole = await this.warrant.Role.create({ roleId: "admin", meta: { name: "Admin", description: "The admin role" }}); 360 | const viewerRole = await this.warrant.Role.create({ roleId: "viewer", meta: { name: "Viewer", description: "The viewer role" }}); 361 | 362 | // Create permissions 363 | const createPermission = await this.warrant.Permission.create({ permissionId: "create-report", meta: { name: "Create Report", description: "Permission to create reports" }}); 364 | const viewPermission = await this.warrant.Permission.create({ permissionId: "view-report", meta: { name: "View Report", description: "Permission to view reports" }}); 365 | 366 | let adminUserRolesList = await this.warrant.Role.listRolesForUser(adminUser.userId, { limit: 100 }, { warrantToken: "latest" }); 367 | let adminRolePermissionsList = await this.warrant.Permission.listPermissionsForRole(adminRole.roleId, { limit: 100 }, { warrantToken: "latest" }); 368 | assert.strictEqual(adminUserRolesList.results.length, 0); 369 | assert.strictEqual(adminRolePermissionsList.results.length, 0); 370 | 371 | let adminUserHasPermission = await this.warrant.Authorization.hasPermission({ permissionId: "create-report", subject: adminUser }, { warrantToken: "latest" }); 372 | assert.strictEqual(adminUserHasPermission, false); 373 | 374 | // Assign 'create-report' -> admin role -> admin user 375 | await this.warrant.Permission.assignPermissionToRole(adminRole.roleId, createPermission.permissionId); 376 | await this.warrant.Role.assignRoleToUser(adminUser.userId, adminRole.roleId); 377 | 378 | adminUserHasPermission = await this.warrant.Authorization.hasPermission({ permissionId: "create-report", subject: adminUser }, { warrantToken: "latest" }); 379 | assert.strictEqual(adminUserHasPermission, true); 380 | 381 | adminUserRolesList = await this.warrant.Role.listRolesForUser(adminUser.userId, { limit: 100 }, { warrantToken: "latest" }); 382 | assert.strictEqual(adminUserRolesList.results.length, 1); 383 | assert.strictEqual(adminUserRolesList.results[0].roleId, adminRole.roleId); 384 | assert.deepStrictEqual(adminUserRolesList.results[0].meta, { name: "Admin", description: "The admin role" }); 385 | 386 | adminRolePermissionsList = await this.warrant.Permission.listPermissionsForRole(adminRole.roleId, { limit: 100 }, { warrantToken: "latest" }); 387 | assert.strictEqual(adminRolePermissionsList.results.length, 1); 388 | assert.strictEqual(adminRolePermissionsList.results[0].permissionId, createPermission.permissionId); 389 | assert.deepStrictEqual(adminRolePermissionsList.results[0].meta, { name: "Create Report", description: "Permission to create reports" }); 390 | 391 | await this.warrant.Permission.removePermissionFromRole(adminRole.roleId, createPermission.permissionId); 392 | await this.warrant.Role.removeRoleFromUser(adminUser.userId, adminRole.roleId); 393 | 394 | adminUserHasPermission = await this.warrant.Authorization.hasPermission({ permissionId: "create-report", subject: adminUser }, { warrantToken: "latest" }); 395 | assert.strictEqual(adminUserHasPermission, false); 396 | 397 | adminUserRolesList = await this.warrant.Role.listRolesForUser(adminUser.userId, { limit: 100 }, { warrantToken: "latest" }); 398 | assert.strictEqual(adminUserRolesList.results.length, 0); 399 | 400 | adminRolePermissionsList = await this.warrant.Permission.listPermissionsForRole(adminRole.roleId, { limit: 100 }, { warrantToken: "latest" }); 401 | assert.strictEqual(adminRolePermissionsList.results.length, 0); 402 | 403 | // Assign 'view-report' -> viewer user 404 | let viewerUserHasPermission = await this.warrant.Authorization.hasPermission({ permissionId: "view-report", subject: viewerUser }); 405 | assert.strictEqual(viewerUserHasPermission, false); 406 | 407 | let viewerUserPermissionsList = await this.warrant.Permission.listPermissionsForUser(viewerUser.userId, { limit: 100 }, { warrantToken: "latest" }); 408 | assert.strictEqual(viewerUserPermissionsList.results.length, 0); 409 | 410 | await this.warrant.Permission.assignPermissionToUser(viewerUser.userId, viewPermission.permissionId); 411 | 412 | viewerUserHasPermission = await this.warrant.Authorization.hasPermission({ permissionId: "view-report", subject: viewerUser }, { warrantToken: "latest" }); 413 | assert.strictEqual(viewerUserHasPermission, true); 414 | 415 | viewerUserPermissionsList = await this.warrant.Permission.listPermissionsForUser(viewerUser.userId, { limit: 100 }, { warrantToken: "latest" }); 416 | assert.strictEqual(viewerUserPermissionsList.results.length, 1); 417 | assert.strictEqual(viewerUserPermissionsList.results[0].permissionId, viewPermission.permissionId); 418 | assert.deepStrictEqual(viewerUserPermissionsList.results[0].meta, { name: "View Report", description: "Permission to view reports" }); 419 | 420 | await this.warrant.Permission.removePermissionFromUser(viewerUser.userId, viewPermission.permissionId); 421 | 422 | viewerUserHasPermission = await this.warrant.Authorization.hasPermission({ permissionId: "view-report", subject: viewerUser }, { warrantToken: "latest" }); 423 | assert.strictEqual(viewerUserHasPermission, false); 424 | 425 | viewerUserPermissionsList = await this.warrant.Permission.listPermissionsForUser(viewerUser.userId, { limit: 100 }, { warrantToken: "latest" }); 426 | assert.strictEqual(viewerUserPermissionsList.results.length, 0); 427 | 428 | // Clean up 429 | let warrantToken = await this.warrant.User.delete(adminUser.userId); 430 | assert(warrantToken); 431 | warrantToken = await this.warrant.User.delete(viewerUser.userId); 432 | assert(warrantToken); 433 | warrantToken = await this.warrant.Role.delete(adminRole.roleId); 434 | assert(warrantToken); 435 | warrantToken = await this.warrant.Role.delete(viewerRole.roleId); 436 | assert(warrantToken); 437 | warrantToken = await this.warrant.Permission.delete(createPermission.permissionId); 438 | assert(warrantToken); 439 | warrantToken = await this.warrant.Permission.delete(viewPermission.permissionId); 440 | assert(warrantToken); 441 | }); 442 | 443 | it('pricing tiers, features, and users example', async function () { 444 | // Create users 445 | const freeUser = await this.warrant.User.create(); 446 | const paidUser = await this.warrant.User.create(); 447 | 448 | // Create pricing tiers 449 | const freeTier = await this.warrant.PricingTier.create({ pricingTierId: "free", meta: { name: "Free Tier" }}); 450 | const paidTier = await this.warrant.PricingTier.create({ pricingTierId: "paid" }); 451 | 452 | // Create features 453 | const customFeature = await this.warrant.Feature.create({ featureId: "custom-feature", meta: { name: "Custom Feature" }}); 454 | const feature1 = await this.warrant.Feature.create({ featureId: "feature-1" }); 455 | const feature2 = await this.warrant.Feature.create({ featureId: "feature-2" }); 456 | 457 | // Assign 'custom-feature' -> paid user 458 | let paidUserHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "custom-feature", subject: paidUser }, { warrantToken: "latest" }); 459 | assert.strictEqual(paidUserHasFeature, false); 460 | 461 | let paidUserFeaturesList = await this.warrant.Feature.listFeaturesForUser(paidUser.userId, { limit: 100 }, { warrantToken: "latest" }); 462 | assert.strictEqual(paidUserFeaturesList.results.length, 0); 463 | 464 | await this.warrant.Feature.assignFeatureToUser(paidUser.userId, customFeature.featureId); 465 | 466 | paidUserHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "custom-feature", subject: paidUser }, { warrantToken: "latest" }); 467 | assert.strictEqual(paidUserHasFeature, true); 468 | 469 | paidUserFeaturesList = await this.warrant.Feature.listFeaturesForUser(paidUser.userId, { limit: 100 }, { warrantToken: "latest" }); 470 | assert.strictEqual(paidUserFeaturesList.results.length, 1); 471 | assert.strictEqual(paidUserFeaturesList.results[0].featureId, "custom-feature"); 472 | assert.deepStrictEqual(paidUserFeaturesList.results[0].meta, { name: "Custom Feature" }) 473 | 474 | await this.warrant.Feature.removeFeatureFromUser(paidUser.userId, customFeature.featureId); 475 | 476 | paidUserHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "custom-feature", subject: paidUser }, { warrantToken: "latest" }); 477 | assert.strictEqual(paidUserHasFeature, false); 478 | 479 | paidUserFeaturesList = await this.warrant.Feature.listFeaturesForUser(paidUser.userId, { limit: 100 }, { warrantToken: "latest" }); 480 | assert.strictEqual(paidUserFeaturesList.results.length, 0); 481 | 482 | // Assign 'feature-1' -> 'free' tier -> free user 483 | let freeUserHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "feature-1", subject: freeUser }, { warrantToken: "latest" }); 484 | assert.strictEqual(freeUserHasFeature, false); 485 | 486 | let freeTierFeaturesList = await this.warrant.Feature.listFeaturesForPricingTier(freeTier.pricingTierId, { limit: 100 }, { warrantToken: "latest" }); 487 | assert.strictEqual(freeTierFeaturesList.results.length, 0); 488 | 489 | let freeUserTiersList = await this.warrant.PricingTier.listPricingTiersForUser(freeUser.userId, { limit: 100 }, { warrantToken: "latest" }); 490 | assert.strictEqual(freeUserTiersList.results.length, 0); 491 | 492 | await this.warrant.Feature.assignFeatureToPricingTier(freeTier.pricingTierId, feature1.featureId); 493 | await this.warrant.PricingTier.assignPricingTierToUser(freeUser.userId, freeTier.pricingTierId); 494 | 495 | freeUserHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "feature-1", subject: freeUser }, { warrantToken: "latest" }); 496 | assert.strictEqual(freeUserHasFeature, true); 497 | 498 | freeTierFeaturesList = await this.warrant.Feature.listFeaturesForPricingTier(freeTier.pricingTierId, { limit: 100 }, { warrantToken: "latest" }); 499 | assert.strictEqual(freeTierFeaturesList.results.length, 1); 500 | assert.strictEqual(freeTierFeaturesList.results[0].featureId, "feature-1"); 501 | assert.strictEqual(freeTierFeaturesList.results[0].meta, undefined); 502 | 503 | freeUserTiersList = await this.warrant.PricingTier.listPricingTiersForUser(freeUser.userId, { limit: 100 }, { warrantToken: "latest" }); 504 | assert.strictEqual(freeUserTiersList.results.length, 1); 505 | assert.strictEqual(freeUserTiersList.results[0].pricingTierId, "free"); 506 | assert.deepStrictEqual(freeUserTiersList.results[0].meta, { name: "Free Tier" }); 507 | 508 | await this.warrant.Feature.removeFeatureFromPricingTier(freeTier.pricingTierId, feature1.featureId); 509 | await this.warrant.PricingTier.removePricingTierFromUser(freeUser.userId, freeTier.pricingTierId); 510 | 511 | freeUserHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "feature-1", subject: freeUser }, { warrantToken: "latest" }); 512 | assert.strictEqual(freeUserHasFeature, false); 513 | 514 | freeTierFeaturesList = await this.warrant.Feature.listFeaturesForPricingTier(freeTier.pricingTierId, { limit: 100 }, { warrantToken: "latest" }); 515 | assert.strictEqual(freeTierFeaturesList.results.length, 0); 516 | 517 | freeUserTiersList = await this.warrant.PricingTier.listPricingTiersForUser(freeUser.userId, { limit: 100 }, { warrantToken: "latest" }); 518 | assert.strictEqual(freeUserTiersList.results.length, 0); 519 | 520 | // Clean up 521 | let warrantToken = await this.warrant.User.delete(freeUser.userId); 522 | assert(warrantToken); 523 | warrantToken = await this.warrant.User.delete(paidUser.userId); 524 | assert(warrantToken); 525 | warrantToken = await this.warrant.PricingTier.delete(freeTier.pricingTierId); 526 | assert(warrantToken); 527 | warrantToken = await this.warrant.PricingTier.delete(paidTier.pricingTierId); 528 | assert(warrantToken); 529 | warrantToken = await this.warrant.Feature.delete(customFeature.featureId); 530 | assert(warrantToken); 531 | warrantToken = await this.warrant.Feature.delete(feature1.featureId); 532 | assert(warrantToken); 533 | warrantToken = await this.warrant.Feature.delete(feature2.featureId); 534 | assert(warrantToken); 535 | }); 536 | 537 | it('pricing tiers, features, and tenants example', async function () { 538 | // Create tenants 539 | const freeTenant = await this.warrant.Tenant.create(); 540 | const paidTenant = await this.warrant.Tenant.create(); 541 | 542 | // Create pricing tiers 543 | const freeTier = await this.warrant.PricingTier.create({ pricingTierId: "free", meta: { name: "Free Tier" }}); 544 | const paidTier = await this.warrant.PricingTier.create({ pricingTierId: "paid" }); 545 | 546 | // Create features 547 | const customFeature = await this.warrant.Feature.create({ featureId: "custom-feature", meta: { name: "Custom Feature" }}); 548 | const feature1 = await this.warrant.Feature.create({ featureId: "feature-1", meta: { description: "First feature" } }); 549 | const feature2 = await this.warrant.Feature.create({ featureId: "feature-2" }); 550 | 551 | // Assign 'custom-feature' -> paid tenant 552 | let paidTenantHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "custom-feature", subject: paidTenant }, { warrantToken: "latest" }); 553 | assert.strictEqual(paidTenantHasFeature, false); 554 | 555 | let paidTenantFeaturesList = await this.warrant.Feature.listFeaturesForTenant(paidTenant.tenantId, { limit: 100 }, { warrantToken: "latest" }); 556 | assert.strictEqual(paidTenantFeaturesList.results.length, 0); 557 | 558 | await this.warrant.Feature.assignFeatureToTenant(paidTenant.tenantId, customFeature.featureId); 559 | 560 | paidTenantHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "custom-feature", subject: paidTenant }, { warrantToken: "latest" }); 561 | assert.strictEqual(paidTenantHasFeature, true); 562 | 563 | paidTenantFeaturesList = await this.warrant.Feature.listFeaturesForTenant(paidTenant.tenantId, { limit: 100 }, { warrantToken: "latest" }); 564 | assert.strictEqual(paidTenantFeaturesList.results.length, 1); 565 | assert.strictEqual(paidTenantFeaturesList.results[0].featureId, "custom-feature"); 566 | assert.deepStrictEqual(paidTenantFeaturesList.results[0].meta, { name: "Custom Feature" }); 567 | 568 | await this.warrant.Feature.removeFeatureFromTenant(paidTenant.tenantId, customFeature.featureId); 569 | 570 | paidTenantHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "custom-feature", subject: paidTenant }, { warrantToken: "latest" }); 571 | assert.strictEqual(paidTenantHasFeature, false); 572 | 573 | paidTenantFeaturesList = await this.warrant.Feature.listFeaturesForTenant(paidTenant.tenantId, { limit: 100 }, { warrantToken: "latest" }); 574 | assert.strictEqual(paidTenantFeaturesList.results.length, 0); 575 | 576 | // Assign 'feature-1' -> 'free' tier -> free tenant 577 | let freeTenantHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "feature-1", subject: freeTenant }, { warrantToken: "latest" }); 578 | assert.strictEqual(freeTenantHasFeature, false); 579 | 580 | let freeTierFeaturesList = await this.warrant.Feature.listFeaturesForPricingTier(freeTier.pricingTierId, { limit: 100 }, { warrantToken: "latest" }); 581 | assert.strictEqual(freeTierFeaturesList.results.length, 0); 582 | 583 | let freeTenantTiersList = await this.warrant.PricingTier.listPricingTiersForTenant(freeTenant.tenantId, { limit: 100 }, { warrantToken: "latest" }); 584 | assert.strictEqual(freeTenantTiersList.results.length, 0); 585 | 586 | await this.warrant.Feature.assignFeatureToPricingTier(freeTier.pricingTierId, feature1.featureId); 587 | await this.warrant.PricingTier.assignPricingTierToTenant(freeTenant.tenantId, freeTier.pricingTierId); 588 | 589 | freeTenantHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "feature-1", subject: freeTenant }, { warrantToken: "latest" }); 590 | assert.strictEqual(freeTenantHasFeature, true); 591 | 592 | freeTierFeaturesList = await this.warrant.Feature.listFeaturesForPricingTier(freeTier.pricingTierId, { limit: 100 }, { warrantToken: "latest" }); 593 | assert.strictEqual(freeTierFeaturesList.results.length, 1); 594 | assert.strictEqual(freeTierFeaturesList.results[0].featureId, "feature-1"); 595 | assert.deepStrictEqual(freeTierFeaturesList.results[0].meta, { description: "First feature" }); 596 | 597 | freeTenantTiersList = await this.warrant.PricingTier.listPricingTiersForTenant(freeTenant.tenantId, { limit: 100 }, { warrantToken: "latest" }); 598 | assert.strictEqual(freeTenantTiersList.results.length, 1); 599 | assert.strictEqual(freeTenantTiersList.results[0].pricingTierId, "free"); 600 | assert.deepStrictEqual(freeTenantTiersList.results[0].meta, { name: "Free Tier" }); 601 | 602 | await this.warrant.Feature.removeFeatureFromPricingTier(freeTier.pricingTierId, feature1.featureId); 603 | await this.warrant.PricingTier.removePricingTierFromTenant(freeTenant.tenantId, freeTier.pricingTierId); 604 | 605 | freeTenantHasFeature = await this.warrant.Authorization.hasFeature({ featureId: "feature-1", subject: freeTenant }, { warrantToken: "latest" }); 606 | assert.strictEqual(freeTenantHasFeature, false); 607 | 608 | freeTierFeaturesList = await this.warrant.Feature.listFeaturesForPricingTier(freeTier.pricingTierId, { limit: 100 }, { warrantToken: "latest" }); 609 | assert.strictEqual(freeTierFeaturesList.results.length, 0); 610 | 611 | freeTenantTiersList = await this.warrant.PricingTier.listPricingTiersForTenant(freeTenant.tenantId, { limit: 100 }, { warrantToken: "latest" }); 612 | assert.strictEqual(freeTenantTiersList.results.length, 0); 613 | 614 | // Clean up 615 | let warrantToken = await this.warrant.Tenant.delete(freeTenant.tenantId); 616 | assert(warrantToken); 617 | warrantToken = await this.warrant.Tenant.delete(paidTenant.tenantId); 618 | assert(warrantToken); 619 | warrantToken = await this.warrant.PricingTier.delete(freeTier.pricingTierId); 620 | assert(warrantToken); 621 | warrantToken = await this.warrant.PricingTier.delete(paidTier.pricingTierId); 622 | assert(warrantToken); 623 | warrantToken = await this.warrant.Feature.delete(customFeature.featureId); 624 | assert(warrantToken); 625 | warrantToken = await this.warrant.Feature.delete(feature1.featureId); 626 | assert(warrantToken); 627 | warrantToken = await this.warrant.Feature.delete(feature2.featureId); 628 | assert(warrantToken); 629 | }); 630 | 631 | it('sessions', async function () { 632 | const user = await this.warrant.User.create(); 633 | const tenant = await this.warrant.Tenant.create(); 634 | 635 | await this.warrant.User.assignUserToTenant(tenant.tenantId, user.userId, "admin"); 636 | 637 | const userAuthzSession = await this.warrant.Session.createAuthorizationSession({ userId: user.userId }); 638 | assert(userAuthzSession); 639 | 640 | const userSelfServiceDashboardUrl = await this.warrant.Session.createSelfServiceSession({ 641 | userId: user.userId, 642 | tenantId: tenant.tenantId, 643 | selfServiceStrategy: SelfServiceStrategy.FGAC, 644 | }, "http://localhost:8080"); 645 | assert(userSelfServiceDashboardUrl); 646 | 647 | let warrantToken = await this.warrant.User.delete(user.userId); 648 | assert(warrantToken); 649 | warrantToken = await this.warrant.Tenant.delete(tenant.tenantId); 650 | assert(warrantToken); 651 | }); 652 | 653 | it('warrants', async function () { 654 | const user1 = await this.warrant.User.create(); 655 | const user2 = await this.warrant.User.create(); 656 | const newPermission = await this.warrant.Permission.create({ permissionId: "perm1", meta: { name: "Permission 1", description: "Permission 1" }}); 657 | 658 | let userHasPermission = await this.warrant.Authorization.check({ 659 | object: newPermission, 660 | relation: "member", 661 | subject: user1 662 | }, { 663 | warrantToken: "latest" 664 | }); 665 | assert.strictEqual(userHasPermission, false); 666 | 667 | const warrant1 = await this.warrant.Warrant.create({ 668 | object: newPermission, 669 | relation: "member", 670 | subject: user1 671 | }); 672 | assert(warrant1.warrantToken); 673 | 674 | const warrant2 = await this.warrant.Warrant.create({ 675 | object: newPermission, 676 | relation: "member", 677 | subject: user2 678 | }); 679 | assert(warrant2.warrantToken); 680 | 681 | const warrants1 = await this.warrant.Warrant.list({ limit: 1 }, { warrantToken: "latest" }); 682 | assert.strictEqual(warrants1.results.length, 1); 683 | assert.strictEqual(warrants1.results[0].objectType, "permission"); 684 | assert.strictEqual(warrants1.results[0].objectId, "perm1"); 685 | assert.strictEqual(warrants1.results[0].relation, "member"); 686 | assert.strictEqual(warrants1.results[0].subject.objectType, "user"); 687 | assert.strictEqual(warrants1.results[0].subject.objectId, user1.userId); 688 | 689 | const warrants2 = await this.warrant.Warrant.list({ limit: 1, nextCursor: warrants1.nextCursor }, { warrantToken: "latest" }); 690 | assert.strictEqual(warrants2.results.length, 1); 691 | assert.strictEqual(warrants2.results[0].objectType, "permission"); 692 | assert.strictEqual(warrants2.results[0].objectId, "perm1"); 693 | assert.strictEqual(warrants2.results[0].relation, "member"); 694 | assert.strictEqual(warrants2.results[0].subject.objectType, "user"); 695 | assert.strictEqual(warrants2.results[0].subject.objectId, user2.userId); 696 | 697 | const warrants3 = await this.warrant.Warrant.list({ subjectId: user1.userId }, { warrantToken: "latest" }); 698 | assert.strictEqual(warrants3.results.length, 1); 699 | assert.strictEqual(warrants3.results[0].objectType, "permission"); 700 | assert.strictEqual(warrants3.results[0].objectId, "perm1"); 701 | assert.strictEqual(warrants3.results[0].relation, "member"); 702 | assert.strictEqual(warrants3.results[0].subject.objectType, "user"); 703 | assert.strictEqual(warrants3.results[0].subject.objectId, user1.userId); 704 | 705 | userHasPermission = await this.warrant.Authorization.check({ 706 | object: newPermission, 707 | relation: "member", 708 | subject: user1 709 | }, { 710 | warrantToken: "latest" 711 | }); 712 | assert.strictEqual(userHasPermission, true); 713 | 714 | const query = `select permission where user:${user1.userId} is member`; 715 | const response = await this.warrant.Warrant.query(query, { warrantToken: "latest" }); 716 | 717 | assert.strictEqual(response.results.length, 1); 718 | assert.strictEqual(response.results[0].objectType, "permission"); 719 | assert.strictEqual(response.results[0].objectId, "perm1"); 720 | assert.strictEqual(response.results[0].warrant.relation, "member"); 721 | 722 | let warrantToken = await this.warrant.Warrant.delete({ 723 | object: newPermission, 724 | relation: "member", 725 | subject: user1 726 | }); 727 | assert(warrantToken); 728 | 729 | userHasPermission = await this.warrant.Authorization.check({ 730 | object: newPermission, 731 | relation: "member", 732 | subject: user1 733 | }, { 734 | warrantToken: "latest" 735 | }); 736 | assert.strictEqual(userHasPermission, false); 737 | 738 | warrantToken = await this.warrant.User.delete(user1.userId); 739 | assert(warrantToken); 740 | warrantToken = await this.warrant.User.delete(user2.userId); 741 | assert(warrantToken); 742 | warrantToken = await this.warrant.Permission.delete(newPermission.permissionId); 743 | assert(warrantToken); 744 | }); 745 | 746 | it('batch create/delete/check warrants', async function () { 747 | const newUser = await this.warrant.User.create(); 748 | const permission1 = await this.warrant.Permission.create({ permissionId: "perm1", meta: { name: "Permission 1", description: "Permission 1" }}); 749 | const permission2 = await this.warrant.Permission.create({ permissionId: "perm2", meta: { name: "Permission 2", description: "Permission 2" }}); 750 | 751 | let userHasPermissions = await this.warrant.Authorization.batchCheck({ 752 | warrants: [ 753 | { 754 | object: permission1, 755 | relation: "member", 756 | subject: newUser 757 | }, 758 | { 759 | object: permission2, 760 | relation: "member", 761 | subject: newUser 762 | } 763 | ] 764 | }, { 765 | warrantToken: "latest" 766 | }); 767 | assert.strictEqual(userHasPermissions[0], false); 768 | assert.strictEqual(userHasPermissions[1], false); 769 | 770 | const warrants = await this.warrant.Warrant.batchCreate([ 771 | { 772 | object: permission1, 773 | relation: "member", 774 | subject: newUser 775 | }, 776 | { 777 | object: permission2, 778 | relation: "member", 779 | subject: newUser 780 | } 781 | ]); 782 | assert.strictEqual(warrants.length, 2); 783 | for (const warrant of warrants) { 784 | assert(warrant.warrantToken); 785 | } 786 | 787 | userHasPermissions = await this.warrant.Authorization.batchCheck({ 788 | warrants: [ 789 | { 790 | object: permission1, 791 | relation: "member", 792 | subject: newUser 793 | }, 794 | { 795 | object: permission2, 796 | relation: "member", 797 | subject: newUser 798 | } 799 | ] 800 | }, { 801 | warrantToken: "latest" 802 | }); 803 | assert.strictEqual(userHasPermissions[0], true); 804 | assert.strictEqual(userHasPermissions[1], true); 805 | 806 | let warrantToken = await this.warrant.Warrant.batchDelete([ 807 | { 808 | object: permission1, 809 | relation: "member", 810 | subject: newUser 811 | }, 812 | { 813 | object: permission2, 814 | relation: "member", 815 | subject: newUser 816 | } 817 | ]); 818 | assert(warrantToken); 819 | warrantToken = await this.warrant.Object.batchDelete([ 820 | { object: { objectType: "permission", objectId: permission1.permissionId } }, 821 | { object: { objectType: "permission", objectId: permission2.permissionId } }, 822 | { object: { objectType: "user", objectId: newUser.userId } }, 823 | ]); 824 | assert(warrantToken); 825 | 826 | userHasPermissions = await this.warrant.Authorization.batchCheck({ 827 | warrants: [ 828 | { 829 | object: permission1, 830 | relation: "member", 831 | subject: newUser 832 | }, 833 | { 834 | object: permission2, 835 | relation: "member", 836 | subject: newUser 837 | } 838 | ] 839 | }, { 840 | warrantToken: "latest" 841 | }); 842 | assert.strictEqual(userHasPermissions[0], false); 843 | assert.strictEqual(userHasPermissions[1], false); 844 | }); 845 | 846 | it('warrant with policy', async function () { 847 | await this.warrant.Warrant.create({ 848 | object: { 849 | objectType: "permission", 850 | objectId: "test-permission" 851 | }, 852 | relation: "member", 853 | subject: { 854 | objectType: "user", 855 | objectId: "user-1" 856 | }, 857 | policy: `geo == "us"` 858 | }); 859 | 860 | let checkResult = await this.warrant.Authorization.check({ 861 | object: { 862 | objectType: "permission", 863 | objectId: "test-permission" 864 | }, 865 | relation: "member", 866 | subject: { 867 | objectType: "user", 868 | objectId: "user-1" 869 | }, 870 | context: { 871 | "geo": "us", 872 | } 873 | }, { 874 | warrantToken: "latest" 875 | }); 876 | assert.strictEqual(checkResult, true); 877 | 878 | checkResult = await this.warrant.Authorization.check({ 879 | object: { 880 | objectType: "permission", 881 | objectId: "test-permission" 882 | }, 883 | relation: "member", 884 | subject: { 885 | objectType: "user", 886 | objectId: "user-1" 887 | }, 888 | context: { 889 | "geo": "eu", 890 | } 891 | }, { 892 | warrantToken: "latest" 893 | }); 894 | assert.strictEqual(checkResult, false); 895 | 896 | let warrantToken = await this.warrant.Warrant.delete({ 897 | object: { 898 | objectType: "permission", 899 | objectId: "test-permission" 900 | }, 901 | relation: "member", 902 | subject: { 903 | objectType: "user", 904 | objectId: "user-1" 905 | }, 906 | policy: `geo == "us"` 907 | }); 908 | assert(warrantToken); 909 | 910 | // Clean up 911 | warrantToken = await this.warrant.Permission.delete("test-permission"); 912 | assert(warrantToken); 913 | warrantToken = await this.warrant.User.delete("user-1"); 914 | assert(warrantToken); 915 | }); 916 | 917 | it('query', async function() { 918 | const userA = await this.warrant.User.create({ userId: "userA" }); 919 | const userB = await this.warrant.User.create({ userId: "userB" }); 920 | const permission1 = await this.warrant.Permission.create({ 921 | permissionId: "perm1", 922 | meta: { 923 | name: "Permission 1", 924 | description: "This is permission 1.", 925 | }, 926 | }); 927 | const permission2 = await this.warrant.Permission.create({ permissionId: "perm2" }); 928 | const permission3 = await this.warrant.Permission.create({ 929 | permissionId: "perm3", 930 | meta: { 931 | name: "Permission 3", 932 | description: "This is permission 3.", 933 | }, 934 | }); 935 | const role1 = await this.warrant.Role.create({ 936 | roleId: "role1", 937 | meta: { 938 | name: "Role 1", 939 | description: "This is role 1.", 940 | }, 941 | }); 942 | const role2 = await this.warrant.Role.create({ 943 | roleId: "role2", 944 | meta: { 945 | name: "Role 2", 946 | }, 947 | }); 948 | 949 | await this.warrant.Permission.assignPermissionToRole(role1.roleId, permission1.permissionId); 950 | await this.warrant.Permission.assignPermissionToRole(role2.roleId, permission2.permissionId); 951 | await this.warrant.Permission.assignPermissionToRole(role2.roleId, permission3.permissionId); 952 | await this.warrant.Warrant.create({ 953 | object: role2, 954 | relation: "member", 955 | subject: role1, 956 | }); 957 | await this.warrant.Warrant.create({ 958 | object: permission1, 959 | relation: "member", 960 | subject: role2, 961 | policy: "tenantId == 123", 962 | }); 963 | await this.warrant.Role.assignRoleToUser(userA.userId, role1.roleId); 964 | await this.warrant.Role.assignRoleToUser(userB.userId, role2.roleId); 965 | 966 | let resultSet = await this.warrant.Warrant.query("select role where user:userA is member", { limit: 1 }); 967 | assert.strictEqual(1, resultSet.results.length); 968 | assert.strictEqual("role", resultSet.results[0].objectType); 969 | assert.strictEqual("role1", resultSet.results[0].objectId); 970 | assert.strictEqual(false, resultSet.results[0].isImplicit); 971 | assert.strictEqual("role", resultSet.results[0].warrant.objectType); 972 | assert.strictEqual("role1", resultSet.results[0].warrant.objectId); 973 | assert.strictEqual("member", resultSet.results[0].warrant.relation); 974 | assert.strictEqual("user", resultSet.results[0].warrant.subject.objectType); 975 | assert.strictEqual("userA", resultSet.results[0].warrant.subject.objectId); 976 | 977 | resultSet = await this.warrant.Warrant.query("select role where user:userA is member", { limit: 1, nextCursor: resultSet.nextCursor }); 978 | assert.strictEqual(1, resultSet.results.length); 979 | assert.strictEqual("role", resultSet.results[0].objectType); 980 | assert.strictEqual("role2", resultSet.results[0].objectId); 981 | assert.strictEqual(true, resultSet.results[0].isImplicit); 982 | assert.strictEqual("role", resultSet.results[0].warrant.objectType); 983 | assert.strictEqual("role2", resultSet.results[0].warrant.objectId); 984 | assert.strictEqual("member", resultSet.results[0].warrant.relation); 985 | assert.strictEqual("role", resultSet.results[0].warrant.subject.objectType); 986 | assert.strictEqual("role1", resultSet.results[0].warrant.subject.objectId); 987 | 988 | resultSet = await this.warrant.Warrant.query({ 989 | query: "select permission where user:userB is member", 990 | context: { 991 | tenantId: 123, 992 | }, 993 | }); 994 | assert.strictEqual(3, resultSet.results.length); 995 | assert.strictEqual("permission", resultSet.results[0].objectType); 996 | assert.strictEqual("perm1", resultSet.results[0].objectId); 997 | assert.strictEqual("permission", resultSet.results[1].objectType); 998 | assert.strictEqual("perm2", resultSet.results[1].objectId); 999 | assert.strictEqual("permission", resultSet.results[2].objectType); 1000 | assert.strictEqual("perm3", resultSet.results[2].objectId); 1001 | 1002 | let warrantToken = await this.warrant.Role.delete(role1.roleId); 1003 | assert(warrantToken); 1004 | warrantToken = await this.warrant.Role.delete(role2.roleId); 1005 | assert(warrantToken); 1006 | warrantToken = await this.warrant.Permission.delete(permission1.permissionId); 1007 | assert(warrantToken); 1008 | warrantToken = await this.warrant.Permission.delete(permission2.permissionId); 1009 | assert(warrantToken); 1010 | warrantToken = await this.warrant.Permission.delete(permission3.permissionId); 1011 | assert(warrantToken); 1012 | warrantToken = await this.warrant.User.delete(userA.userId); 1013 | assert(warrantToken); 1014 | warrantToken = await this.warrant.User.delete(userB.userId); 1015 | assert(warrantToken); 1016 | }); 1017 | }); 1018 | -------------------------------------------------------------------------------- /test/WarrantClientTest.spec.ts: -------------------------------------------------------------------------------- 1 | import WarrantClient from "../src/WarrantClient"; 2 | import { ApiError } from "../src/types"; 3 | import { assert } from "chai"; 4 | import { MockAgent, setGlobalDispatcher } from "undici"; 5 | 6 | describe('WarrantClientTest', function () { 7 | before(function () { 8 | this.warrant = new WarrantClient({ apiKey: "my_api_key", endpoint: "http://localhost:8000" }); 9 | 10 | const agent = new MockAgent(); 11 | agent.disableNetConnect(); 12 | this.client = agent.get("http://localhost:8000") 13 | setGlobalDispatcher(agent); 14 | }); 15 | 16 | it('should make request after retries', async function () { 17 | this.timeout(10000); 18 | this.client 19 | .intercept({ 20 | path: "/v2/objects/user/some-user", 21 | method: "GET" 22 | }) 23 | .reply(502); 24 | 25 | this.client 26 | .intercept({ 27 | path: "/v2/objects/user/some-user", 28 | method: "GET" 29 | }) 30 | .reply(502); 31 | 32 | this.client 33 | .intercept({ 34 | path: "/v2/objects/user/some-user", 35 | method: "GET" 36 | }) 37 | .reply(200, { "objectType": "user", "objectId": "some-user" }); 38 | 39 | const fetchedUser = await this.warrant.User.get("some-user"); 40 | 41 | assert.strictEqual(fetchedUser.userId, "some-user"); 42 | assert.strictEqual(fetchedUser.meta, undefined); 43 | }); 44 | 45 | it('should stop requests after max retries', async function () { 46 | this.timeout(10000); 47 | this.client 48 | .intercept({ 49 | path: "/v2/objects/user/some-user", 50 | method: "GET" 51 | }) 52 | .reply(502, { 53 | message: "Bad Gateway" 54 | }); 55 | 56 | this.client 57 | .intercept({ 58 | path: "/v2/objects/user/some-user", 59 | method: "GET" 60 | }) 61 | .reply(502, { 62 | message: "Bad Gateway" 63 | }); 64 | 65 | this.client 66 | .intercept({ 67 | path: "/v2/objects/user/some-user", 68 | method: "GET" 69 | }) 70 | .reply(502, { 71 | message: "Bad Gateway" 72 | }); 73 | 74 | try { 75 | await this.warrant.User.get("some-user"); 76 | } catch (e) { 77 | assert.instanceOf(e, ApiError); 78 | } 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "lib": ["es2017"], 6 | "target": "es6", 7 | "declaration": true, 8 | "noImplicitAny": true, 9 | "moduleResolution": "node", 10 | "outDir": "dist", 11 | }, 12 | "include": ["src"], 13 | "exclude": ["node_modules"] 14 | } 15 | --------------------------------------------------------------------------------