diff --git a/ScimApp.ts b/ScimApp.ts index 7e784dc..1a6e329 100644 --- a/ScimApp.ts +++ b/ScimApp.ts @@ -1,6 +1,7 @@ import { IAppAccessors, IConfigurationExtend, + IConfigurationModify, ILogger, } from "@rocket.chat/apps-engine/definition/accessors"; import { @@ -9,12 +10,16 @@ import { } from "@rocket.chat/apps-engine/definition/api"; import { App } from "@rocket.chat/apps-engine/definition/App"; import { IAppInfo } from "@rocket.chat/apps-engine/definition/metadata"; -import { SettingType } from "@rocket.chat/apps-engine/definition/settings"; +import { + ISetting, + SettingType, +} from "@rocket.chat/apps-engine/definition/settings"; import { GroupEndpoint } from "./src/endpoints/GroupEndpoint"; import { GroupsEndpoint } from "./src/endpoints/GroupsEndpoint"; import { UserEndpoint } from "./src/endpoints/UserEndpoint"; import { UsersEndpoint } from "./src/endpoints/UsersEndpoint"; +import crypto = require("crypto"); export class ScimApp extends App { constructor(info: IAppInfo, logger: ILogger, accessors: IAppAccessors) { @@ -50,14 +55,15 @@ export class ScimApp extends App { public: false, i18nLabel: "Rocket.Chat Token", }); - } - // public async onSettingUpdated( - // setting: ISetting, - // configurationModify: IConfigurationModify, - // read: IRead, - // http: IHttp - // ): Promise { - // this.con - // } + configuration.settings.provideSetting({ + id: "auth-bearer", + type: SettingType.STRING, + packageValue: "", + required: true, + public: false, + i18nLabel: "A bearer token to access the SCIM endpoints.", + value: crypto.randomBytes(128).toString("base64").slice(0, 128), + }); + } } diff --git a/src/endpoints/Context.ts b/src/endpoints/Context.ts index c1ed04c..bfddd2e 100644 --- a/src/endpoints/Context.ts +++ b/src/endpoints/Context.ts @@ -9,6 +9,7 @@ import { IApiRequest, } from "@rocket.chat/apps-engine/definition/api"; import { EmptyRequestError } from "../errors/EmptyRequestError"; +import { UnauthorizedError } from "../errors/UnauthorizedError"; import { RcSdk } from "../rc-sdk/RcSdk"; import { Store } from "../store/Store"; @@ -52,4 +53,14 @@ export class Context { } return this.request.content; } + + public async checkAuth() { + const token = await this.read + .getEnvironmentReader() + .getSettings() + .getValueById("auth-bearer"); + if (this.request.headers.authorization !== `Bearer ${token}`) { + throw new UnauthorizedError(); + } + } } diff --git a/src/endpoints/ScimEndpoint.ts b/src/endpoints/ScimEndpoint.ts index bdfd269..3e6eae4 100644 --- a/src/endpoints/ScimEndpoint.ts +++ b/src/endpoints/ScimEndpoint.ts @@ -118,9 +118,16 @@ export abstract class ScimEndpoint extends ApiEndpoint { persis: IPersistence, ): Promise => { try { - return await method.bind(this)( - new Context(request, endpoint, read, modify, http, persis), + const ctx = new Context( + request, + endpoint, + read, + modify, + http, + persis, ); + await ctx.checkAuth(); + return await method.bind(this)(ctx); } catch (e) { let err: SCIMError; if (e.toSCIMError && typeof e.toSCIMError === "function") { diff --git a/src/errors/UnauthorizedError.ts b/src/errors/UnauthorizedError.ts new file mode 100644 index 0000000..6e2495e --- /dev/null +++ b/src/errors/UnauthorizedError.ts @@ -0,0 +1,13 @@ +import { HttpStatusCode } from "@rocket.chat/apps-engine/definition/accessors"; +import { SCIMError, SCIMErrorType } from "../scim/Error"; +import { BaseError } from "./BaseError"; + +export class UnauthorizedError extends BaseError { + public message = "The bearer token is missing or doesn't match."; + public toSCIMError(): SCIMError { + return new SCIMError() + .setStatus(HttpStatusCode.UNAUTHORIZED) + .setScimType(SCIMErrorType.INVALID_SYNTAX) + .setDetail(this.message); + } +}