From 9972fd4e98882b8fba803c013a58a75777e155cd Mon Sep 17 00:00:00 2001 From: box-sdk-build Date: Mon, 15 Jan 2024 01:12:02 -0800 Subject: [PATCH] generated with codegen at box/box-codegen@7e31d1a and spec at box/box-openapi@e121116 --- docs/authentication.md | 51 ++-- docs/taskAssignments.md | 42 ++- docs/termsOfServices.md | 25 +- src/authSchemas.ts | 109 ------- src/ccgAuth.generated.ts | 140 +++++++++ src/ccgAuth.ts | 95 ------ src/index.ts | 4 +- src/jwtAuth.generated.ts | 319 +++++++++++++++++++++ src/jwtAuth.ts | 250 ---------------- src/oauth.generated.ts | 92 ++---- src/test/auth.generated.test.ts | 154 ++++++++-- src/test/commons.generated.ts | 36 ++- src/test/taskAssignments.generated.test.ts | 126 ++++++++ src/test/termsOfServices.generated.test.ts | 56 ++++ src/utils.ts | 69 +++++ 15 files changed, 998 insertions(+), 570 deletions(-) delete mode 100644 src/authSchemas.ts create mode 100644 src/ccgAuth.generated.ts delete mode 100644 src/ccgAuth.ts create mode 100644 src/jwtAuth.generated.ts delete mode 100644 src/jwtAuth.ts create mode 100644 src/test/taskAssignments.generated.test.ts create mode 100644 src/test/termsOfServices.generated.test.ts diff --git a/docs/authentication.md b/docs/authentication.md index 50e7bd1b..97b35123 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -14,8 +14,8 @@ - [Switching between Service Account and User](#switching-between-service-account-and-user) - [OAuth 2.0 Auth](#oauth-20-auth) - [Authentication with OAuth2](#authentication-with-oauth2) - - [Revoking tokens](#revoking-tokens) - - [Downscoping tokens](#downscoping-tokens) +- [Revoke token](#revoke-token) +- [Downscope token](#downscope-token) - [Token storage](#token-storage) - [In-memory token storage](#in-memory-token-storage) - [Custom storage](#custom-storage) @@ -119,8 +119,8 @@ make calls as that user. See the [API documentation](https://developer.box.com/) for detailed instructions on how to use app auth. Clients for making calls as an App User can be created with the same JSON JWT config file generated through the -[Box Developer Console][dev_console], but it is also required to call `jwtAuth.asUser('USER_ID')`, with -a user ID you want to authenticate. +[Box Developer Console][dev_console]. Calling `jwtAuth.asUser('USER_ID')` method will return a new auth object, +which is authenticated as the user with provided id, leaving the original object unchanged. ```js const { BoxClient } = require('box-typescript-sdk-gen/lib/client.generated.js'); @@ -131,8 +131,8 @@ const { const jwtConfig = JwtConfig.fromConfigFile('/path/to/settings.json'); const jwtAuth = new BoxJwtAuth({ config: jwtConfig }); -jwtAuth.asUser('USER_ID'); -const userClient = new BoxClient({ auth: jwtAuth }); +const userAuth = jwtAuth.asUser('USER_ID'); +const userClient = new BoxClient({ auth: userAuth }); ``` Alternatively, clients for making calls as an App User can be created with the same `JwtConfig` @@ -234,19 +234,22 @@ const client = new BoxClient({ auth: ccgAuth }); ### Switching between Service Account and User -In order to switch between being authenticated as Service Account and a User you can call: +You can easily switch to be authenticated as a Service Account or as a User. +To create a new auth object authenticated as Service Account you can call: ```js -ccgAuth.asEnterprise('YOUR_ENTERPRISE_ID'); +const enterpriseAuth = ccgAuth.asEnterprise('YOUR_ENTERPRISE_ID'); +const enterpriseClient = new BoxClient({ auth: ccgAuth }); ``` -to authenticate as enterprise or +To authenticate as user with provided User ID call: ```js -ccgAuth.asUser('YOUR_USER_ID'); +const userAuth = ccgAuth.asUser('YOUR_USER_ID'); +const userClient = new BoxClient({ auth: ccgAuth }); ``` -to authenticate as User with provided ID. The new token will be automatically fetched with a next API call. +The new token will be automatically fetched with a next API call. [ccg_guide]: https://developer.box.com/guides/authentication/client-credentials/client-credentials-setup/ @@ -295,44 +298,48 @@ You need to provide the auth code to the SDK to obtain an access token, then you await oauth.getTokensAuthorizationCodeGrant('code'); ``` -### Revoking tokens +# Revoke token -Access tokens for a client can be revoked when needed. As this removes the client's way of authenticating this client can no -longer be used after this call. This method is only available for OAuth2 clients. +Access tokens for a client can be revoked when needed. This call invalidates old token. +For CCGAuth and JWTAuth you can still reuse the `auth` object to retrieve a new token. If you make any new call after revoking the token, +a new token will be automatically retrieved. +For OAuth it would be necessary to manually go through the authentication process again. To revoke current client's tokens in the storage use the following code: ```js -await oauth.revokeTokens(); +await auth.revokeTokens(); // client's tokens have been revoked ``` -### Downscoping tokens +# Downscope token You can exchange a client's access token for one with a lower scope, in order to restrict the permissions for a child client or to pass to a less secure location (e.g. a browser-based app). A downscoped token does not include a refresh token. -In that case, to get a new downscoped token, refresh the original refresh token and use that new token to get a downscoped token. +In such a scenario, to obtain a new downscoped token, refresh the original token +and utilize the newly acquired token to obtain the downscoped token. More information about downscoping tokens can be found [here](https://developer.box.com/guides/authentication/tokens/downscope/). If you want to learn more about available scopes please go [here](https://developer.box.com/guides/api-calls/permissions-and-errors/scopes/#scopes-for-downscoping). -For example to exchange the token for a new token with only `item_preview` scope, restricted to a single file, suitable for the [Content Preview UI Element](https://developer.box.com/en/guides/embed/ui-elements/preview/) you can the following code: +For example to get a new token with only `item_preview` scope, restricted to a single file, suitable for the +[Content Preview UI Element](https://developer.box.com/en/guides/embed/ui-elements/preview/) you can use the following code. +You can also initialize `DeveloperTokenAuth` with the retrieved access token and use it to create a new Client. ```js let resource = 'https://api.box.com/2.0/files/123456789'; -let accessToken = await oauth.downscopeToken(['item_preview'], resource); -// accessToken contains the new downscoped access token +let token = await oauth.downscopeToken(['item_preview'], resource); +const auth = new BoxDeveloperTokenAuth({ token: token.accessToken }); +const client = new BoxClient({ auth }); ``` -This method is only available for OAuth2 clients. - # Token storage ## In-memory token storage diff --git a/docs/taskAssignments.md b/docs/taskAssignments.md index 64bfe7c8..fcc554a2 100644 --- a/docs/taskAssignments.md +++ b/docs/taskAssignments.md @@ -15,7 +15,11 @@ This operation is performed by calling function `getTaskAssignments`. See the endpoint docs at [API Reference](https://developer.box.com/reference/get-tasks-id-assignments/). -_Currently we don't have an example for calling `getTaskAssignments` in integration tests_ + + +```ts +await client.taskAssignments.getTaskAssignments(task.id!); +``` ### Arguments @@ -45,7 +49,19 @@ This operation is performed by calling function `createTaskAssignment`. See the endpoint docs at [API Reference](https://developer.box.com/reference/post-task-assignments/). -_Currently we don't have an example for calling `createTaskAssignment` in integration tests_ + + +```ts +await client.taskAssignments.createTaskAssignment({ + task: { + type: 'task' as CreateTaskAssignmentRequestBodyTaskTypeField, + id: task.id!, + } satisfies CreateTaskAssignmentRequestBodyTaskField, + assignTo: { + id: currentUser.id, + } satisfies CreateTaskAssignmentRequestBodyAssignToField, +} satisfies CreateTaskAssignmentRequestBody); +``` ### Arguments @@ -71,7 +87,11 @@ This operation is performed by calling function `getTaskAssignmentById`. See the endpoint docs at [API Reference](https://developer.box.com/reference/get-task-assignments-id/). -_Currently we don't have an example for calling `getTaskAssignmentById` in integration tests_ + + +```ts +await client.taskAssignments.getTaskAssignmentById(taskAssignment.id!); +``` ### Arguments @@ -99,7 +119,15 @@ This operation is performed by calling function `updateTaskAssignmentById`. See the endpoint docs at [API Reference](https://developer.box.com/reference/put-task-assignments-id/). -_Currently we don't have an example for calling `updateTaskAssignmentById` in integration tests_ + + +```ts +await client.taskAssignments.updateTaskAssignmentById(taskAssignment.id!, { + message: 'updated message', + resolutionState: + 'approved' as UpdateTaskAssignmentByIdRequestBodyResolutionStateField, +} satisfies UpdateTaskAssignmentByIdRequestBody); +``` ### Arguments @@ -127,7 +155,11 @@ This operation is performed by calling function `deleteTaskAssignmentById`. See the endpoint docs at [API Reference](https://developer.box.com/reference/delete-task-assignments-id/). -_Currently we don't have an example for calling `deleteTaskAssignmentById` in integration tests_ + + +```ts +await client.taskAssignments.deleteTaskAssignmentById(taskAssignment.id!); +``` ### Arguments diff --git a/docs/termsOfServices.md b/docs/termsOfServices.md index e98b17c7..45d86018 100644 --- a/docs/termsOfServices.md +++ b/docs/termsOfServices.md @@ -15,7 +15,11 @@ This operation is performed by calling function `getTermsOfService`. See the endpoint docs at [API Reference](https://developer.box.com/reference/get-terms-of-services/). -_Currently we don't have an example for calling `getTermsOfService` in integration tests_ + + +```ts +await client.termsOfServices.getTermsOfService(); +``` ### Arguments @@ -43,7 +47,15 @@ This operation is performed by calling function `createTermsOfService`. See the endpoint docs at [API Reference](https://developer.box.com/reference/post-terms-of-services/). -_Currently we don't have an example for calling `createTermsOfService` in integration tests_ + + +```ts +await client.termsOfServices.createTermsOfService({ + status: 'enabled' as CreateTermsOfServiceRequestBodyStatusField, + tosType: 'managed' as CreateTermsOfServiceRequestBodyTosTypeField, + text: 'Test TOS', +} satisfies CreateTermsOfServiceRequestBody); +``` ### Arguments @@ -95,7 +107,14 @@ This operation is performed by calling function `updateTermsOfServiceById`. See the endpoint docs at [API Reference](https://developer.box.com/reference/put-terms-of-services-id/). -_Currently we don't have an example for calling `updateTermsOfServiceById` in integration tests_ + + +```ts +await client.termsOfServices.updateTermsOfServiceById(tos.id, { + status: 'disabled' as UpdateTermsOfServiceByIdRequestBodyStatusField, + text: 'Disabled TOS', +} satisfies UpdateTermsOfServiceByIdRequestBody); +``` ### Arguments diff --git a/src/authSchemas.ts b/src/authSchemas.ts deleted file mode 100644 index 0813c110..00000000 --- a/src/authSchemas.ts +++ /dev/null @@ -1,109 +0,0 @@ -export type TokenRequestGrantType = - | 'authorization_code' - | 'refresh_token' - | 'client_credentials' - | 'urn:ietf:params:oauth:grant-type:jwt-bearer' - | 'urn:ietf:params:oauth:grant-type:token-exchange'; - -export type TokenRequestBoxSubjectType = 'enterprise' | 'user'; - -export interface TokenRequest { - /** - * The type of request being made, either using a client-side obtained - * authorization code, a refresh token, a JWT assertion, client credentials - * grant or another access token for the purpose of downscoping a token. */ - readonly grant_type: TokenRequestGrantType; - /** - * The Client ID of the application requesting an access token. - * - * Used in combination with `authorization_code`, `client_credentials`, or - * `urn:ietf:params:oauth:grant-type:jwt-bearer` as the `grant_type`. */ - readonly client_id?: string; - /** - * The client secret of the application requesting an access token. - * - * Used in combination with `authorization_code`, `client_credentials`, or - * `urn:ietf:params:oauth:grant-type:jwt-bearer` as the `grant_type`. */ - readonly client_secret?: string; - /** - * The client-side authorization code passed to your application by - * Box in the browser redirect after the user has successfully - * granted your application permission to make API calls on their - * behalf. - * - * Used in combination with `authorization_code` as the `grant_type`. */ - readonly code?: string; - /** - * A refresh token used to get a new access token with. - * - * Used in combination with `refresh_token` as the `grant_type`. */ - readonly refresh_token?: string; - /** - * A JWT assertion for which to request a new access token. - * - * Used in combination with `urn:ietf:params:oauth:grant-type:jwt-bearer` - * as the `grant_type`. */ - readonly assertion?: string; - /** - * The token to exchange for a downscoped token. This can be a regular - * access token, a JWT assertion, or an app token. - * - * Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` - * as the `grant_type`. */ - readonly subject_token?: string; - /** - * The type of `subject_token` passed in. - * - * Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` - * as the `grant_type`. */ - readonly subject_token_type?: 'urn:ietf:params:oauth:token-type:access_token'; - /** - * The token used to create an annotator token. - * This is a JWT assertion. - * - * Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` - * as the `grant_type`. */ - readonly actor_token?: string; - /** - * The type of `actor_token` passed in. - * - * Used in combination with `urn:ietf:params:oauth:grant-type:token-exchange` - * as the `grant_type`. */ - readonly actor_token_type?: 'urn:ietf:params:oauth:token-type:id_token'; - /** - * The space-delimited list of scopes that you want apply to the - * new access token. - * - * The `subject_token` will need to have all of these scopes or - * the call will error with **401 Unauthorized**. */ - readonly scope?: string; - /** - * Full URL for the file that the token should be generated for. */ - readonly resource?: string; - /** - * Used in combination with `client_credentials` as the `grant_type`. */ - readonly box_subject_type?: TokenRequestBoxSubjectType; - /** - * Used in combination with `client_credentials` as the `grant_type`. - * Value is determined by `box_subject_type`. If `user` use user ID and if - * `enterprise` use enterprise ID. */ - readonly box_subject_id?: string; - /** - * Full URL of the shared link on the file or folder - * that the token should be generated for. */ - readonly box_shared_link?: string; -} - -export type FileScope = - | 'annotation_edit' - | 'annotation_view_all' - | 'annotation_view_self' - | 'base_explorer' - | 'base_picker' - | 'base_preview' - | 'base_upload' - | 'item_delete' - | 'item_download' - | 'item_preview' - | 'item_rename' - | 'item_share'; diff --git a/src/ccgAuth.generated.ts b/src/ccgAuth.generated.ts new file mode 100644 index 00000000..a1499f2d --- /dev/null +++ b/src/ccgAuth.generated.ts @@ -0,0 +1,140 @@ +import { PostOAuth2Token } from './schemas.generated.js'; +import { PostOAuth2TokenGrantTypeField } from './schemas.generated.js'; +import { PostOAuth2TokenSubjectTokenTypeField } from './schemas.generated.js'; +import { PostOAuth2Revoke } from './schemas.generated.js'; +import { AccessToken } from './schemas.generated.js'; +import { PostOAuth2TokenBoxSubjectTypeField } from './schemas.generated.js'; +import { Authentication } from './auth.js'; +import { NetworkSession } from './network.js'; +import { TokenStorage } from './tokenStorage.generated.js'; +import { InMemoryTokenStorage } from './tokenStorage.generated.js'; +import { AuthorizationManager } from './managers/authorization.generated.js'; +export class CcgConfig { + readonly clientId!: string; + readonly clientSecret!: string; + readonly enterpriseId?: string; + readonly userId?: string; + readonly tokenStorage: TokenStorage = new InMemoryTokenStorage({}); + constructor( + fields: + | Omit + | Partial> + ) { + Object.assign(this, fields); + } +} +export class BoxCcgAuth implements Authentication { + readonly config!: CcgConfig; + readonly tokenStorage: TokenStorage; + readonly subjectId?: string; + readonly subjectType?: PostOAuth2TokenBoxSubjectTypeField; + constructor( + fields: Omit< + BoxCcgAuth, + | 'tokenStorage' + | 'subjectId' + | 'subjectType' + | 'refreshToken' + | 'retrieveToken' + | 'asUser' + | 'asEnterprise' + | 'downscopeToken' + | 'revokeToken' + > + ) { + Object.assign(this, fields); + this.tokenStorage = this.config.tokenStorage; + this.subjectId = !(this.config.userId == void 0) + ? this.config.userId + : this.config.enterpriseId; + this.subjectType = !(this.config.userId == void 0) ? 'user' : 'enterprise'; + } + async refreshToken(networkSession?: NetworkSession): Promise { + const authManager: AuthorizationManager = !(networkSession == void 0) + ? new AuthorizationManager({ networkSession: networkSession }) + : new AuthorizationManager({}); + const token: AccessToken = await authManager.requestAccessToken({ + grantType: 'client_credentials' as PostOAuth2TokenGrantTypeField, + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + boxSubjectId: this.subjectId, + boxSubjectType: this.subjectType, + } satisfies PostOAuth2Token); + await this.tokenStorage.store(token); + return token; + } + async retrieveToken(networkSession?: NetworkSession): Promise { + const oldToken: any = await this.tokenStorage.get(); + if (oldToken == void 0) { + const newToken: AccessToken = await this.refreshToken(networkSession); + return newToken; + } + return oldToken; + } + async asUser( + userId: string, + tokenStorage: TokenStorage = new InMemoryTokenStorage({}) + ): Promise { + const newConfig: CcgConfig = new CcgConfig({ + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + enterpriseId: this.config.enterpriseId, + userId: userId, + tokenStorage: tokenStorage, + }); + return new BoxCcgAuth({ config: newConfig }); + } + async asEnterprise( + enterpriseId: string, + tokenStorage: TokenStorage = new InMemoryTokenStorage({}) + ): Promise { + const newConfig: CcgConfig = new CcgConfig({ + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + enterpriseId: enterpriseId, + userId: void 0, + tokenStorage: tokenStorage, + }); + return new BoxCcgAuth({ config: newConfig }); + } + async downscopeToken( + scopes: readonly string[], + resource?: string, + sharedLink?: string, + networkSession?: NetworkSession + ): Promise { + const token: undefined | AccessToken = await this.tokenStorage.get(); + if (token == void 0) { + throw 'No access token is available. Make an API call to retrieve a token before calling this method.'; + } + const authManager: AuthorizationManager = !(networkSession == void 0) + ? new AuthorizationManager({ networkSession: networkSession }) + : new AuthorizationManager({}); + const downscopedToken: AccessToken = await authManager.requestAccessToken({ + grantType: + 'urn:ietf:params:oauth:grant-type:token-exchange' as PostOAuth2TokenGrantTypeField, + subjectToken: token.accessToken, + subjectTokenType: + 'urn:ietf:params:oauth:token-type:access_token' as PostOAuth2TokenSubjectTokenTypeField, + resource: resource, + scope: scopes.join(' ') as string, + boxSharedLink: sharedLink, + } satisfies PostOAuth2Token); + return downscopedToken; + } + async revokeToken(networkSession?: NetworkSession): Promise { + const oldToken: undefined | AccessToken = await this.tokenStorage.get(); + if (oldToken == void 0) { + return void 0; + } + const authManager: AuthorizationManager = !(networkSession == void 0) + ? new AuthorizationManager({ networkSession: networkSession }) + : new AuthorizationManager({}); + await authManager.revokeAccessToken({ + token: oldToken.accessToken, + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + } satisfies PostOAuth2Revoke); + return await this.tokenStorage.clear(); + } +} diff --git a/src/ccgAuth.ts b/src/ccgAuth.ts deleted file mode 100644 index fa5a9e14..00000000 --- a/src/ccgAuth.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Authentication } from './auth'; -import { - TokenRequestBoxSubjectType, - TokenRequestGrantType, -} from './authSchemas'; -import { AuthorizationManager } from './managers/authorization.generated'; -import { NetworkSession } from './network'; -import { AccessToken } from './schemas.generated.js'; -import { InMemoryTokenStorage, TokenStorage } from './tokenStorage.generated'; - -export class CcgConfig { - clientId: string; - clientSecret: string; - enterpriseId?: string; - userId?: string; - tokenStorage?: TokenStorage; - - constructor(fields: CcgConfig) { - this.clientId = fields.clientId; - this.clientSecret = fields.clientSecret; - this.enterpriseId = fields.enterpriseId; - this.userId = fields.userId; - this.tokenStorage = fields.tokenStorage ?? new InMemoryTokenStorage({}); - } -} - -export class BoxCcgAuth implements Authentication { - config: CcgConfig; - token?: AccessToken; - subjectId: string; - subjectType: TokenRequestBoxSubjectType; - tokenStorage: TokenStorage; - - constructor({ config }: Pick) { - if (!config.enterpriseId && !config.userId) { - throw new Error('Enterprise ID or User ID is needed'); - } - this.config = config; - this.tokenStorage = config.tokenStorage!; - - if (this.config.userId) { - this.subjectId = this.config.userId!; - this.subjectType = 'user'; - } else { - this.subjectId = this.config.enterpriseId!; - this.subjectType = 'enterprise'; - } - } - - /** - * Get the access token for the app user. If the token is not cached or is expired, a new one will be fetched. - * @param networkSession An object to keep network session state - * @returns {Promise} A promise resolving to the access token. - */ - async retrieveToken(networkSession?: NetworkSession): Promise { - const token = await this.tokenStorage.get(); - if (!token) { - return (await this.refreshToken(networkSession))!; - } - return token; - } - - /** - * Get a new access token for the app user. - * @param networkSession An object to keep network session state - * @returns {Promise} A promise resolving to the access token. - */ - async refreshToken(networkSession?: NetworkSession): Promise { - const authManager: AuthorizationManager = !(networkSession == void 0) - ? new AuthorizationManager({ networkSession: networkSession }) - : new AuthorizationManager({}); - const newToken = await authManager.requestAccessToken({ - grantType: 'client_credentials' satisfies TokenRequestGrantType, - clientId: this.config.clientId, - clientSecret: this.config.clientSecret, - boxSubjectId: this.subjectId, - boxSubjectType: this.subjectType, - }); - - await this.tokenStorage.store(newToken); - return newToken; - } - - async asUser(userId: string) { - this.subjectId = userId; - this.subjectType = 'user' as TokenRequestBoxSubjectType; - await this.tokenStorage.clear(); - } - - async asEnterprise(enterpriseId: string) { - this.subjectId = enterpriseId; - this.subjectType = 'enterprise' as TokenRequestBoxSubjectType; - await this.tokenStorage.clear(); - } -} diff --git a/src/index.ts b/src/index.ts index 72b60551..2ab22b3b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ -export { BoxCcgAuth, CcgConfig } from './ccgAuth'; +export { BoxCcgAuth, CcgConfig } from './ccgAuth.generated'; export { BoxClient } from './client.generated'; export { BoxDeveloperTokenAuth } from './developerTokenAuth.generated'; export type { FetchOptions, FetchResponse, MultipartItem } from './fetch'; -export { BoxJwtAuth, JwtConfig } from './jwtAuth'; +export { BoxJwtAuth, JwtConfig } from './jwtAuth.generated'; export { BoxOAuth } from './oauth.generated'; diff --git a/src/jwtAuth.generated.ts b/src/jwtAuth.generated.ts new file mode 100644 index 00000000..a9ff8529 --- /dev/null +++ b/src/jwtAuth.generated.ts @@ -0,0 +1,319 @@ +import { PostOAuth2TokenGrantTypeField } from './schemas.generated.js'; +import { PostOAuth2TokenSubjectTokenTypeField } from './schemas.generated.js'; +import { Authentication } from './auth.js'; +import { NetworkSession } from './network.js'; +import { AccessToken } from './schemas.generated.js'; +import { PostOAuth2Token } from './schemas.generated.js'; +import { PostOAuth2Revoke } from './schemas.generated.js'; +import { TokenStorage } from './tokenStorage.generated.js'; +import { InMemoryTokenStorage } from './tokenStorage.generated.js'; +import { jsonToSerializedData } from './json.js'; +import { SerializedData } from './json.js'; +import { getUuid } from './utils.js'; +import { readTextFromFile } from './utils.js'; +import { isBrowser } from './utils.js'; +import { getEpochTimeInSeconds } from './utils.js'; +import { createJwtAssertion } from './utils.js'; +import { JwtSignOptions } from './utils.js'; +import { JwtKey } from './utils.js'; +import { JwtAlgorithm } from './utils.js'; +import { AuthorizationManager } from './managers/authorization.generated.js'; +import { sdIsEmpty } from './json.js'; +import { sdIsBoolean } from './json.js'; +import { sdIsNumber } from './json.js'; +import { sdIsString } from './json.js'; +import { sdIsList } from './json.js'; +import { sdIsMap } from './json.js'; +const boxJwtAudience: string = 'https://api.box.com/oauth2/token'; +export interface JwtConfigAppSettingsAppAuth { + readonly publicKeyId: string; + readonly privateKey: string; + readonly passphrase: string; +} +export interface JwtConfigAppSettings { + readonly clientId: string; + readonly clientSecret: string; + readonly appAuth: JwtConfigAppSettingsAppAuth; +} +export interface JwtConfigFile { + readonly enterpriseId?: string; + readonly userId?: string; + readonly boxAppSettings: JwtConfigAppSettings; +} +export class JwtConfig { + readonly clientId!: string; + readonly clientSecret!: string; + readonly jwtKeyId!: string; + readonly privateKey!: string; + readonly privateKeyPassphrase!: string; + readonly enterpriseId?: string; + readonly userId?: string; + readonly jwtAlgorithm?: JwtAlgorithm = 'RS256'; + readonly tokenStorage: TokenStorage = new InMemoryTokenStorage({}); + constructor( + fields: + | Omit< + JwtConfig, + | 'jwtAlgorithm' + | 'tokenStorage' + | 'fromConfigJsonString' + | 'fromConfigFile' + > + | Partial> + ) { + Object.assign(this, fields); + } + static fromConfigJsonString( + configJsonString: string, + tokenStorage?: TokenStorage + ): JwtConfig { + const configJson: JwtConfigFile = deserializeJwtConfigFile( + jsonToSerializedData(configJsonString) + ); + const newConfig: any = !(tokenStorage == void 0) + ? new JwtConfig({ + clientId: configJson.boxAppSettings.clientId, + clientSecret: configJson.boxAppSettings.clientSecret, + enterpriseId: configJson.enterpriseId, + userId: configJson.userId, + jwtKeyId: configJson.boxAppSettings.appAuth.publicKeyId, + privateKey: configJson.boxAppSettings.appAuth.privateKey, + privateKeyPassphrase: configJson.boxAppSettings.appAuth.passphrase, + tokenStorage: tokenStorage, + }) + : new JwtConfig({ + clientId: configJson.boxAppSettings.clientId, + clientSecret: configJson.boxAppSettings.clientSecret, + enterpriseId: configJson.enterpriseId, + userId: configJson.userId, + jwtKeyId: configJson.boxAppSettings.appAuth.publicKeyId, + privateKey: configJson.boxAppSettings.appAuth.privateKey, + privateKeyPassphrase: configJson.boxAppSettings.appAuth.passphrase, + }); + return newConfig; + } + static fromConfigFile( + configFilePath: string, + tokenStorage?: TokenStorage + ): JwtConfig { + const configJsonString: string = readTextFromFile(configFilePath); + return JwtConfig.fromConfigJsonString(configJsonString, tokenStorage); + } +} +export class BoxJwtAuth implements Authentication { + readonly config!: JwtConfig; + readonly tokenStorage: TokenStorage; + readonly subjectId?: string; + readonly subjectType?: string; + constructor( + fields: Omit< + BoxJwtAuth, + | 'tokenStorage' + | 'subjectId' + | 'subjectType' + | 'refreshToken' + | 'retrieveToken' + | 'asUser' + | 'asEnterprise' + | 'downscopeToken' + | 'addSpace' + | 'revokeToken' + > + ) { + Object.assign(this, fields); + this.tokenStorage = this.config.tokenStorage; + this.subjectId = !(this.config.enterpriseId == void 0) + ? this.config.enterpriseId + : this.config.userId; + this.subjectType = !(this.config.enterpriseId == void 0) + ? 'enterprise' + : 'user'; + } + async refreshToken(networkSession?: NetworkSession): Promise { + if (isBrowser()) { + throw 'JWT auth is not supported in browser environment.'; + } + const alg: JwtAlgorithm = !(this.config.jwtAlgorithm == void 0) + ? this.config.jwtAlgorithm + : 'RS256'; + const claims: { + readonly [key: string]: any; + } = { + ['exp']: getEpochTimeInSeconds() + 30, + ['box_sub_type']: this.subjectType, + }; + const jwtOptions: JwtSignOptions = { + algorithm: alg, + audience: boxJwtAudience, + subject: this.subjectId, + issuer: this.config.clientId, + jwtid: getUuid(), + keyid: this.config.jwtKeyId, + } satisfies JwtSignOptions; + const jwtKey: JwtKey = { + key: this.config.privateKey, + passphrase: this.config.privateKeyPassphrase, + } satisfies JwtKey; + const assertion: string = createJwtAssertion(claims, jwtKey, jwtOptions); + const authManager: AuthorizationManager = !(networkSession == void 0) + ? new AuthorizationManager({ networkSession: networkSession }) + : new AuthorizationManager({}); + const token: AccessToken = await authManager.requestAccessToken({ + grantType: + 'urn:ietf:params:oauth:grant-type:jwt-bearer' as PostOAuth2TokenGrantTypeField, + assertion: assertion, + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + } satisfies PostOAuth2Token); + await this.tokenStorage.store(token); + return token; + } + async retrieveToken(networkSession?: NetworkSession): Promise { + const oldToken: any = await this.tokenStorage.get(); + if (oldToken == void 0) { + const newToken: AccessToken = await this.refreshToken(networkSession); + return newToken; + } + return oldToken; + } + async asUser( + userId: string, + tokenStorage: TokenStorage = new InMemoryTokenStorage({}) + ): Promise { + const newConfig: JwtConfig = new JwtConfig({ + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + enterpriseId: void 0, + userId: userId, + jwtKeyId: this.config.jwtKeyId, + privateKey: this.config.privateKey, + privateKeyPassphrase: this.config.privateKeyPassphrase, + tokenStorage: tokenStorage, + }); + const newAuth: BoxJwtAuth = new BoxJwtAuth({ config: newConfig }); + return newAuth; + } + async asEnterprise( + userId: string, + tokenStorage: TokenStorage = new InMemoryTokenStorage({}) + ): Promise { + const newConfig: JwtConfig = new JwtConfig({ + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + enterpriseId: userId, + userId: void 0, + jwtKeyId: this.config.jwtKeyId, + privateKey: this.config.privateKey, + privateKeyPassphrase: this.config.privateKeyPassphrase, + tokenStorage: tokenStorage, + }); + const newAuth: BoxJwtAuth = new BoxJwtAuth({ config: newConfig }); + return newAuth; + } + async downscopeToken( + scopes: readonly string[], + resource?: string, + sharedLink?: string, + networkSession?: NetworkSession + ): Promise { + const token: undefined | AccessToken = await this.tokenStorage.get(); + if (token == void 0) { + throw 'No access token is available. Make an API call to retrieve a token before calling this method.'; + } + const authManager: AuthorizationManager = !(networkSession == void 0) + ? new AuthorizationManager({ networkSession: networkSession }) + : new AuthorizationManager({}); + const downscopedToken: AccessToken = await authManager.requestAccessToken({ + grantType: + 'urn:ietf:params:oauth:grant-type:token-exchange' as PostOAuth2TokenGrantTypeField, + subjectToken: token.accessToken, + subjectTokenType: + 'urn:ietf:params:oauth:token-type:access_token' as PostOAuth2TokenSubjectTokenTypeField, + resource: resource, + scope: scopes.join(' ') as string, + boxSharedLink: sharedLink, + } satisfies PostOAuth2Token); + return downscopedToken; + } + addSpace(text: string): string { + return ''.concat() as string; + } + async revokeToken(networkSession?: NetworkSession): Promise { + const oldToken: undefined | AccessToken = await this.tokenStorage.get(); + if (oldToken == void 0) { + return void 0; + } + const authManager: AuthorizationManager = !(networkSession == void 0) + ? new AuthorizationManager({ networkSession: networkSession }) + : new AuthorizationManager({}); + await authManager.revokeAccessToken({ + token: oldToken.accessToken, + clientId: this.config.clientId, + clientSecret: this.config.clientSecret, + } satisfies PostOAuth2Revoke); + return await this.tokenStorage.clear(); + } +} +export function serializeJwtConfigAppSettingsAppAuth( + val: JwtConfigAppSettingsAppAuth +): SerializedData { + return { + ['publicKeyID']: val.publicKeyId, + ['privateKey']: val.privateKey, + ['passphrase']: val.passphrase, + }; +} +export function deserializeJwtConfigAppSettingsAppAuth( + val: any +): JwtConfigAppSettingsAppAuth { + const publicKeyId: string = val.publicKeyID; + const privateKey: string = val.privateKey; + const passphrase: string = val.passphrase; + return { + publicKeyId: publicKeyId, + privateKey: privateKey, + passphrase: passphrase, + } satisfies JwtConfigAppSettingsAppAuth; +} +export function serializeJwtConfigAppSettings( + val: JwtConfigAppSettings +): SerializedData { + return { + ['clientID']: val.clientId, + ['clientSecret']: val.clientSecret, + ['appAuth']: serializeJwtConfigAppSettingsAppAuth(val.appAuth), + }; +} +export function deserializeJwtConfigAppSettings( + val: any +): JwtConfigAppSettings { + const clientId: string = val.clientID; + const clientSecret: string = val.clientSecret; + const appAuth: JwtConfigAppSettingsAppAuth = + deserializeJwtConfigAppSettingsAppAuth(val.appAuth); + return { + clientId: clientId, + clientSecret: clientSecret, + appAuth: appAuth, + } satisfies JwtConfigAppSettings; +} +export function serializeJwtConfigFile(val: JwtConfigFile): SerializedData { + return { + ['enterpriseID']: val.enterpriseId == void 0 ? void 0 : val.enterpriseId, + ['userID']: val.userId == void 0 ? void 0 : val.userId, + ['boxAppSettings']: serializeJwtConfigAppSettings(val.boxAppSettings), + }; +} +export function deserializeJwtConfigFile(val: any): JwtConfigFile { + const enterpriseId: undefined | string = + val.enterpriseID == void 0 ? void 0 : val.enterpriseID; + const userId: undefined | string = val.userID == void 0 ? void 0 : val.userID; + const boxAppSettings: JwtConfigAppSettings = deserializeJwtConfigAppSettings( + val.boxAppSettings + ); + return { + enterpriseId: enterpriseId, + userId: userId, + boxAppSettings: boxAppSettings, + } satisfies JwtConfigFile; +} diff --git a/src/jwtAuth.ts b/src/jwtAuth.ts deleted file mode 100644 index c121e46c..00000000 --- a/src/jwtAuth.ts +++ /dev/null @@ -1,250 +0,0 @@ -import type * as jwt from 'jsonwebtoken'; -import { v4 as uuidv4 } from 'uuid'; -import { Authentication } from './auth'; -import { - TokenRequestBoxSubjectType, - TokenRequestGrantType, -} from './authSchemas.js'; -import { AuthorizationManager } from './managers/authorization.generated'; -import { NetworkSession } from './network'; -import { AccessToken } from './schemas.generated.js'; -import { InMemoryTokenStorage, TokenStorage } from './tokenStorage.generated'; -import { isBrowser } from './utils.js'; - -const BOX_JWT_AUDIENCE = 'https://api.box.com/oauth2/token'; -const BOX_JWT_GRANT_TYPE: TokenRequestGrantType = - 'urn:ietf:params:oauth:grant-type:jwt-bearer'; - -/** - * Determines if a given string could represent an authorization code or token. - * - * @param {string} codeOrToken The code or token to check. - * @returns {boolean} True if codeOrToken is valid, false if not. - * @private - */ -function isValidCodeOrToken(codeOrToken: string) { - return typeof codeOrToken === 'string' && codeOrToken.length > 0; -} - -/** - * Determines if a token grant response is valid - * - * @param {string} grantType the type of token grant - * @param {Object} responseBody the body of the response to check - * @returns {boolean} True if response body has expected fields, false if not. - * @private - */ -function isValidTokenResponse( - grantType: string, - responseBody: Record /* FIXME */ -) { - if (!isValidCodeOrToken(responseBody.access_token)) { - return false; - } - if (typeof responseBody.expires_in !== 'number') { - return false; - } - // Check the refresh_token for certain types of grants - if (grantType === 'authorization_code' || grantType === 'refresh_token') { - if (!isValidCodeOrToken(responseBody.refresh_token)) { - return false; - } - } - return true; -} - -/** - * A class managing the configuration for JWT authentication. - * @typedef {Object} JwtConfig - */ -export class JwtConfig { - clientId!: string; - clientSecret!: string; - jwtKeyId!: string; - privateKey!: string; - privateKeyPassphrase!: string; - enterpriseId?: string; - userId?: string; - jwtAlgorithm?: jwt.Algorithm; - tokenStorage?: TokenStorage; - - constructor( - fields: Omit - ) { - if (!fields.enterpriseId && !fields.userId) { - throw new Error('Enterprise ID or User ID is needed'); - } - Object.assign(this, fields); - this.jwtAlgorithm = fields.jwtAlgorithm ?? 'RS256'; - this.tokenStorage = fields.tokenStorage ?? new InMemoryTokenStorage({}); - } - - /** - * Create a JwtConfig instance from a JSON string. - * @param {string} configJsonString The JSON string to parse. - * @param {TokenStorage} tokenStorage Object responsible for storing token. If no custom implementation provided, - * the token will be stored in memory. - * @returns {JwtConfig} The JwtConfig instance. - */ - static fromConfigJsonString( - configJsonString: string, - tokenStorage?: TokenStorage - ): JwtConfig { - let config; - try { - config = JSON.parse(configJsonString); - } catch (err) { - throw new Error('Unable to parse the JWT configuration.'); - } - if (!config['boxAppSettings']) { - throw new Error( - 'boxAppSettings is missing from the config file. Please check the config file.' - ); - } - if (!config['boxAppSettings']['appAuth']) { - throw new Error( - 'appAuth is missing from the config file. Please check the config file.' - ); - } - - return new JwtConfig({ - clientId: config['boxAppSettings']['clientID'], - clientSecret: config['boxAppSettings']['clientSecret'], - enterpriseId: config['enterpriseID'], - userId: config['userID'], - jwtKeyId: config['boxAppSettings']['appAuth']['publicKeyID'], - privateKey: config['boxAppSettings']['appAuth']['privateKey'], - privateKeyPassphrase: config['boxAppSettings']['appAuth']['passphrase'], - tokenStorage: tokenStorage, - }); - } - - /** - * Create a JwtConfig instance from a JSON file. - * @param {string} configFilePath The path to the JSON file. - * @param {TokenStorage} tokenStorage Object responsible for storing token. If no custom implementation provided, - * the token will be stored in memory. - * @returns {JwtConfig} The JwtConfig instance. - * @throws {Error} If the file cannot be read. If the file is not valid JSON. - * If the file is missing required fields. - */ - static fromConfigFile( - configFilePath: string, - tokenStorage?: TokenStorage - ): JwtConfig { - if (isBrowser()) { - throw new Error('JWT is unavailble in browser environment'); - } - - const { readFileSync } = eval('require')('fs'); - const config = readFileSync(configFilePath, 'utf8'); - return JwtConfig.fromConfigJsonString(config, tokenStorage); - } -} - -/** - * A class that manages the retrieval and storage of access tokens for a given app user. - * @param {JwtConfig} config The JwtConfig instance. - * @typedef {Object} BoxJwtAuth - */ -export class BoxJwtAuth implements Authentication { - config: JwtConfig; - token?: AccessToken; - subjectId: string; - subjectType: string; - tokenStorage: TokenStorage; - - constructor({ config }: Pick) { - this.config = config; - this.tokenStorage = config.tokenStorage!; - - if (this.config.enterpriseId) { - this.subjectId = this.config.enterpriseId!; - this.subjectType = 'enterprise' as TokenRequestBoxSubjectType; - } else { - this.subjectId = this.config.userId!; - this.subjectType = 'user' as TokenRequestBoxSubjectType; - } - } - - /** - * Get the access token for the app user. If the token is not cached or is expired, a new one will be fetched. - * @param networkSession An object to keep network session state - * @returns {Promise} A promise resolving to the access token. - */ - async retrieveToken(networkSession?: NetworkSession): Promise { - const token = await this.tokenStorage.get(); - if (!token) { - return (await this.refreshToken(networkSession))!; - } - return token; - } - - /** - * Get a new access token for the app user. - * @param networkSession An object to keep network session state - * @returns {Promise} A promise resolving to the access token. - */ - async refreshToken(networkSession?: NetworkSession): Promise { - if (isBrowser()) { - throw new Error('JWT is unavailble in browser environment'); - } - - const expInSec = Math.floor(Date.now() / 1000) + 30; - const claims = { - exp: expInSec, - box_sub_type: this.subjectType, - }; - const jwtOptions: jwt.SignOptions = { - algorithm: this.config.jwtAlgorithm, - audience: BOX_JWT_AUDIENCE, - subject: this.subjectId, - issuer: this.config.clientId, - jwtid: uuidv4(), - keyid: this.config.jwtKeyId, - }; - const keyParams = { - key: this.config.privateKey, - passphrase: this.config.privateKeyPassphrase, - }; - - const assertion = eval('require')('jsonwebtoken').sign( - claims, - keyParams, - jwtOptions - ); - - const authManager: AuthorizationManager = !(networkSession == void 0) - ? new AuthorizationManager({ networkSession: networkSession }) - : new AuthorizationManager({}); - const newToken = await authManager.requestAccessToken({ - grantType: BOX_JWT_GRANT_TYPE, - assertion, - clientId: this.config.clientId, - clientSecret: this.config.clientSecret, - }); - - await this.tokenStorage.store(newToken); - return newToken; - } - - /** - * Set authentication as user. The new token will be automatically fetched with a next API call. - * @param {string} userId The ID of the user to authenticate as - */ - async asUser(userId: string) { - this.subjectId = userId; - this.subjectType = 'user' as TokenRequestBoxSubjectType; - await this.tokenStorage.clear(); - } - - /** - * Set authentication as enterprise. The new token will be automatically fetched with a next API call. - * @param {string} enterpriseId The ID of the enterprise to authenticate as - */ - async asEnterprise(enterpriseId: string) { - this.subjectId = enterpriseId; - this.subjectType = 'enterprise' as TokenRequestBoxSubjectType; - await this.tokenStorage.clear(); - } -} diff --git a/src/oauth.generated.ts b/src/oauth.generated.ts index a2edbe2c..c3bf659f 100644 --- a/src/oauth.generated.ts +++ b/src/oauth.generated.ts @@ -1,19 +1,15 @@ +import { PostOAuth2TokenGrantTypeField } from './schemas.generated.js'; +import { PostOAuth2TokenSubjectTokenTypeField } from './schemas.generated.js'; import { Authentication } from './auth.js'; import { NetworkSession } from './network.js'; import { AccessToken } from './schemas.generated.js'; +import { PostOAuth2Token } from './schemas.generated.js'; +import { PostOAuth2Revoke } from './schemas.generated.js'; import { AuthorizationManager } from './managers/authorization.generated.js'; import { TokenStorage } from './tokenStorage.generated.js'; import { InMemoryTokenStorage } from './tokenStorage.generated.js'; import { sdToUrlParams } from './json.js'; -import { toString } from './utils.js'; -import { sdToJson } from './json.js'; -import { SerializedData } from './json.js'; -import { sdIsEmpty } from './json.js'; -import { sdIsBoolean } from './json.js'; -import { sdIsNumber } from './json.js'; -import { sdIsString } from './json.js'; -import { sdIsList } from './json.js'; -import { sdIsMap } from './json.js'; +import { prepareParams } from './utils.js'; const boxOauth2AuthUrl: string = 'https://account.box.com/api/oauth2/authorize'; export class OAuthConfig { readonly clientId!: string; @@ -56,22 +52,18 @@ export class BoxOAuth implements Authentication { options: GetAuthorizeUrlOptions = {} satisfies GetAuthorizeUrlOptions ): string { const params: { - readonly [key: string]: any; - } = { - ...{ - ['client_id']: !(options.clientId == void 0) - ? options.clientId - : this.config.clientId, - ['response_type']: !(options.responseType == void 0) - ? options.responseType - : 'code', - }, - ...(!(options.redirectUri == void 0) - ? { ['redirect_uri']: options.redirectUri } - : void 0), - ...(!(options.state == void 0) ? { ['state']: options.state } : void 0), - ...(!(options.scope == void 0) ? { ['scope']: options.scope } : void 0), - }; + readonly [key: string]: string; + } = prepareParams({ + ['client_id']: !(options.clientId == void 0) + ? options.clientId + : this.config.clientId, + ['response_type']: !(options.responseType == void 0) + ? options.responseType + : 'code', + ['redirect_uri']: options.redirectUri, + ['state']: options.state, + ['scope']: options.scope, + }); return ''.concat(boxOauth2AuthUrl, '?', sdToUrlParams(params)) as string; } async getTokensAuthorizationCodeGrant( @@ -82,11 +74,11 @@ export class BoxOAuth implements Authentication { ? new AuthorizationManager({ networkSession: networkSession }) : new AuthorizationManager({}); const token: AccessToken = await authManager.requestAccessToken({ - grantType: 'authorization_code', + grantType: 'authorization_code' as PostOAuth2TokenGrantTypeField, code: authorizationCode, clientId: this.config.clientId, clientSecret: this.config.clientSecret, - }); + } satisfies PostOAuth2Token); await this.tokenStorage.store(token); return token; } @@ -111,11 +103,11 @@ export class BoxOAuth implements Authentication { ? new AuthorizationManager({ networkSession: networkSession }) : new AuthorizationManager({}); const token: AccessToken = await authManager.requestAccessToken({ - grantType: 'refresh_token', + grantType: 'refresh_token' as PostOAuth2TokenGrantTypeField, clientId: this.config.clientId, clientSecret: this.config.clientSecret, refreshToken: tokenUsedForRefresh, - }); + } satisfies PostOAuth2Token); await this.tokenStorage.store(token); return token; } @@ -131,7 +123,7 @@ export class BoxOAuth implements Authentication { clientId: this.config.clientId, clientSecret: this.config.clientSecret, token: token.accessToken, - }); + } satisfies PostOAuth2Revoke); await this.tokenStorage.clear(); return void 0; } @@ -149,43 +141,15 @@ export class BoxOAuth implements Authentication { ? new AuthorizationManager({ networkSession: networkSession }) : new AuthorizationManager({}); const downscopedToken: AccessToken = await authManager.requestAccessToken({ - grantType: 'urn:ietf:params:oauth:grant-type:token-exchange', + grantType: + 'urn:ietf:params:oauth:grant-type:token-exchange' as PostOAuth2TokenGrantTypeField, subjectToken: token.accessToken, - subjectTokenType: 'urn:ietf:params:oauth:token-type:access_token', - scope: toString(scopes) as string, + subjectTokenType: + 'urn:ietf:params:oauth:token-type:access_token' as PostOAuth2TokenSubjectTokenTypeField, + scope: scopes.join(' ') as string, resource: resource, boxSharedLink: sharedLink, - }); + } satisfies PostOAuth2Token); return downscopedToken; } } -export function serializeGetAuthorizeUrlOptions( - val: GetAuthorizeUrlOptions -): SerializedData { - return { - ['clientId']: val.clientId == void 0 ? void 0 : val.clientId, - ['redirectUri']: val.redirectUri == void 0 ? void 0 : val.redirectUri, - ['responseType']: val.responseType == void 0 ? void 0 : val.responseType, - ['state']: val.state == void 0 ? void 0 : val.state, - ['scope']: val.scope == void 0 ? void 0 : val.scope, - }; -} -export function deserializeGetAuthorizeUrlOptions( - val: any -): GetAuthorizeUrlOptions { - const clientId: undefined | string = - val.clientId == void 0 ? void 0 : val.clientId; - const redirectUri: undefined | string = - val.redirectUri == void 0 ? void 0 : val.redirectUri; - const responseType: undefined | string = - val.responseType == void 0 ? void 0 : val.responseType; - const state: undefined | string = val.state == void 0 ? void 0 : val.state; - const scope: undefined | string = val.scope == void 0 ? void 0 : val.scope; - return { - clientId: clientId, - redirectUri: redirectUri, - responseType: responseType, - state: state, - scope: scope, - } satisfies GetAuthorizeUrlOptions; -} diff --git a/src/test/auth.generated.test.ts b/src/test/auth.generated.test.ts index 42564f2e..4f03e1ef 100644 --- a/src/test/auth.generated.test.ts +++ b/src/test/auth.generated.test.ts @@ -8,16 +8,28 @@ import { serializeFileFull } from '../schemas.generated.js'; import { deserializeFileFull } from '../schemas.generated.js'; import { serializeUpdateFileByIdRequestBody } from '../managers/files.generated.js'; import { deserializeUpdateFileByIdRequestBody } from '../managers/files.generated.js'; +import { serializeFolderFull } from '../schemas.generated.js'; +import { deserializeFolderFull } from '../schemas.generated.js'; +import { serializeCreateFolderRequestBody } from '../managers/folders.generated.js'; +import { deserializeCreateFolderRequestBody } from '../managers/folders.generated.js'; +import { serializeCreateFolderRequestBodyParentField } from '../managers/folders.generated.js'; +import { deserializeCreateFolderRequestBodyParentField } from '../managers/folders.generated.js'; +import { serializeUpdateFolderByIdRequestBody } from '../managers/folders.generated.js'; +import { deserializeUpdateFolderByIdRequestBody } from '../managers/folders.generated.js'; import { serializeUserFull } from '../schemas.generated.js'; import { deserializeUserFull } from '../schemas.generated.js'; import { GetUserMeQueryParams } from '../managers/users.generated.js'; -import { AccessToken } from '../schemas.generated.js'; import { Files } from '../schemas.generated.js'; import { UploadFileRequestBody } from '../managers/uploads.generated.js'; import { UploadFileRequestBodyAttributesField } from '../managers/uploads.generated.js'; import { UploadFileRequestBodyAttributesParentField } from '../managers/uploads.generated.js'; import { FileFull } from '../schemas.generated.js'; +import { AccessToken } from '../schemas.generated.js'; import { UpdateFileByIdRequestBody } from '../managers/files.generated.js'; +import { FolderFull } from '../schemas.generated.js'; +import { CreateFolderRequestBody } from '../managers/folders.generated.js'; +import { CreateFolderRequestBodyParentField } from '../managers/folders.generated.js'; +import { UpdateFolderByIdRequestBody } from '../managers/folders.generated.js'; import { decodeBase64 } from '../utils.js'; import { getEnvVar } from '../utils.js'; import { getUuid } from '../utils.js'; @@ -27,14 +39,14 @@ import { generateByteBuffer } from '../utils.js'; import { generateByteStream } from '../utils.js'; import { decodeBase64ByteStream } from '../utils.js'; import { BoxClient } from '../client.generated.js'; -import { BoxCcgAuth } from '../ccgAuth.js'; -import { CcgConfig } from '../ccgAuth.js'; +import { BoxCcgAuth } from '../ccgAuth.generated.js'; +import { CcgConfig } from '../ccgAuth.generated.js'; import { BoxDeveloperTokenAuth } from '../developerTokenAuth.generated.js'; import { BoxOAuth } from '../oauth.generated.js'; import { OAuthConfig } from '../oauth.generated.js'; import { UserFull } from '../schemas.generated.js'; -import { BoxJwtAuth } from '../jwtAuth.js'; -import { JwtConfig } from '../jwtAuth.js'; +import { BoxJwtAuth } from '../jwtAuth.generated.js'; +import { JwtConfig } from '../jwtAuth.generated.js'; import { SerializedData } from '../json.js'; import { sdIsEmpty } from '../json.js'; import { sdIsBoolean } from '../json.js'; @@ -63,14 +75,15 @@ test('test_jwt_auth', async function test_jwt_auth(): Promise { decodeBase64(getEnvVar('JWT_CONFIG_BASE_64')) ); const auth: BoxJwtAuth = new BoxJwtAuth({ config: jwtConfig }); - const client: BoxClient = new BoxClient({ auth: auth }); - await auth.asUser(userId); - const currentUser: UserFull = await client.users.getUserMe(); + const userAuth: BoxJwtAuth = await auth.asUser(userId); + const userClient: BoxClient = new BoxClient({ auth: userAuth }); + const currentUser: UserFull = await userClient.users.getUserMe(); if (!(currentUser.id == userId)) { throw 'Assertion failed'; } - await auth.asEnterprise(enterpriseId); - const newUser: UserFull = await client.users.getUserMe({ + const enterpriseAuth: BoxJwtAuth = await auth.asEnterprise(enterpriseId); + const enterpriseClient: BoxClient = new BoxClient({ auth: enterpriseAuth }); + const newUser: UserFull = await enterpriseClient.users.getUserMe({ fields: ['enterprise' as ''], } satisfies GetUserMeQueryParams); if (!!(newUser.enterprise == void 0)) { @@ -83,6 +96,60 @@ test('test_jwt_auth', async function test_jwt_auth(): Promise { throw 'Assertion failed'; } }); +test('test_jwt_auth_downscope', async function test_jwt_auth_downscope(): Promise { + const jwtConfig: JwtConfig = JwtConfig.fromConfigJsonString( + decodeBase64(getEnvVar('JWT_CONFIG_BASE_64')) + ); + const auth: BoxJwtAuth = new BoxJwtAuth({ config: jwtConfig }); + const parentClient: BoxClient = new BoxClient({ auth: auth }); + const uploadedFiles: Files = await parentClient.uploads.uploadFile({ + attributes: { + name: getUuid(), + parent: { id: '0' } satisfies UploadFileRequestBodyAttributesParentField, + } satisfies UploadFileRequestBodyAttributesField, + file: generateByteStream(1024 * 1024), + } satisfies UploadFileRequestBody); + const file: FileFull = uploadedFiles.entries![0]; + const resourcePath: string = ''.concat( + 'https://api.box.com/2.0/files/', + file.id + ) as string; + const downscopedToken: AccessToken = await auth.downscopeToken( + ['item_rename', 'item_preview'], + resourcePath + ); + if (!!(downscopedToken.accessToken == void 0)) { + throw 'Assertion failed'; + } + const downscopedClient: BoxClient = new BoxClient({ + auth: new BoxDeveloperTokenAuth({ token: downscopedToken.accessToken! }), + }); + await downscopedClient.files.updateFileById(file.id, { + name: getUuid(), + } satisfies UpdateFileByIdRequestBody); + await expect(async () => { + await downscopedClient.files.deleteFileById(file.id); + }).rejects.toThrow(); + await parentClient.files.deleteFileById(file.id); +}); +test('test_jwt_auth_revoke', async function test_jwt_auth_revoke(): Promise { + const jwtConfig: JwtConfig = JwtConfig.fromConfigJsonString( + decodeBase64(getEnvVar('JWT_CONFIG_BASE_64')) + ); + const auth: BoxJwtAuth = new BoxJwtAuth({ config: jwtConfig }); + await auth.retrieveToken(); + const tokenFromStorageBeforeRevoke: undefined | AccessToken = + await auth.tokenStorage.get(); + await auth.revokeToken(); + const tokenFromStorageAfterRevoke: undefined | AccessToken = + await auth.tokenStorage.get(); + if (!!(tokenFromStorageBeforeRevoke == void 0)) { + throw 'Assertion failed'; + } + if (!(tokenFromStorageAfterRevoke == void 0)) { + throw 'Assertion failed'; + } +}); test('test_oauth_auth_authorizeUrl', function test_oauth_auth_authorizeUrl(): any { const config: OAuthConfig = new OAuthConfig({ clientId: 'OAUTH_CLIENT_ID', @@ -111,14 +178,15 @@ test('test_ccg_auth', async function test_ccg_auth(): Promise { userId: userId, }); const auth: BoxCcgAuth = new BoxCcgAuth({ config: ccgConfig }); - const client: BoxClient = new BoxClient({ auth: auth }); - await auth.asUser(userId); - const currentUser: UserFull = await client.users.getUserMe(); + const userAuth: BoxCcgAuth = await auth.asUser(userId); + const userClient: BoxClient = new BoxClient({ auth: userAuth }); + const currentUser: UserFull = await userClient.users.getUserMe(); if (!(currentUser.id == userId)) { throw 'Assertion failed'; } - await auth.asEnterprise(enterpriseId); - const newUser: UserFull = await client.users.getUserMe({ + const enterpriseAuth: BoxCcgAuth = await auth.asEnterprise(enterpriseId); + const enterpriseClient: BoxClient = new BoxClient({ auth: enterpriseAuth }); + const newUser: UserFull = await enterpriseClient.users.getUserMe({ fields: ['enterprise' as ''], } satisfies GetUserMeQueryParams); if (!!(newUser.enterprise == void 0)) { @@ -131,6 +199,60 @@ test('test_ccg_auth', async function test_ccg_auth(): Promise { throw 'Assertion failed'; } }); +test('test_ccg_auth_downscope', async function test_ccg_auth_downscope(): Promise { + const ccgConfig: CcgConfig = new CcgConfig({ + clientId: getEnvVar('CLIENT_ID'), + clientSecret: getEnvVar('CLIENT_SECRET'), + userId: getEnvVar('USER_ID'), + }); + const auth: BoxCcgAuth = new BoxCcgAuth({ config: ccgConfig }); + const parentClient: BoxClient = new BoxClient({ auth: auth }); + const folder: FolderFull = await parentClient.folders.createFolder({ + name: getUuid(), + parent: { id: '0' } satisfies CreateFolderRequestBodyParentField, + } satisfies CreateFolderRequestBody); + const resourcePath: string = ''.concat( + 'https://api.box.com/2.0/folders/', + folder.id + ) as string; + const downscopedToken: AccessToken = await auth.downscopeToken( + ['item_rename', 'item_preview'], + resourcePath + ); + if (!!(downscopedToken.accessToken == void 0)) { + throw 'Assertion failed'; + } + const downscopedClient: BoxClient = new BoxClient({ + auth: new BoxDeveloperTokenAuth({ token: downscopedToken.accessToken! }), + }); + await downscopedClient.folders.updateFolderById(folder.id, { + name: getUuid(), + } satisfies UpdateFolderByIdRequestBody); + await expect(async () => { + await downscopedClient.folders.deleteFolderById(folder.id); + }).rejects.toThrow(); + await parentClient.folders.deleteFolderById(folder.id); +}); +test('test_ccg_auth_revoke', async function test_ccg_auth_revoke(): Promise { + const ccgConfig: CcgConfig = new CcgConfig({ + clientId: getEnvVar('CLIENT_ID'), + clientSecret: getEnvVar('CLIENT_SECRET'), + userId: getEnvVar('USER_ID'), + }); + const auth: BoxCcgAuth = new BoxCcgAuth({ config: ccgConfig }); + await auth.retrieveToken(); + const tokenFromStorageBeforeRevoke: undefined | AccessToken = + await auth.tokenStorage.get(); + await auth.revokeToken(); + const tokenFromStorageAfterRevoke: undefined | AccessToken = + await auth.tokenStorage.get(); + if (!!(tokenFromStorageBeforeRevoke == void 0)) { + throw 'Assertion failed'; + } + if (!(tokenFromStorageAfterRevoke == void 0)) { + throw 'Assertion failed'; + } +}); test('test_developer_token_auth', async function test_developer_token_auth(): Promise { const userId: string = getEnvVar('USER_ID'); const token: AccessToken = await getAccessToken(); @@ -185,7 +307,7 @@ test('test_oauth_auth_downscope', async function test_oauth_auth_downscope(): Pr file.id ) as string; const downscopedToken: AccessToken = await auth.downscopeToken( - ['item_rename'], + ['item_rename', 'item_preview'], resourcePath ); if (!!(downscopedToken.accessToken == void 0)) { diff --git a/src/test/commons.generated.ts b/src/test/commons.generated.ts index 549421d3..734854e7 100644 --- a/src/test/commons.generated.ts +++ b/src/test/commons.generated.ts @@ -12,6 +12,16 @@ import { serializeUploadFileRequestBodyAttributesField } from '../managers/uploa import { deserializeUploadFileRequestBodyAttributesField } from '../managers/uploads.generated.js'; import { serializeUploadFileRequestBodyAttributesParentField } from '../managers/uploads.generated.js'; import { deserializeUploadFileRequestBodyAttributesParentField } from '../managers/uploads.generated.js'; +import { serializeTermsOfService } from '../schemas.generated.js'; +import { deserializeTermsOfService } from '../schemas.generated.js'; +import { serializeTermsOfServices } from '../schemas.generated.js'; +import { deserializeTermsOfServices } from '../schemas.generated.js'; +import { serializeCreateTermsOfServiceRequestBody } from '../managers/termsOfServices.generated.js'; +import { deserializeCreateTermsOfServiceRequestBody } from '../managers/termsOfServices.generated.js'; +import { serializeCreateTermsOfServiceRequestBodyStatusField } from '../managers/termsOfServices.generated.js'; +import { deserializeCreateTermsOfServiceRequestBodyStatusField } from '../managers/termsOfServices.generated.js'; +import { serializeCreateTermsOfServiceRequestBodyTosTypeField } from '../managers/termsOfServices.generated.js'; +import { deserializeCreateTermsOfServiceRequestBodyTosTypeField } from '../managers/termsOfServices.generated.js'; import { serializeClassificationTemplateFieldsOptionsField } from '../schemas.generated.js'; import { deserializeClassificationTemplateFieldsOptionsField } from '../schemas.generated.js'; import { serializeAddClassificationRequestBody } from '../managers/classifications.generated.js'; @@ -63,6 +73,11 @@ import { Files } from '../schemas.generated.js'; import { UploadFileRequestBody } from '../managers/uploads.generated.js'; import { UploadFileRequestBodyAttributesField } from '../managers/uploads.generated.js'; import { UploadFileRequestBodyAttributesParentField } from '../managers/uploads.generated.js'; +import { TermsOfService } from '../schemas.generated.js'; +import { TermsOfServices } from '../schemas.generated.js'; +import { CreateTermsOfServiceRequestBody } from '../managers/termsOfServices.generated.js'; +import { CreateTermsOfServiceRequestBodyStatusField } from '../managers/termsOfServices.generated.js'; +import { CreateTermsOfServiceRequestBodyTosTypeField } from '../managers/termsOfServices.generated.js'; import { ClassificationTemplateFieldsOptionsField } from '../schemas.generated.js'; import { AddClassificationRequestBody } from '../managers/classifications.generated.js'; import { AddClassificationRequestBodyOpField } from '../managers/classifications.generated.js'; @@ -89,8 +104,8 @@ import { getUuid } from '../utils.js'; import { generateByteStream } from '../utils.js'; import { BoxClient } from '../client.generated.js'; import { ClassificationTemplate } from '../schemas.generated.js'; -import { BoxJwtAuth } from '../jwtAuth.js'; -import { JwtConfig } from '../jwtAuth.js'; +import { BoxJwtAuth } from '../jwtAuth.generated.js'; +import { JwtConfig } from '../jwtAuth.generated.js'; import { SerializedData } from '../json.js'; import { sdIsEmpty } from '../json.js'; import { sdIsBoolean } from '../json.js'; @@ -109,8 +124,8 @@ export async function getDefaultClientAsUser( userId: string ): Promise { const auth: BoxJwtAuth = getJwtAuth(); - await auth.asUser(userId); - return new BoxClient({ auth: auth }); + const authUser: BoxJwtAuth = await auth.asUser(userId); + return new BoxClient({ auth: authUser }); } export function getDefaultClient(): BoxClient { const client: BoxClient = new BoxClient({ auth: getJwtAuth() }); @@ -137,6 +152,19 @@ export async function uploadNewFile(): Promise { } satisfies UploadFileRequestBody); return uploadedFiles.entries![0]; } +export async function getOrCreateTermsOfServices(): Promise { + const client: BoxClient = getDefaultClient(); + const tos: TermsOfServices = await client.termsOfServices.getTermsOfService(); + const numberOfTos: number = tos.entries!.length; + if (numberOfTos == 0) { + return await client.termsOfServices.createTermsOfService({ + status: 'enabled' as CreateTermsOfServiceRequestBodyStatusField, + tosType: 'managed' as CreateTermsOfServiceRequestBodyTosTypeField, + text: 'Test TOS', + } satisfies CreateTermsOfServiceRequestBody); + } + return tos.entries![0]; +} export async function getOrCreateClassification( classificationTemplate: ClassificationTemplate ): Promise { diff --git a/src/test/taskAssignments.generated.test.ts b/src/test/taskAssignments.generated.test.ts new file mode 100644 index 00000000..cf7b7a99 --- /dev/null +++ b/src/test/taskAssignments.generated.test.ts @@ -0,0 +1,126 @@ +import { serializeFileFull } from '../schemas.generated.js'; +import { deserializeFileFull } from '../schemas.generated.js'; +import { serializeTask } from '../schemas.generated.js'; +import { deserializeTask } from '../schemas.generated.js'; +import { serializeCreateTaskRequestBody } from '../managers/tasks.generated.js'; +import { deserializeCreateTaskRequestBody } from '../managers/tasks.generated.js'; +import { serializeCreateTaskRequestBodyItemField } from '../managers/tasks.generated.js'; +import { deserializeCreateTaskRequestBodyItemField } from '../managers/tasks.generated.js'; +import { serializeCreateTaskRequestBodyItemTypeField } from '../managers/tasks.generated.js'; +import { deserializeCreateTaskRequestBodyItemTypeField } from '../managers/tasks.generated.js'; +import { serializeCreateTaskRequestBodyActionField } from '../managers/tasks.generated.js'; +import { deserializeCreateTaskRequestBodyActionField } from '../managers/tasks.generated.js'; +import { serializeCreateTaskRequestBodyCompletionRuleField } from '../managers/tasks.generated.js'; +import { deserializeCreateTaskRequestBodyCompletionRuleField } from '../managers/tasks.generated.js'; +import { serializeUserFull } from '../schemas.generated.js'; +import { deserializeUserFull } from '../schemas.generated.js'; +import { serializeTaskAssignment } from '../schemas.generated.js'; +import { deserializeTaskAssignment } from '../schemas.generated.js'; +import { serializeCreateTaskAssignmentRequestBody } from '../managers/taskAssignments.generated.js'; +import { deserializeCreateTaskAssignmentRequestBody } from '../managers/taskAssignments.generated.js'; +import { serializeCreateTaskAssignmentRequestBodyTaskField } from '../managers/taskAssignments.generated.js'; +import { deserializeCreateTaskAssignmentRequestBodyTaskField } from '../managers/taskAssignments.generated.js'; +import { serializeCreateTaskAssignmentRequestBodyTaskTypeField } from '../managers/taskAssignments.generated.js'; +import { deserializeCreateTaskAssignmentRequestBodyTaskTypeField } from '../managers/taskAssignments.generated.js'; +import { serializeCreateTaskAssignmentRequestBodyAssignToField } from '../managers/taskAssignments.generated.js'; +import { deserializeCreateTaskAssignmentRequestBodyAssignToField } from '../managers/taskAssignments.generated.js'; +import { serializeTaskAssignments } from '../schemas.generated.js'; +import { deserializeTaskAssignments } from '../schemas.generated.js'; +import { serializeUpdateTaskAssignmentByIdRequestBody } from '../managers/taskAssignments.generated.js'; +import { deserializeUpdateTaskAssignmentByIdRequestBody } from '../managers/taskAssignments.generated.js'; +import { serializeUpdateTaskAssignmentByIdRequestBodyResolutionStateField } from '../managers/taskAssignments.generated.js'; +import { deserializeUpdateTaskAssignmentByIdRequestBodyResolutionStateField } from '../managers/taskAssignments.generated.js'; +import { BoxClient } from '../client.generated.js'; +import { FileFull } from '../schemas.generated.js'; +import { Task } from '../schemas.generated.js'; +import { CreateTaskRequestBody } from '../managers/tasks.generated.js'; +import { CreateTaskRequestBodyItemField } from '../managers/tasks.generated.js'; +import { CreateTaskRequestBodyItemTypeField } from '../managers/tasks.generated.js'; +import { CreateTaskRequestBodyActionField } from '../managers/tasks.generated.js'; +import { CreateTaskRequestBodyCompletionRuleField } from '../managers/tasks.generated.js'; +import { UserFull } from '../schemas.generated.js'; +import { TaskAssignment } from '../schemas.generated.js'; +import { CreateTaskAssignmentRequestBody } from '../managers/taskAssignments.generated.js'; +import { CreateTaskAssignmentRequestBodyTaskField } from '../managers/taskAssignments.generated.js'; +import { CreateTaskAssignmentRequestBodyTaskTypeField } from '../managers/taskAssignments.generated.js'; +import { CreateTaskAssignmentRequestBodyAssignToField } from '../managers/taskAssignments.generated.js'; +import { TaskAssignments } from '../schemas.generated.js'; +import { UpdateTaskAssignmentByIdRequestBody } from '../managers/taskAssignments.generated.js'; +import { UpdateTaskAssignmentByIdRequestBodyResolutionStateField } from '../managers/taskAssignments.generated.js'; +import { uploadNewFile } from './commons.generated.js'; +import { getDefaultClient } from './commons.generated.js'; +import { toString } from '../utils.js'; +import { sdToJson } from '../json.js'; +import { SerializedData } from '../json.js'; +import { sdIsEmpty } from '../json.js'; +import { sdIsBoolean } from '../json.js'; +import { sdIsNumber } from '../json.js'; +import { sdIsString } from '../json.js'; +import { sdIsList } from '../json.js'; +import { sdIsMap } from '../json.js'; +const client: BoxClient = getDefaultClient(); +test('testCreateUpdateGetDeleteTaskAssignment', async function testCreateUpdateGetDeleteTaskAssignment(): Promise { + const file: FileFull = await uploadNewFile(); + const task: Task = await client.tasks.createTask({ + item: { + type: 'file' as CreateTaskRequestBodyItemTypeField, + id: file.id, + } satisfies CreateTaskRequestBodyItemField, + message: 'test message', + dueAt: '2035-01-01T00:00:00Z', + action: 'review' as CreateTaskRequestBodyActionField, + completionRule: 'all_assignees' as CreateTaskRequestBodyCompletionRuleField, + } satisfies CreateTaskRequestBody); + if (!(task.message == 'test message')) { + throw 'Assertion failed'; + } + if (!(task.item!.id == file.id)) { + throw 'Assertion failed'; + } + const currentUser: UserFull = await client.users.getUserMe(); + const taskAssignment: TaskAssignment = + await client.taskAssignments.createTaskAssignment({ + task: { + type: 'task' as CreateTaskAssignmentRequestBodyTaskTypeField, + id: task.id!, + } satisfies CreateTaskAssignmentRequestBodyTaskField, + assignTo: { + id: currentUser.id, + } satisfies CreateTaskAssignmentRequestBodyAssignToField, + } satisfies CreateTaskAssignmentRequestBody); + if (!(taskAssignment.item!.id == file.id)) { + throw 'Assertion failed'; + } + if (!(taskAssignment.assignedTo!.id == currentUser.id)) { + throw 'Assertion failed'; + } + const taskAssignmentById: TaskAssignment = + await client.taskAssignments.getTaskAssignmentById(taskAssignment.id!); + if (!(taskAssignmentById.id == taskAssignment.id)) { + throw 'Assertion failed'; + } + const taskAssignmentsOnTask: TaskAssignments = + await client.taskAssignments.getTaskAssignments(task.id!); + if (!(taskAssignmentsOnTask.totalCount! == 1)) { + throw 'Assertion failed'; + } + const updatedTaskAssignment: TaskAssignment = + await client.taskAssignments.updateTaskAssignmentById(taskAssignment.id!, { + message: 'updated message', + resolutionState: + 'approved' as UpdateTaskAssignmentByIdRequestBodyResolutionStateField, + } satisfies UpdateTaskAssignmentByIdRequestBody); + if (!(updatedTaskAssignment.message == 'updated message')) { + throw 'Assertion failed'; + } + if ( + !((toString(updatedTaskAssignment.resolutionState) as string) == 'approved') + ) { + throw 'Assertion failed'; + } + await expect(async () => { + await client.taskAssignments.deleteTaskAssignmentById(taskAssignment.id!); + }).rejects.toThrow(); + await client.files.deleteFileById(file.id); +}); +export {}; diff --git a/src/test/termsOfServices.generated.test.ts b/src/test/termsOfServices.generated.test.ts new file mode 100644 index 00000000..4bd2e58c --- /dev/null +++ b/src/test/termsOfServices.generated.test.ts @@ -0,0 +1,56 @@ +import { serializeTermsOfService } from '../schemas.generated.js'; +import { deserializeTermsOfService } from '../schemas.generated.js'; +import { serializeUpdateTermsOfServiceByIdRequestBody } from '../managers/termsOfServices.generated.js'; +import { deserializeUpdateTermsOfServiceByIdRequestBody } from '../managers/termsOfServices.generated.js'; +import { serializeUpdateTermsOfServiceByIdRequestBodyStatusField } from '../managers/termsOfServices.generated.js'; +import { deserializeUpdateTermsOfServiceByIdRequestBodyStatusField } from '../managers/termsOfServices.generated.js'; +import { serializeTermsOfServices } from '../schemas.generated.js'; +import { deserializeTermsOfServices } from '../schemas.generated.js'; +import { BoxClient } from '../client.generated.js'; +import { TermsOfService } from '../schemas.generated.js'; +import { UpdateTermsOfServiceByIdRequestBody } from '../managers/termsOfServices.generated.js'; +import { UpdateTermsOfServiceByIdRequestBodyStatusField } from '../managers/termsOfServices.generated.js'; +import { TermsOfServices } from '../schemas.generated.js'; +import { getDefaultClient } from './commons.generated.js'; +import { getOrCreateTermsOfServices } from './commons.generated.js'; +import { toString } from '../utils.js'; +import { sdToJson } from '../json.js'; +import { SerializedData } from '../json.js'; +import { sdIsEmpty } from '../json.js'; +import { sdIsBoolean } from '../json.js'; +import { sdIsNumber } from '../json.js'; +import { sdIsString } from '../json.js'; +import { sdIsList } from '../json.js'; +import { sdIsMap } from '../json.js'; +const client: BoxClient = getDefaultClient(); +test('testGetTermsOfServices', async function testGetTermsOfServices(): Promise { + const tos: TermsOfService = await getOrCreateTermsOfServices(); + const updatedTos1: TermsOfService = + await client.termsOfServices.updateTermsOfServiceById(tos.id, { + status: 'enabled' as UpdateTermsOfServiceByIdRequestBodyStatusField, + text: 'Enabled TOS', + } satisfies UpdateTermsOfServiceByIdRequestBody); + if (!((toString(updatedTos1.status) as string) == 'enabled')) { + throw 'Assertion failed'; + } + if (!(updatedTos1.text == 'Enabled TOS')) { + throw 'Assertion failed'; + } + const updatedTos2: TermsOfService = + await client.termsOfServices.updateTermsOfServiceById(tos.id, { + status: 'disabled' as UpdateTermsOfServiceByIdRequestBodyStatusField, + text: 'Disabled TOS', + } satisfies UpdateTermsOfServiceByIdRequestBody); + if (!((toString(updatedTos2.status) as string) == 'disabled')) { + throw 'Assertion failed'; + } + if (!(updatedTos2.text == 'Disabled TOS')) { + throw 'Assertion failed'; + } + const listTos: TermsOfServices = + await client.termsOfServices.getTermsOfService(); + if (!(listTos.totalCount! > 0)) { + throw 'Assertion failed'; + } +}); +export {}; diff --git a/src/utils.ts b/src/utils.ts index 8d2dfaca..8b81491a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -240,3 +240,72 @@ type CancellationToken = AbortSignal; export function createTokenAndCancelAfter(delay: number): CancellationToken { return AbortSignal.timeout(delay); } + +export type JwtKey = { + key: string; + passphrase: string; +}; + +export type JwtAlgorithm = + | 'HS256' + | 'HS384' + | 'HS512' + | 'RS256' + | 'RS384' + | 'RS512' + | 'ES256' + | 'ES384' + | 'ES512' + | 'PS256' + | 'PS384' + | 'PS512' + | 'none'; + +export type JwtSignOptions = { + algorithm?: JwtAlgorithm; + keyid?: string | undefined; + expiresIn?: string | number | undefined; + notBefore?: string | number | undefined; + audience?: string | string[] | undefined; + subject?: string | undefined; + issuer?: string | undefined; + jwtid?: string | undefined; + mutatePayload?: boolean | undefined; + noTimestamp?: boolean | undefined; + encoding?: string | undefined; + allowInsecureKeySizes?: boolean | undefined; + allowInvalidAsymmetricKeyTypes?: boolean | undefined; +}; + +/** + * Creates a JWT assertion. + * + * @param claims + * @param key + * @param options + * @returns + */ +export function createJwtAssertion( + claims: { + readonly [key: string]: any; + }, + key: JwtKey, + options: JwtSignOptions +): string { + const jwt = eval('require')('jsonwebtoken'); + return jwt.sign(claims, key, options); +} + +/** + * Reads a text file and returns its content. + */ +export function readTextFromFile(filepath: string): string { + return eval('require')('fs').readFileSync(filepath, 'utf8'); +} + +/** + * Get current epoch time in seconds. + */ +export function getEpochTimeInSeconds(): number { + return Math.floor(Date.now() / 1000); +}