diff --git a/backend/src/ee/middleware/requireSecretSnapshotAuth.ts b/backend/src/ee/middleware/requireSecretSnapshotAuth.ts index f43e9c315..af4d1e21c 100644 --- a/backend/src/ee/middleware/requireSecretSnapshotAuth.ts +++ b/backend/src/ee/middleware/requireSecretSnapshotAuth.ts @@ -15,32 +15,28 @@ import { const requireSecretSnapshotAuth = ({ acceptedRoles, }: { - acceptedRoles: string[]; + acceptedRoles: Array<'admin' | 'member'>; }) => { return async (req: Request, res: Response, next: NextFunction) => { - try { - const { secretSnapshotId } = req.params; - - const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId); - - if (!secretSnapshot) { - return next(SecretSnapshotNotFoundError({ - message: 'Failed to find secret snapshot' - })); - } - - await validateMembership({ - userId: req.user._id, - workspaceId: secretSnapshot.workspace, - acceptedRoles - }); - - req.secretSnapshot = secretSnapshot as any; - - next(); - } catch (err) { - return next(UnauthorizedRequestError({ message: 'Unable to authenticate secret snapshot' })); + const { secretSnapshotId } = req.params; + + const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId); + + if (!secretSnapshot) { + return next(SecretSnapshotNotFoundError({ + message: 'Failed to find secret snapshot' + })); } + + await validateMembership({ + userId: req.user._id, + workspaceId: secretSnapshot.workspace, + acceptedRoles + }); + + req.secretSnapshot = secretSnapshot as any; + + next(); } } diff --git a/backend/src/helpers/bot.ts b/backend/src/helpers/bot.ts index 5cfbeebf5..1e242778d 100644 --- a/backend/src/helpers/bot.ts +++ b/backend/src/helpers/bot.ts @@ -1,10 +1,16 @@ import * as Sentry from '@sentry/node'; +import { Types } from 'mongoose'; import { Bot, BotKey, Secret, ISecret, - IUser + IUser, + User, + IServiceAccount, + ServiceAccount, + IServiceTokenData, + ServiceTokenData } from '../models'; import { generateKeyPair, @@ -12,8 +18,88 @@ import { decryptSymmetric, decryptAsymmetric } from '../utils/crypto'; -import { SECRET_SHARED } from '../variables'; +import { + SECRET_SHARED, + AUTH_MODE_JWT, + AUTH_MODE_SERVICE_ACCOUNT, + AUTH_MODE_SERVICE_TOKEN, + AUTH_MODE_API_KEY +} from '../variables'; import { getEncryptionKey } from '../config'; +import { BotNotFoundError, UnauthorizedRequestError } from '../utils/errors'; +import { + validateMembership +} from '../helpers/membership'; +import { + validateUserClientForWorkspace +} from '../helpers/user'; +import { + validateServiceAccountClientForWorkspace +} from '../helpers/serviceAccount'; + +/** + * Validate authenticated clients for bot with id [botId] based + * on any known permissions. + * @param {Object} obj + * @param {Object} obj.authData - authenticated client details + * @param {Types.ObjectId} obj.botId - id of bot to validate against + * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles + */ +const validateClientForBot = async ({ + authData, + botId, + acceptedRoles +}: { + authData: { + authMode: string; + authPayload: IUser | IServiceAccount | IServiceTokenData; + }; + botId: Types.ObjectId; + acceptedRoles: Array<'admin' | 'member'>; +}) => { + const bot = await Bot.findById(botId); + + if (!bot) throw BotNotFoundError(); + + if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) { + await validateUserClientForWorkspace({ + user: authData.authPayload, + workspaceId: bot.workspace, + acceptedRoles + }); + + return bot; + } + + if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) { + await validateServiceAccountClientForWorkspace({ + serviceAccount: authData.authPayload, + workspaceId: bot.workspace + }); + + return bot; + } + + if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) { + throw UnauthorizedRequestError({ + message: 'Failed service token authorization for bot' + }); + } + + if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) { + await validateUserClientForWorkspace({ + user: authData.authPayload, + workspaceId: bot.workspace, + acceptedRoles + }); + + return bot; + } + + throw BotNotFoundError({ + message: 'Failed client authorization for bot' + }); +} /** * Create an inactive bot with name [name] for workspace with id [workspaceId] @@ -222,6 +308,7 @@ const decryptSymmetricHelper = async ({ } export { + validateClientForBot, createBot, getSecretsHelper, encryptSymmetricHelper, diff --git a/backend/src/helpers/membership.ts b/backend/src/helpers/membership.ts index 93c1ac7e9..8ae31812d 100644 --- a/backend/src/helpers/membership.ts +++ b/backend/src/helpers/membership.ts @@ -1,10 +1,106 @@ import * as Sentry from '@sentry/node'; import { Types } from 'mongoose'; -import { Membership, Key } from '../models'; +import { + Membership, + Key, + IUser, + User, + IServiceAccount, + ServiceAccount, + IServiceTokenData, + ServiceTokenData +} from '../models'; import { MembershipNotFoundError, - BadRequestError + BadRequestError, + UnauthorizedRequestError } from '../utils/errors'; +import { + AUTH_MODE_JWT, + AUTH_MODE_SERVICE_ACCOUNT, + AUTH_MODE_SERVICE_TOKEN, + AUTH_MODE_API_KEY +} from '../variables'; +import { + validateUserClientForWorkspace +} from '../helpers/user'; +import { + validateServiceAccountClientForWorkspace +} from '../helpers/serviceAccount'; +import { + validateServiceTokenDataClientForWorkspace +} from '../helpers/serviceTokenData'; + +/** + * Validate authenticated clients for membership with id [membershipId] based + * on any known permissions. + * @param {Object} obj + * @param {Object} obj.authData - authenticated client details + * @param {Types.ObjectId} obj.membershipId - id of membership to validate against + * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspaceRoles + * @returns {Membership} - validated membership + */ +const validateClientForMembership = async ({ + authData, + membershipId, + acceptedRoles +}: { + authData: { + authMode: string; + authPayload: IUser | IServiceAccount | IServiceTokenData; + }; + membershipId: Types.ObjectId; + acceptedRoles: Array<'admin' | 'member'>; +}) => { + + const membership = await Membership.findById(membershipId); + + if (!membership) throw MembershipNotFoundError({ + message: 'Failed to find membership' + }); + + if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) { + await validateUserClientForWorkspace({ + user: authData.authPayload, + workspaceId: membership.workspace, + acceptedRoles + }); + + return membership; + } + + if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) { + await validateServiceAccountClientForWorkspace({ + serviceAccount: authData.authPayload, + workspaceId: membership.workspace + }); + + return membership; + } + + if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) { + await validateServiceTokenDataClientForWorkspace({ + serviceTokenData: authData.authPayload, + workspaceId: new Types.ObjectId(membership.workspace) + }); + + return membership; + } + + if (authData.authMode == AUTH_MODE_API_KEY && authData.authPayload instanceof User) { + await validateUserClientForWorkspace({ + user: authData.authPayload, + workspaceId: membership.workspace, + acceptedRoles + }); + + return membership; + } + + throw UnauthorizedRequestError({ + message: 'Failed client authorization for membership' + }); +} /** * Validate that user with id [userId] is a member of workspace with id [workspaceId] @@ -21,7 +117,7 @@ const validateMembership = async ({ }: { userId: Types.ObjectId; workspaceId: Types.ObjectId; - acceptedRoles?: string[]; + acceptedRoles?: Array<'admin' | 'member'>; }) => { const membership = await Membership.findOne({ @@ -134,6 +230,7 @@ const deleteMembership = async ({ membershipId }: { membershipId: string }) => { }; export { + validateClientForMembership, validateMembership, addMemberships, findMembership, diff --git a/backend/src/helpers/membershipOrg.ts b/backend/src/helpers/membershipOrg.ts index efe95e2c7..b34e5dd2f 100644 --- a/backend/src/helpers/membershipOrg.ts +++ b/backend/src/helpers/membershipOrg.ts @@ -1,10 +1,98 @@ import * as Sentry from '@sentry/node'; import { Types } from 'mongoose'; -import { MembershipOrg, Workspace, Membership, Key } from '../models'; +import { + MembershipOrg, + Workspace, + Membership, + Key, + IUser, + User, + IServiceAccount, + ServiceAccount, + IServiceTokenData, + ServiceTokenData +} from '../models'; import { MembershipOrgNotFoundError, - BadRequestError + BadRequestError, + UnauthorizedRequestError } from '../utils/errors'; +import { + AUTH_MODE_JWT, + AUTH_MODE_SERVICE_ACCOUNT, + AUTH_MODE_SERVICE_TOKEN, + AUTH_MODE_API_KEY +} from '../variables'; + +/** + * Validate authenticated clients for organization membership with id [membershipOrgId] based + * on any known permissions. + * @param {Object} obj + * @param {Object} obj.authData - authenticated client details + * @param {Types.ObjectId} obj.membershipOrgId - id of organization membership to validate against + * @param {Array<'owner' | 'admin' | 'member'>} obj.acceptedRoles - accepted organization roles + * @param {MembershipOrg} - validated organization membership + */ +const validateClientForMembershipOrg = async ({ + authData, + membershipOrgId, + acceptedRoles, + acceptedStatuses +}: { + authData: { + authMode: string; + authPayload: IUser | IServiceAccount | IServiceTokenData; + }; + membershipOrgId: Types.ObjectId; + acceptedRoles: Array<'owner' | 'admin' | 'member'>; + acceptedStatuses: Array<'invited' | 'accepted'>; +}) => { + const membershipOrg = await MembershipOrg.findById(membershipOrgId); + + if (!membershipOrg) throw MembershipOrgNotFoundError({ + message: 'Failed to find organization membership ' + }); + + if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) { + await validateMembershipOrg({ + userId: authData.authPayload._id, + organizationId: membershipOrg.organization, + acceptedRoles, + acceptedStatuses + }); + + return membershipOrg; + } + + if (authData.authMode === AUTH_MODE_SERVICE_ACCOUNT && authData.authPayload instanceof ServiceAccount) { + if (!authData.authPayload.organization.equals(membershipOrg.organization)) throw UnauthorizedRequestError({ + message: 'Failed service account client authorization for organization membership' + }); + + return membershipOrg; + } + + if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) { + throw UnauthorizedRequestError({ + message: 'Failed service account client authorization for organization membership' + }); + } + + if (authData.authMode === AUTH_MODE_API_KEY && authData.authPayload instanceof User) { + await validateMembershipOrg({ + userId: authData.authPayload._id, + organizationId: membershipOrg.organization, + acceptedRoles, + acceptedStatuses + }); + + return membershipOrg; + } + + throw UnauthorizedRequestError({ + message: 'Failed client authorization for organization membership' + }); +} /** * Validate that user with id [userId] is a member of organization with id [organizationId] @@ -22,8 +110,8 @@ const validateMembershipOrg = async ({ }: { userId: Types.ObjectId; organizationId: Types.ObjectId; - acceptedRoles: Array<'owner' | 'admin' | 'member'>; - acceptedStatuses: Array<'invited' | 'accepted'>; + acceptedRoles?: Array<'owner' | 'admin' | 'member'>; + acceptedStatuses?: Array<'invited' | 'accepted'>; }) => { const membershipOrg = await MembershipOrg.findOne({ user: userId, @@ -34,12 +122,16 @@ const validateMembershipOrg = async ({ throw MembershipOrgNotFoundError({ message: 'Failed to find organization membership' }); } - if (!acceptedRoles.includes(membershipOrg.role)) { - throw BadRequestError({ message: 'Failed to validate organization membership role' }); + if (acceptedRoles) { + if (!acceptedRoles.includes(membershipOrg.role)) { + throw UnauthorizedRequestError({ message: 'Failed to validate organization membership role' }); + } } - - if (!acceptedStatuses.includes(membershipOrg.status)) { - throw BadRequestError({ message: 'Failed to validate organization membership status' }); + + if (acceptedStatuses) { + if (!acceptedStatuses.includes(membershipOrg.status)) { + throw UnauthorizedRequestError({ message: 'Failed to validate organization membership status' }); + } } return membershipOrg; @@ -164,6 +256,7 @@ const deleteMembershipOrg = async ({ }; export { + validateClientForMembershipOrg, validateMembershipOrg, findMembershipOrg, addMembershipsOrg, diff --git a/backend/src/helpers/organization.ts b/backend/src/helpers/organization.ts index 10788cd8f..9840c9075 100644 --- a/backend/src/helpers/organization.ts +++ b/backend/src/helpers/organization.ts @@ -86,7 +86,7 @@ const validateClientForOrganization = async ({ if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) { throw UnauthorizedRequestError({ - message: 'Failed service token authorization for organization resource' + message: 'Failed service token authorization for organization' }); } @@ -102,7 +102,7 @@ const validateClientForOrganization = async ({ } throw UnauthorizedRequestError({ - message: 'Failed client authorization for organization resource' + message: 'Failed client authorization for organization' }); } diff --git a/backend/src/helpers/serviceAccount.ts b/backend/src/helpers/serviceAccount.ts index c8d126491..892767259 100644 --- a/backend/src/helpers/serviceAccount.ts +++ b/backend/src/helpers/serviceAccount.ts @@ -9,6 +9,7 @@ import { IServiceTokenData, ISecret, IOrganization, + IServiceAccountWorkspacePermission, ServiceAccountWorkspacePermission } from '../models'; import { @@ -111,16 +112,19 @@ const validateClientForServiceAccount = async ({ requiredPermissions?: string[]; }) => { if (environment) { + // case: environment specified -> + // evaluate service account authorization for workspace + // in the context of a specific environment [environment] const permission = await ServiceAccountWorkspacePermission.findOne({ serviceAccount, workspace: new Types.ObjectId(workspaceId), environment }); - + if (!permission) throw UnauthorizedRequestError({ message: 'Failed service account authorization for the given workspace environment' }); - + let runningIsDisallowed = false; requiredPermissions?.forEach((requiredPermission: string) => { switch (requiredPermission) { @@ -140,6 +144,20 @@ const validateClientForServiceAccount = async ({ }); } }); + + } else { + // case: no environment specified -> + // evaluate service account authorization for workspace + // without need of environment [environment] + + const permission = await ServiceAccountWorkspacePermission.findOne({ + serviceAccount, + workspace: new Types.ObjectId(workspaceId) + }); + + if (!permission) throw UnauthorizedRequestError({ + message: 'Failed service account authorization for the given workspace' + }); } } diff --git a/backend/src/helpers/serviceTokenData.ts b/backend/src/helpers/serviceTokenData.ts index 70c9d416b..8616ba237 100644 --- a/backend/src/helpers/serviceTokenData.ts +++ b/backend/src/helpers/serviceTokenData.ts @@ -34,20 +34,24 @@ import { UnauthorizedRequestError } from '../utils/errors'; }); } - if (serviceTokenData.environment !== environment) { - // case: invalid environment passed - throw UnauthorizedRequestError({ - message: 'Failed service token authorization for the given workspace environment' - }); - } - - requiredPermissions?.forEach((permission) => { - if (!serviceTokenData.permissions.includes(permission)) { + if (environment) { + // case: environment is specified + + if (serviceTokenData.environment !== environment) { + // case: invalid environment passed throw UnauthorizedRequestError({ - message: `Failed service token authorization for the given workspace environment action: ${permission}` + message: 'Failed service token authorization for the given workspace environment' }); } - }); + + requiredPermissions?.forEach((permission) => { + if (!serviceTokenData.permissions.includes(permission)) { + throw UnauthorizedRequestError({ + message: `Failed service token authorization for the given workspace environment action: ${permission}` + }); + } + }); + } } /** diff --git a/backend/src/helpers/user.ts b/backend/src/helpers/user.ts index 549991148..7a0c52cb7 100644 --- a/backend/src/helpers/user.ts +++ b/backend/src/helpers/user.ts @@ -179,21 +179,23 @@ const validateUserClientForWorkspace = async ({ user, workspaceId, environment, + acceptedRoles, requiredPermissions }: { user: IUser; workspaceId: Types.ObjectId; environment?: string; + acceptedRoles: Array<'admin' | 'member'>; requiredPermissions?: string[]; }) => { // validate user membership in workspace const membership = await validateMembership({ userId: user._id, - workspaceId + workspaceId, + acceptedRoles }); - // TODO: refactor let runningIsDisallowed = false; requiredPermissions?.forEach((requiredPermission: string) => { switch (requiredPermission) { diff --git a/backend/src/helpers/workspace.ts b/backend/src/helpers/workspace.ts index 7b077e7d1..f3b27bf10 100644 --- a/backend/src/helpers/workspace.ts +++ b/backend/src/helpers/workspace.ts @@ -19,7 +19,7 @@ import { validateUserClientForWorkspace } from '../helpers/user'; import { validateServiceAccountClientForWorkspace } from '../helpers/serviceAccount'; import { validateServiceTokenDataClientForWorkspace } from '../helpers/serviceTokenData'; import { validateMembership } from '../helpers/membership'; -import { UnauthorizedRequestError } from '../utils/errors'; +import { UnauthorizedRequestError, WorkspaceNotFoundError } from '../utils/errors'; import { AUTH_MODE_JWT, AUTH_MODE_SERVICE_ACCOUNT, @@ -34,28 +34,38 @@ import { * @param {Object} obj.authData - authenticated client details * @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against * @param {String} obj.environment - (optional) environment in workspace to validate against + * @param {Array<'admin' | 'member'>} obj.acceptedRoles - accepted workspace roles * @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint */ const validateClientForWorkspace = async ({ authData, workspaceId, environment, + acceptedRoles, requiredPermissions }: { authData: { authMode: string; authPayload: IUser | IServiceAccount | IServiceTokenData; - }, + }; workspaceId: Types.ObjectId; environment?: string; + acceptedRoles: Array<'admin' | 'member'>; requiredPermissions?: string[]; }) => { + + const workspace = await Workspace.findById(workspaceId); + + if (!workspace) throw WorkspaceNotFoundError({ + message: 'Failed to find workspace' + }); if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) { const membership = await validateUserClientForWorkspace({ user: authData.authPayload, workspaceId, environment, + acceptedRoles, requiredPermissions }); @@ -89,6 +99,7 @@ const validateClientForWorkspace = async ({ user: authData.authPayload, workspaceId, environment, + acceptedRoles, requiredPermissions }); @@ -96,7 +107,7 @@ const validateClientForWorkspace = async ({ } throw UnauthorizedRequestError({ - message: 'Failed client authorization for workspace resource' + message: 'Failed client authorization for workspace' }); } diff --git a/backend/src/middleware/requireBotAuth.ts b/backend/src/middleware/requireBotAuth.ts index c06f1c861..df7cf016e 100644 --- a/backend/src/middleware/requireBotAuth.ts +++ b/backend/src/middleware/requireBotAuth.ts @@ -5,11 +5,13 @@ import { AccountNotFoundError } from '../utils/errors'; type req = 'params' | 'body' | 'query'; +// TODO: transform + const requireBotAuth = ({ acceptedRoles, location = 'params' }: { - acceptedRoles: string[]; + acceptedRoles: Array<'admin' | 'member'>; location?: req; }) => { return async (req: Request, res: Response, next: NextFunction) => { diff --git a/backend/src/middleware/requireIntegrationAuth.ts b/backend/src/middleware/requireIntegrationAuth.ts index 51051584c..7b6cac880 100644 --- a/backend/src/middleware/requireIntegrationAuth.ts +++ b/backend/src/middleware/requireIntegrationAuth.ts @@ -13,7 +13,7 @@ import { IntegrationNotFoundError, UnauthorizedRequestError } from '../utils/err const requireIntegrationAuth = ({ acceptedRoles }: { - acceptedRoles: string[]; + acceptedRoles: Array<'admin' | 'member'>; }) => { return async (req: Request, res: Response, next: NextFunction) => { // integration authorization middleware diff --git a/backend/src/middleware/requireIntegrationAuthorizationAuth.ts b/backend/src/middleware/requireIntegrationAuthorizationAuth.ts index 07f347ecc..8e28227f3 100644 --- a/backend/src/middleware/requireIntegrationAuthorizationAuth.ts +++ b/backend/src/middleware/requireIntegrationAuthorizationAuth.ts @@ -19,7 +19,7 @@ const requireIntegrationAuthorizationAuth = ({ attachAccessToken = true, location = 'params' }: { - acceptedRoles: string[]; + acceptedRoles: Array<'admin' | 'member'>; attachAccessToken?: boolean; location?: req; }) => { diff --git a/backend/src/middleware/requireMembershipAuth.ts b/backend/src/middleware/requireMembershipAuth.ts index 136fabadb..d06c0bda8 100644 --- a/backend/src/middleware/requireMembershipAuth.ts +++ b/backend/src/middleware/requireMembershipAuth.ts @@ -1,9 +1,13 @@ +import { Types } from 'mongoose'; import { Request, Response, NextFunction } from 'express'; import { UnauthorizedRequestError } from '../utils/errors'; import { Membership, } from '../models'; -import { validateMembership } from '../helpers/membership'; +import { + validateClientForMembership, + validateMembership +} from '../helpers/membership'; type req = 'params' | 'body' | 'query'; @@ -16,43 +20,25 @@ type req = 'params' | 'body' | 'query'; */ const requireMembershipAuth = ({ acceptedRoles, - location = 'params' + locationMembershipId = 'params' }: { - acceptedRoles: string[]; - location?: req; + acceptedRoles: Array<'admin' | 'member'>; + locationMembershipId: req }) => { return async ( req: Request, res: Response, next: NextFunction ) => { - try { - const { membershipId } = req[location]; + const { membershipId } = req[locationMembershipId]; - const membership = await Membership.findById(membershipId); - - if (!membership) throw new Error('Failed to find target membership'); - - const userMembership = await Membership.findOne({ - workspace: membership.workspace - }); - - if (!userMembership) throw new Error('Failed to validate own membership') - - const targetMembership = await validateMembership({ - userId: req.user._id, - workspaceId: membership.workspace, + req.targetMembership = await validateClientForMembership({ + authData: req.authData, + membershipId: new Types.ObjectId(membershipId), acceptedRoles }); - req.targetMembership = targetMembership; - return next(); - } catch (err) { - return next(UnauthorizedRequestError({ - message: 'Unable to validate workspace membership' - })); - } } } diff --git a/backend/src/middleware/requireMembershipOrgAuth.ts b/backend/src/middleware/requireMembershipOrgAuth.ts index f3ad32391..b34c9c5e2 100644 --- a/backend/src/middleware/requireMembershipOrgAuth.ts +++ b/backend/src/middleware/requireMembershipOrgAuth.ts @@ -1,11 +1,17 @@ +import { Types } from 'mongoose'; import { Request, Response, NextFunction } from 'express'; import { UnauthorizedRequestError } from '../utils/errors'; import { MembershipOrg } from '../models'; -import { validateMembershipOrg } from '../helpers/membershipOrg'; +import { + validateClientForMembershipOrg, + validateMembershipOrg +} from '../helpers/membershipOrg'; +// TODO: transform + type req = 'params' | 'body' | 'query'; /** @@ -18,32 +24,23 @@ type req = 'params' | 'body' | 'query'; const requireMembershipOrgAuth = ({ acceptedRoles, acceptedStatuses, - location = 'params' + locationMembershipOrgId = 'params' }: { acceptedRoles: Array<'owner' | 'admin' | 'member'>; acceptedStatuses: Array<'invited' | 'accepted'>; - location?: req; + locationMembershipOrgId?: req; }) => { return async (req: Request, res: Response, next: NextFunction) => { - try { - const { membershipId } = req[location]; - const membershipOrg = await MembershipOrg.findById(membershipId); - - if (!membershipOrg) throw new Error('Failed to find target organization membership'); - - req.targetMembership = await validateMembershipOrg({ - userId: req.user._id, - organizationId: membershipOrg.organization, - acceptedRoles, - acceptedStatuses - }); - - return next(); - } catch (err) { - return next(UnauthorizedRequestError({ - message: 'Unable to validate organization membership' - })); - } + const { membershipId } = req[locationMembershipOrgId]; + + req.membershipOrg = await validateClientForMembershipOrg({ + authData: req.authData, + membershipOrgId: new Types.ObjectId(membershipId), + acceptedRoles, + acceptedStatuses + }); + + return next(); } } diff --git a/backend/src/middleware/requireOrganizationAuth.ts b/backend/src/middleware/requireOrganizationAuth.ts index 8d8e967f2..f6d8eb8ce 100644 --- a/backend/src/middleware/requireOrganizationAuth.ts +++ b/backend/src/middleware/requireOrganizationAuth.ts @@ -26,8 +26,6 @@ const requireOrganizationAuth = ({ return async (req: Request, res: Response, next: NextFunction) => { const { organizationId } = req[locationOrganizationId]; - // TODO: incorporate [acceptedRoles] and [acceptedStatuses] - const { organization, membershipOrg } = await validateClientForOrganization({ authData: req.authData, organizationId: new Types.ObjectId(organizationId), diff --git a/backend/src/middleware/requireSecretAuth.ts b/backend/src/middleware/requireSecretAuth.ts index c86b7b680..c131e624f 100644 --- a/backend/src/middleware/requireSecretAuth.ts +++ b/backend/src/middleware/requireSecretAuth.ts @@ -17,32 +17,28 @@ import { const requireSecretAuth = ({ acceptedRoles }: { - acceptedRoles: string[]; + acceptedRoles: Array<'admin' | 'member'>; }) => { return async (req: Request, res: Response, next: NextFunction) => { - try { - const { secretId } = req.params; - - const secret = await Secret.findById(secretId); - - if (!secret) { - return next(SecretNotFoundError({ - message: 'Failed to find secret' - })); - } - - await validateMembership({ - userId: req.user._id, - workspaceId: secret.workspace, - acceptedRoles - }); - - req._secret = secret; - - next(); - } catch (err) { - return next(UnauthorizedRequestError({ message: 'Unable to authenticate secret' })); + const { secretId } = req.params; + + const secret = await Secret.findById(secretId); + + if (!secret) { + return next(SecretNotFoundError({ + message: 'Failed to find secret' + })); } + + await validateMembership({ + userId: req.user._id, + workspaceId: secret.workspace, + acceptedRoles + }); + + req._secret = secret; + + next(); } } diff --git a/backend/src/middleware/requireServiceTokenDataAuth.ts b/backend/src/middleware/requireServiceTokenDataAuth.ts index 513cbe604..f474e23c0 100644 --- a/backend/src/middleware/requireServiceTokenDataAuth.ts +++ b/backend/src/middleware/requireServiceTokenDataAuth.ts @@ -9,7 +9,7 @@ const requireServiceTokenDataAuth = ({ acceptedRoles, location = 'params' }: { - acceptedRoles: string[]; + acceptedRoles: Array<'admin' | 'member'>; location?: req; }) => { return async (req: Request, res: Response, next: NextFunction) => { diff --git a/backend/src/middleware/requireWorkspaceAuth.ts b/backend/src/middleware/requireWorkspaceAuth.ts index 64c1c37f2..9ca1b3f71 100644 --- a/backend/src/middleware/requireWorkspaceAuth.ts +++ b/backend/src/middleware/requireWorkspaceAuth.ts @@ -19,7 +19,7 @@ const requireWorkspaceAuth = ({ locationEnvironment = undefined, requiredPermissions = [] }: { - acceptedRoles: string[]; + acceptedRoles: Array<'admin' | 'member'>; locationWorkspaceId: req; locationEnvironment?: req | undefined; requiredPermissions?: string[]; @@ -34,6 +34,7 @@ const requireWorkspaceAuth = ({ authData: req.authData, workspaceId: new Types.ObjectId(workspaceId), environment, + acceptedRoles, requiredPermissions }); diff --git a/backend/src/routes/v2/workspace.ts b/backend/src/routes/v2/workspace.ts index d2180624b..e258636fb 100644 --- a/backend/src/routes/v2/workspace.ts +++ b/backend/src/routes/v2/workspace.ts @@ -106,7 +106,8 @@ router.patch( // TODO - rewire dashboard to this route locationWorkspaceId: 'params' }), requireMembershipAuth({ - acceptedRoles: [ADMIN] + acceptedRoles: [ADMIN], + locationMembershipId: 'params' }), workspaceController.updateWorkspaceMembership ); @@ -124,7 +125,8 @@ router.delete( // TODO - rewire dashboard to this route locationWorkspaceId: 'params' }), requireMembershipAuth({ - acceptedRoles: [ADMIN] + acceptedRoles: [ADMIN], + locationMembershipId: 'params' }), workspaceController.deleteWorkspaceMembership ); diff --git a/backend/src/utils/errors.ts b/backend/src/utils/errors.ts index 4799728b9..2542f84ff 100644 --- a/backend/src/utils/errors.ts +++ b/backend/src/utils/errors.ts @@ -202,4 +202,13 @@ export const ServiceAccountKeyNotFoundError = (error?: Partial) => new RequestError({ + logLevel: error?.logLevel ?? LogLevel.ERROR, + statusCode: error?.statusCode ?? 404, + type: error?.type ?? 'bot_not_found_error', + message: error?.message ?? 'The requested bot was not found', + context: error?.context, + stack: error?.stack +}) + //* ----->[MISC ERRORS]<----- diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 280669e70..f19bd1120 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -1,6 +1,6 @@ --- title: "Quickstart" -description: "Start managing your developer secrets and configs with Infisical in 10 minutes." +description: "Start managing developer secrets and configs with Infisical in minutes." --- These examples demonstrate how to store and fetch environment variables from [Infisical Cloud](https://app.infisical.com) into your application. diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx index e6e2622cd..12121f120 100644 --- a/docs/integrations/overview.mdx +++ b/docs/integrations/overview.mdx @@ -42,7 +42,5 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi | [Flask](/integrations/frameworks/flask) | Framework | Available | | [Laravel](/integrations/frameworks/laravel) | Framework | Available | | [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available | -| GCP | Cloud | Coming soon | -| DigitalOcean | Cloud | Coming soon | -| GitHub Actions | CI/CD | Coming soon | +| GCP Secret Manager | Cloud | Coming soon | | Jenkins | CI/CD | Coming soon |