Continue refactoring remaining middleware to be compatible with multiple clients

This commit is contained in:
Tuan Dang
2023-04-11 23:58:29 +03:00
parent 5032450b1c
commit 9df8e8926d
22 changed files with 438 additions and 141 deletions

View File

@@ -15,32 +15,28 @@ import {
const requireSecretSnapshotAuth = ({ const requireSecretSnapshotAuth = ({
acceptedRoles, acceptedRoles,
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { const { secretSnapshotId } = req.params;
const { secretSnapshotId } = req.params;
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId);
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId);
if (!secretSnapshot) {
if (!secretSnapshot) { return next(SecretSnapshotNotFoundError({
return next(SecretSnapshotNotFoundError({ message: 'Failed to find secret snapshot'
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' }));
} }
await validateMembership({
userId: req.user._id,
workspaceId: secretSnapshot.workspace,
acceptedRoles
});
req.secretSnapshot = secretSnapshot as any;
next();
} }
} }

View File

@@ -1,10 +1,16 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import { import {
Bot, Bot,
BotKey, BotKey,
Secret, Secret,
ISecret, ISecret,
IUser IUser,
User,
IServiceAccount,
ServiceAccount,
IServiceTokenData,
ServiceTokenData
} from '../models'; } from '../models';
import { import {
generateKeyPair, generateKeyPair,
@@ -12,8 +18,88 @@ import {
decryptSymmetric, decryptSymmetric,
decryptAsymmetric decryptAsymmetric
} from '../utils/crypto'; } 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 { 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] * Create an inactive bot with name [name] for workspace with id [workspaceId]
@@ -222,6 +308,7 @@ const decryptSymmetricHelper = async ({
} }
export { export {
validateClientForBot,
createBot, createBot,
getSecretsHelper, getSecretsHelper,
encryptSymmetricHelper, encryptSymmetricHelper,

View File

@@ -1,10 +1,106 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import { Membership, Key } from '../models'; import {
Membership,
Key,
IUser,
User,
IServiceAccount,
ServiceAccount,
IServiceTokenData,
ServiceTokenData
} from '../models';
import { import {
MembershipNotFoundError, MembershipNotFoundError,
BadRequestError BadRequestError,
UnauthorizedRequestError
} from '../utils/errors'; } 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] * Validate that user with id [userId] is a member of workspace with id [workspaceId]
@@ -21,7 +117,7 @@ const validateMembership = async ({
}: { }: {
userId: Types.ObjectId; userId: Types.ObjectId;
workspaceId: Types.ObjectId; workspaceId: Types.ObjectId;
acceptedRoles?: string[]; acceptedRoles?: Array<'admin' | 'member'>;
}) => { }) => {
const membership = await Membership.findOne({ const membership = await Membership.findOne({
@@ -134,6 +230,7 @@ const deleteMembership = async ({ membershipId }: { membershipId: string }) => {
}; };
export { export {
validateClientForMembership,
validateMembership, validateMembership,
addMemberships, addMemberships,
findMembership, findMembership,

View File

@@ -1,10 +1,98 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose'; 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 { import {
MembershipOrgNotFoundError, MembershipOrgNotFoundError,
BadRequestError BadRequestError,
UnauthorizedRequestError
} from '../utils/errors'; } 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] * Validate that user with id [userId] is a member of organization with id [organizationId]
@@ -22,8 +110,8 @@ const validateMembershipOrg = async ({
}: { }: {
userId: Types.ObjectId; userId: Types.ObjectId;
organizationId: Types.ObjectId; organizationId: Types.ObjectId;
acceptedRoles: Array<'owner' | 'admin' | 'member'>; acceptedRoles?: Array<'owner' | 'admin' | 'member'>;
acceptedStatuses: Array<'invited' | 'accepted'>; acceptedStatuses?: Array<'invited' | 'accepted'>;
}) => { }) => {
const membershipOrg = await MembershipOrg.findOne({ const membershipOrg = await MembershipOrg.findOne({
user: userId, user: userId,
@@ -34,12 +122,16 @@ const validateMembershipOrg = async ({
throw MembershipOrgNotFoundError({ message: 'Failed to find organization membership' }); throw MembershipOrgNotFoundError({ message: 'Failed to find organization membership' });
} }
if (!acceptedRoles.includes(membershipOrg.role)) { if (acceptedRoles) {
throw BadRequestError({ message: 'Failed to validate organization membership role' }); if (!acceptedRoles.includes(membershipOrg.role)) {
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership role' });
}
} }
if (!acceptedStatuses.includes(membershipOrg.status)) { if (acceptedStatuses) {
throw BadRequestError({ message: 'Failed to validate organization membership status' }); if (!acceptedStatuses.includes(membershipOrg.status)) {
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership status' });
}
} }
return membershipOrg; return membershipOrg;
@@ -164,6 +256,7 @@ const deleteMembershipOrg = async ({
}; };
export { export {
validateClientForMembershipOrg,
validateMembershipOrg, validateMembershipOrg,
findMembershipOrg, findMembershipOrg,
addMembershipsOrg, addMembershipsOrg,

View File

@@ -86,7 +86,7 @@ const validateClientForOrganization = async ({
if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) { if (authData.authMode === AUTH_MODE_SERVICE_TOKEN && authData.authPayload instanceof ServiceTokenData) {
throw UnauthorizedRequestError({ 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({ throw UnauthorizedRequestError({
message: 'Failed client authorization for organization resource' message: 'Failed client authorization for organization'
}); });
} }

View File

@@ -9,6 +9,7 @@ import {
IServiceTokenData, IServiceTokenData,
ISecret, ISecret,
IOrganization, IOrganization,
IServiceAccountWorkspacePermission,
ServiceAccountWorkspacePermission ServiceAccountWorkspacePermission
} from '../models'; } from '../models';
import { import {
@@ -111,16 +112,19 @@ const validateClientForServiceAccount = async ({
requiredPermissions?: string[]; requiredPermissions?: string[];
}) => { }) => {
if (environment) { if (environment) {
// case: environment specified ->
// evaluate service account authorization for workspace
// in the context of a specific environment [environment]
const permission = await ServiceAccountWorkspacePermission.findOne({ const permission = await ServiceAccountWorkspacePermission.findOne({
serviceAccount, serviceAccount,
workspace: new Types.ObjectId(workspaceId), workspace: new Types.ObjectId(workspaceId),
environment environment
}); });
if (!permission) throw UnauthorizedRequestError({ if (!permission) throw UnauthorizedRequestError({
message: 'Failed service account authorization for the given workspace environment' message: 'Failed service account authorization for the given workspace environment'
}); });
let runningIsDisallowed = false; let runningIsDisallowed = false;
requiredPermissions?.forEach((requiredPermission: string) => { requiredPermissions?.forEach((requiredPermission: string) => {
switch (requiredPermission) { 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'
});
} }
} }

View File

@@ -34,20 +34,24 @@ import { UnauthorizedRequestError } from '../utils/errors';
}); });
} }
if (serviceTokenData.environment !== environment) { if (environment) {
// case: invalid environment passed // case: environment is specified
throw UnauthorizedRequestError({
message: 'Failed service token authorization for the given workspace environment' if (serviceTokenData.environment !== environment) {
}); // case: invalid environment passed
}
requiredPermissions?.forEach((permission) => {
if (!serviceTokenData.permissions.includes(permission)) {
throw UnauthorizedRequestError({ 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}`
});
}
});
}
} }
/** /**

View File

@@ -179,21 +179,23 @@ const validateUserClientForWorkspace = async ({
user, user,
workspaceId, workspaceId,
environment, environment,
acceptedRoles,
requiredPermissions requiredPermissions
}: { }: {
user: IUser; user: IUser;
workspaceId: Types.ObjectId; workspaceId: Types.ObjectId;
environment?: string; environment?: string;
acceptedRoles: Array<'admin' | 'member'>;
requiredPermissions?: string[]; requiredPermissions?: string[];
}) => { }) => {
// validate user membership in workspace // validate user membership in workspace
const membership = await validateMembership({ const membership = await validateMembership({
userId: user._id, userId: user._id,
workspaceId workspaceId,
acceptedRoles
}); });
// TODO: refactor
let runningIsDisallowed = false; let runningIsDisallowed = false;
requiredPermissions?.forEach((requiredPermission: string) => { requiredPermissions?.forEach((requiredPermission: string) => {
switch (requiredPermission) { switch (requiredPermission) {

View File

@@ -19,7 +19,7 @@ import { validateUserClientForWorkspace } from '../helpers/user';
import { validateServiceAccountClientForWorkspace } from '../helpers/serviceAccount'; import { validateServiceAccountClientForWorkspace } from '../helpers/serviceAccount';
import { validateServiceTokenDataClientForWorkspace } from '../helpers/serviceTokenData'; import { validateServiceTokenDataClientForWorkspace } from '../helpers/serviceTokenData';
import { validateMembership } from '../helpers/membership'; import { validateMembership } from '../helpers/membership';
import { UnauthorizedRequestError } from '../utils/errors'; import { UnauthorizedRequestError, WorkspaceNotFoundError } from '../utils/errors';
import { import {
AUTH_MODE_JWT, AUTH_MODE_JWT,
AUTH_MODE_SERVICE_ACCOUNT, AUTH_MODE_SERVICE_ACCOUNT,
@@ -34,28 +34,38 @@ import {
* @param {Object} obj.authData - authenticated client details * @param {Object} obj.authData - authenticated client details
* @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against * @param {Types.ObjectId} obj.workspaceId - id of workspace to validate against
* @param {String} obj.environment - (optional) environment in 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 * @param {String[]} obj.requiredPermissions - required permissions as part of the endpoint
*/ */
const validateClientForWorkspace = async ({ const validateClientForWorkspace = async ({
authData, authData,
workspaceId, workspaceId,
environment, environment,
acceptedRoles,
requiredPermissions requiredPermissions
}: { }: {
authData: { authData: {
authMode: string; authMode: string;
authPayload: IUser | IServiceAccount | IServiceTokenData; authPayload: IUser | IServiceAccount | IServiceTokenData;
}, };
workspaceId: Types.ObjectId; workspaceId: Types.ObjectId;
environment?: string; environment?: string;
acceptedRoles: Array<'admin' | 'member'>;
requiredPermissions?: string[]; 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) { if (authData.authMode === AUTH_MODE_JWT && authData.authPayload instanceof User) {
const membership = await validateUserClientForWorkspace({ const membership = await validateUserClientForWorkspace({
user: authData.authPayload, user: authData.authPayload,
workspaceId, workspaceId,
environment, environment,
acceptedRoles,
requiredPermissions requiredPermissions
}); });
@@ -89,6 +99,7 @@ const validateClientForWorkspace = async ({
user: authData.authPayload, user: authData.authPayload,
workspaceId, workspaceId,
environment, environment,
acceptedRoles,
requiredPermissions requiredPermissions
}); });
@@ -96,7 +107,7 @@ const validateClientForWorkspace = async ({
} }
throw UnauthorizedRequestError({ throw UnauthorizedRequestError({
message: 'Failed client authorization for workspace resource' message: 'Failed client authorization for workspace'
}); });
} }

View File

@@ -5,11 +5,13 @@ import { AccountNotFoundError } from '../utils/errors';
type req = 'params' | 'body' | 'query'; type req = 'params' | 'body' | 'query';
// TODO: transform
const requireBotAuth = ({ const requireBotAuth = ({
acceptedRoles, acceptedRoles,
location = 'params' location = 'params'
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
location?: req; location?: req;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {

View File

@@ -13,7 +13,7 @@ import { IntegrationNotFoundError, UnauthorizedRequestError } from '../utils/err
const requireIntegrationAuth = ({ const requireIntegrationAuth = ({
acceptedRoles acceptedRoles
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
// integration authorization middleware // integration authorization middleware

View File

@@ -19,7 +19,7 @@ const requireIntegrationAuthorizationAuth = ({
attachAccessToken = true, attachAccessToken = true,
location = 'params' location = 'params'
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
attachAccessToken?: boolean; attachAccessToken?: boolean;
location?: req; location?: req;
}) => { }) => {

View File

@@ -1,9 +1,13 @@
import { Types } from 'mongoose';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { UnauthorizedRequestError } from '../utils/errors'; import { UnauthorizedRequestError } from '../utils/errors';
import { import {
Membership, Membership,
} from '../models'; } from '../models';
import { validateMembership } from '../helpers/membership'; import {
validateClientForMembership,
validateMembership
} from '../helpers/membership';
type req = 'params' | 'body' | 'query'; type req = 'params' | 'body' | 'query';
@@ -16,43 +20,25 @@ type req = 'params' | 'body' | 'query';
*/ */
const requireMembershipAuth = ({ const requireMembershipAuth = ({
acceptedRoles, acceptedRoles,
location = 'params' locationMembershipId = 'params'
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
location?: req; locationMembershipId: req
}) => { }) => {
return async ( return async (
req: Request, req: Request,
res: Response, res: Response,
next: NextFunction next: NextFunction
) => { ) => {
try { const { membershipId } = req[locationMembershipId];
const { membershipId } = req[location];
const membership = await Membership.findById(membershipId); req.targetMembership = await validateClientForMembership({
authData: req.authData,
if (!membership) throw new Error('Failed to find target membership'); membershipId: new Types.ObjectId(membershipId),
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,
acceptedRoles acceptedRoles
}); });
req.targetMembership = targetMembership;
return next(); return next();
} catch (err) {
return next(UnauthorizedRequestError({
message: 'Unable to validate workspace membership'
}));
}
} }
} }

View File

@@ -1,11 +1,17 @@
import { Types } from 'mongoose';
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { UnauthorizedRequestError } from '../utils/errors'; import { UnauthorizedRequestError } from '../utils/errors';
import { import {
MembershipOrg MembershipOrg
} from '../models'; } from '../models';
import { validateMembershipOrg } from '../helpers/membershipOrg'; import {
validateClientForMembershipOrg,
validateMembershipOrg
} from '../helpers/membershipOrg';
// TODO: transform
type req = 'params' | 'body' | 'query'; type req = 'params' | 'body' | 'query';
/** /**
@@ -18,32 +24,23 @@ type req = 'params' | 'body' | 'query';
const requireMembershipOrgAuth = ({ const requireMembershipOrgAuth = ({
acceptedRoles, acceptedRoles,
acceptedStatuses, acceptedStatuses,
location = 'params' locationMembershipOrgId = 'params'
}: { }: {
acceptedRoles: Array<'owner' | 'admin' | 'member'>; acceptedRoles: Array<'owner' | 'admin' | 'member'>;
acceptedStatuses: Array<'invited' | 'accepted'>; acceptedStatuses: Array<'invited' | 'accepted'>;
location?: req; locationMembershipOrgId?: req;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { const { membershipId } = req[locationMembershipOrgId];
const { membershipId } = req[location];
const membershipOrg = await MembershipOrg.findById(membershipId); req.membershipOrg = await validateClientForMembershipOrg({
authData: req.authData,
if (!membershipOrg) throw new Error('Failed to find target organization membership'); membershipOrgId: new Types.ObjectId(membershipId),
acceptedRoles,
req.targetMembership = await validateMembershipOrg({ acceptedStatuses
userId: req.user._id, });
organizationId: membershipOrg.organization,
acceptedRoles, return next();
acceptedStatuses
});
return next();
} catch (err) {
return next(UnauthorizedRequestError({
message: 'Unable to validate organization membership'
}));
}
} }
} }

View File

@@ -26,8 +26,6 @@ const requireOrganizationAuth = ({
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
const { organizationId } = req[locationOrganizationId]; const { organizationId } = req[locationOrganizationId];
// TODO: incorporate [acceptedRoles] and [acceptedStatuses]
const { organization, membershipOrg } = await validateClientForOrganization({ const { organization, membershipOrg } = await validateClientForOrganization({
authData: req.authData, authData: req.authData,
organizationId: new Types.ObjectId(organizationId), organizationId: new Types.ObjectId(organizationId),

View File

@@ -17,32 +17,28 @@ import {
const requireSecretAuth = ({ const requireSecretAuth = ({
acceptedRoles acceptedRoles
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { const { secretId } = req.params;
const { secretId } = req.params;
const secret = await Secret.findById(secretId);
const secret = await Secret.findById(secretId);
if (!secret) {
if (!secret) { return next(SecretNotFoundError({
return next(SecretNotFoundError({ message: 'Failed to find secret'
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' }));
} }
await validateMembership({
userId: req.user._id,
workspaceId: secret.workspace,
acceptedRoles
});
req._secret = secret;
next();
} }
} }

View File

@@ -9,7 +9,7 @@ const requireServiceTokenDataAuth = ({
acceptedRoles, acceptedRoles,
location = 'params' location = 'params'
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
location?: req; location?: req;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {

View File

@@ -19,7 +19,7 @@ const requireWorkspaceAuth = ({
locationEnvironment = undefined, locationEnvironment = undefined,
requiredPermissions = [] requiredPermissions = []
}: { }: {
acceptedRoles: string[]; acceptedRoles: Array<'admin' | 'member'>;
locationWorkspaceId: req; locationWorkspaceId: req;
locationEnvironment?: req | undefined; locationEnvironment?: req | undefined;
requiredPermissions?: string[]; requiredPermissions?: string[];
@@ -34,6 +34,7 @@ const requireWorkspaceAuth = ({
authData: req.authData, authData: req.authData,
workspaceId: new Types.ObjectId(workspaceId), workspaceId: new Types.ObjectId(workspaceId),
environment, environment,
acceptedRoles,
requiredPermissions requiredPermissions
}); });

View File

@@ -106,7 +106,8 @@ router.patch( // TODO - rewire dashboard to this route
locationWorkspaceId: 'params' locationWorkspaceId: 'params'
}), }),
requireMembershipAuth({ requireMembershipAuth({
acceptedRoles: [ADMIN] acceptedRoles: [ADMIN],
locationMembershipId: 'params'
}), }),
workspaceController.updateWorkspaceMembership workspaceController.updateWorkspaceMembership
); );
@@ -124,7 +125,8 @@ router.delete( // TODO - rewire dashboard to this route
locationWorkspaceId: 'params' locationWorkspaceId: 'params'
}), }),
requireMembershipAuth({ requireMembershipAuth({
acceptedRoles: [ADMIN] acceptedRoles: [ADMIN],
locationMembershipId: 'params'
}), }),
workspaceController.deleteWorkspaceMembership workspaceController.deleteWorkspaceMembership
); );

View File

@@ -202,4 +202,13 @@ export const ServiceAccountKeyNotFoundError = (error?: Partial<RequestErrorConte
stack: error?.stack stack: error?.stack
}) })
export const BotNotFoundError = (error?: Partial<RequestErrorContext>) => 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]<----- //* ----->[MISC ERRORS]<-----

View File

@@ -1,6 +1,6 @@
--- ---
title: "Quickstart" 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. These examples demonstrate how to store and fetch environment variables from [Infisical Cloud](https://app.infisical.com) into your application.

View File

@@ -42,7 +42,5 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
| [Flask](/integrations/frameworks/flask) | Framework | Available | | [Flask](/integrations/frameworks/flask) | Framework | Available |
| [Laravel](/integrations/frameworks/laravel) | Framework | Available | | [Laravel](/integrations/frameworks/laravel) | Framework | Available |
| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available | | [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available |
| GCP | Cloud | Coming soon | | GCP Secret Manager | Cloud | Coming soon |
| DigitalOcean | Cloud | Coming soon |
| GitHub Actions | CI/CD | Coming soon |
| Jenkins | CI/CD | Coming soon | | Jenkins | CI/CD | Coming soon |