Fix merge conflicts

This commit is contained in:
Tuan Dang
2023-01-04 21:31:58 +07:00
41 changed files with 380 additions and 220 deletions
+2
View File
@@ -40,6 +40,7 @@ import {
secret as v2SecretRouter, secret as v2SecretRouter,
workspace as v2WorkspaceRouter, workspace as v2WorkspaceRouter,
serviceTokenData as v2ServiceTokenDataRouter, serviceTokenData as v2ServiceTokenDataRouter,
apiKeyData as v2APIKeyDataRouter,
} from './routes/v2'; } from './routes/v2';
import { getLogger } from './utils/logger'; import { getLogger } from './utils/logger';
@@ -98,6 +99,7 @@ app.use('/api/v1/integration-auth', v1IntegrationAuthRouter);
app.use('/api/v2/workspace', v2WorkspaceRouter); app.use('/api/v2/workspace', v2WorkspaceRouter);
app.use('/api/v2/secret', v2SecretRouter); app.use('/api/v2/secret', v2SecretRouter);
app.use('/api/v2/service-token-data', v2ServiceTokenDataRouter); app.use('/api/v2/service-token-data', v2ServiceTokenDataRouter);
app.use('/api/v2/api-key-data', v2APIKeyDataRouter);
//* Handle unrouted requests and respond with proper error message as well as status code //* Handle unrouted requests and respond with proper error message as well as status code
app.use((req, res, next)=>{ app.use((req, res, next)=>{
@@ -2,7 +2,6 @@ import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Key } from '../../models'; import { Key } from '../../models';
import { findMembership } from '../../helpers/membership'; import { findMembership } from '../../helpers/membership';
import { GRANTED } from '../../variables';
/** /**
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with * Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
@@ -26,9 +25,6 @@ export const uploadKey = async (req: Request, res: Response) => {
throw new Error('Failed receiver membership validation for workspace'); throw new Error('Failed receiver membership validation for workspace');
} }
receiverMembership.status = GRANTED;
await receiverMembership.save();
await new Key({ await new Key({
encryptedKey: key.encryptedKey, encryptedKey: key.encryptedKey,
nonce: key.nonce, nonce: key.nonce,
@@ -7,7 +7,7 @@ import {
} from '../../helpers/membership'; } from '../../helpers/membership';
import { sendMail } from '../../helpers/nodemailer'; import { sendMail } from '../../helpers/nodemailer';
import { SITE_URL } from '../../config'; import { SITE_URL } from '../../config';
import { ADMIN, MEMBER, GRANTED, ACCEPTED } from '../../variables'; import { ADMIN, MEMBER, ACCEPTED } from '../../variables';
/** /**
* Check that user is a member of workspace with id [workspaceId] * Check that user is a member of workspace with id [workspaceId]
@@ -175,8 +175,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
// already a member of the workspace // already a member of the workspace
const inviteeMembership = await Membership.findOne({ const inviteeMembership = await Membership.findOne({
user: invitee._id, user: invitee._id,
workspace: workspaceId, workspace: workspaceId
status: GRANTED
}); });
if (inviteeMembership) if (inviteeMembership)
@@ -205,8 +204,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
const m = await new Membership({ const m = await new Membership({
user: invitee._id, user: invitee._id,
workspace: workspaceId, workspace: workspaceId,
role: MEMBER, role: MEMBER
status: GRANTED
}).save(); }).save();
await sendMail({ await sendMail({
@@ -15,7 +15,7 @@ import {
deleteWorkspace as deleteWork deleteWorkspace as deleteWork
} from '../../helpers/workspace'; } from '../../helpers/workspace';
import { addMemberships } from '../../helpers/membership'; import { addMemberships } from '../../helpers/membership';
import { ADMIN, COMPLETED, GRANTED } from '../../variables'; import { ADMIN } from '../../variables';
/** /**
* Return public keys of members of workspace with id [workspaceId] * Return public keys of members of workspace with id [workspaceId]
@@ -33,13 +33,12 @@ export const getWorkspacePublicKeys = async (req: Request, res: Response) => {
workspace: workspaceId workspace: workspaceId
}).populate<{ user: IUser }>('user', 'publicKey') }).populate<{ user: IUser }>('user', 'publicKey')
) )
.filter((m) => m.status === COMPLETED || m.status === GRANTED) .map((member) => {
.map((member) => { return {
return { publicKey: member.user.publicKey,
publicKey: member.user.publicKey, userId: member.user._id
userId: member.user._id };
}; });
});
} catch (err) { } catch (err) {
Sentry.setUser({ email: req.user.email }); Sentry.setUser({ email: req.user.email });
Sentry.captureException(err); Sentry.captureException(err);
@@ -169,8 +168,7 @@ export const createWorkspace = async (req: Request, res: Response) => {
await addMemberships({ await addMemberships({
userIds: [req.user._id], userIds: [req.user._id],
workspaceId: workspace._id.toString(), workspaceId: workspace._id.toString(),
roles: [ADMIN], roles: [ADMIN]
statuses: [GRANTED]
}); });
} catch (err) { } catch (err) {
Sentry.setUser({ email: req.user.email }); Sentry.setUser({ email: req.user.email });
@@ -336,30 +334,3 @@ export const getWorkspaceServiceTokens = async (
serviceTokens serviceTokens
}); });
} }
export const getWorkspaceServiceTokenData = async (
req: Request,
res: Response
) => {
let serviceTokenData;
try {
const { workspaceId } = req.query;
serviceTokenData = await ServiceTokenData
.find({
workspace: workspaceId
})
.select('+encryptedKey +iv +tag');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace service token data'
});
}
return res.status(200).send({
serviceTokenData
});
}
@@ -0,0 +1,106 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import {
APIKeyData
} from '../../models';
import {
SALT_ROUNDS
} from '../../config';
/**
* Return API key data for user with id [req.user_id]
* @param req
* @param res
* @returns
*/
export const getAPIKeyData = async (req: Request, res: Response) => {
let apiKeyData;
try {
apiKeyData = await APIKeyData.find({
user: req.user._id
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get API key data'
});
}
return res.status(200).send({
apiKeyData
});
}
/**
* Create new API key data for user with id [req.user._id]
* @param req
* @param res
*/
export const createAPIKeyData = async (req: Request, res: Response) => {
let apiKey, apiKeyData;
try {
const { name, expiresIn } = req.body;
const secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
apiKeyData = await new APIKeyData({
name,
expiresAt,
user: req.user._id,
secretHash
}).save();
// return api key data without sensitive data
apiKeyData = await APIKeyData.findById(apiKeyData._id);
if (!apiKeyData) throw new Error('Failed to find API key data');
apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
} catch (err) {
console.error(err);
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to API key data'
});
}
return res.status(200).send({
apiKey,
apiKeyData
});
}
/**
* Delete API key data with id [apiKeyDataId].
* @param req
* @param res
* @returns
*/
export const deleteAPIKeyData = async (req: Request, res: Response) => {
let apiKeyData;
try {
const { apiKeyDataId } = req.params;
apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete API key data'
});
}
return res.status(200).send({
apiKeyData
});
}
+3 -1
View File
@@ -1,7 +1,9 @@
import * as workspaceController from './workspaceController'; import * as workspaceController from './workspaceController';
import * as serviceTokenDataController from './serviceTokenDataController'; import * as serviceTokenDataController from './serviceTokenDataController';
import * as apiKeyDataController from './apiKeyDataController';
export { export {
workspaceController, workspaceController,
serviceTokenDataController serviceTokenDataController,
apiKeyDataController
} }
@@ -2,6 +2,7 @@ import { Request, Response } from 'express';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { import {
Key, Key,
ServiceTokenData
} from '../../models'; } from '../../models';
import { import {
v2PushSecrets as push, v2PushSecrets as push,
@@ -183,3 +184,29 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
key key
}); });
} }
export const getWorkspaceServiceTokenData = async (
req: Request,
res: Response
) => {
let serviceTokenData;
try {
const { workspaceId } = req.query;
serviceTokenData = await ServiceTokenData
.find({
workspace: workspaceId
})
.select('+encryptedKey +iv +tag');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace service token data'
});
}
return res.status(200).send({
serviceTokenData
});
}
@@ -14,10 +14,8 @@ import {
*/ */
const requireSecretSnapshotAuth = ({ const requireSecretSnapshotAuth = ({
acceptedRoles, acceptedRoles,
acceptedStatuses
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { try {
@@ -34,8 +32,7 @@ const requireSecretSnapshotAuth = ({
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: secretSnapshot.workspace.toString(), workspaceId: secretSnapshot.workspace.toString(),
acceptedRoles, acceptedRoles
acceptedStatuses
}); });
req.secretSnapshot = secretSnapshot as any; req.secretSnapshot = secretSnapshot as any;
+2 -3
View File
@@ -7,7 +7,7 @@ import {
} from '../../../middleware'; } from '../../../middleware';
import { query, param } from 'express-validator'; import { query, param } from 'express-validator';
import { secretController } from '../../controllers/v1'; import { secretController } from '../../controllers/v1';
import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../../variables'; import { ADMIN, MEMBER } from '../../../variables';
router.get( router.get(
'/:secretId/secret-versions', '/:secretId/secret-versions',
@@ -15,8 +15,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireSecretAuth({ requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('secretId').exists().trim(), param('secretId').exists().trim(),
query('offset').exists().isInt(), query('offset').exists().isInt(),
+2 -3
View File
@@ -8,7 +8,7 @@ import {
validateRequest validateRequest
} from '../../../middleware'; } from '../../../middleware';
import { param } from 'express-validator'; import { param } from 'express-validator';
import { ADMIN, MEMBER, GRANTED } from '../../../variables'; import { ADMIN, MEMBER } from '../../../variables';
import { secretSnapshotController } from '../../controllers/v1'; import { secretSnapshotController } from '../../controllers/v1';
router.get( router.get(
@@ -17,8 +17,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireSecretSnapshotAuth({ requireSecretSnapshotAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('secretSnapshotId').exists().trim(), param('secretSnapshotId').exists().trim(),
validateRequest, validateRequest,
+4 -7
View File
@@ -6,7 +6,7 @@ import {
validateRequest validateRequest
} from '../../../middleware'; } from '../../../middleware';
import { param, query } from 'express-validator'; import { param, query } from 'express-validator';
import { ADMIN, MEMBER, GRANTED } from '../../../variables'; import { ADMIN, MEMBER } from '../../../variables';
import { workspaceController } from '../../controllers/v1'; import { workspaceController } from '../../controllers/v1';
router.get( router.get(
@@ -15,8 +15,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
query('offset').exists().isInt(), query('offset').exists().isInt(),
@@ -31,8 +30,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -43,8 +41,7 @@ router.get(
'/:workspaceId/logs', '/:workspaceId/logs',
requireAuth, requireAuth,
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
query('offset').exists().isInt(), query('offset').exists().isInt(),
+60 -9
View File
@@ -3,22 +3,25 @@ import * as Sentry from '@sentry/node';
import bcrypt from 'bcrypt'; import bcrypt from 'bcrypt';
import { import {
User, User,
ServiceTokenData ServiceTokenData,
APIKeyData
} from '../models'; } from '../models';
import { import {
JWT_AUTH_LIFETIME, JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET, JWT_AUTH_SECRET,
JWT_REFRESH_LIFETIME, JWT_REFRESH_LIFETIME,
JWT_REFRESH_SECRET, JWT_REFRESH_SECRET
SALT_ROUNDS
} from '../config'; } from '../config';
import { import {
AccountNotFoundError, AccountNotFoundError,
ServiceTokenDataNotFoundError, ServiceTokenDataNotFoundError,
UnauthorizedRequestError, APIKeyDataNotFoundError,
BadRequestError UnauthorizedRequestError
} from '../utils/errors'; } from '../utils/errors';
// TODO 1: check if API key works
// TODO 2: optimize middleware
/** /**
* Validate that auth token value [authTokenValue] falls under one of * Validate that auth token value [authTokenValue] falls under one of
* accepted auth modes [acceptedAuthModes]. * accepted auth modes [acceptedAuthModes].
@@ -40,6 +43,9 @@ const validateAuthMode = ({
case 'st': case 'st':
authMode = 'serviceToken'; authMode = 'serviceToken';
break; break;
case 'ak':
authMode = 'apiKey';
break;
default: default:
authMode = 'jwt'; authMode = 'jwt';
break; break;
@@ -106,9 +112,11 @@ const getAuthSTDPayload = async ({
// TODO: optimize double query // TODO: optimize double query
serviceTokenData = await ServiceTokenData serviceTokenData = await ServiceTokenData
.findById(TOKEN_IDENTIFIER, 'secretHash expiresAt'); .findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt');
if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) { if (!serviceTokenData) {
throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
// case: service token expired // case: service token expired
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id); await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
throw UnauthorizedRequestError({ throw UnauthorizedRequestError({
@@ -116,8 +124,6 @@ const getAuthSTDPayload = async ({
}); });
} }
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash); const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
if (!isMatch) throw UnauthorizedRequestError({ if (!isMatch) throw UnauthorizedRequestError({
message: 'Failed to authenticate service token' message: 'Failed to authenticate service token'
@@ -136,6 +142,50 @@ const getAuthSTDPayload = async ({
return serviceTokenData; return serviceTokenData;
} }
/**
* Return API key data payload corresponding to API key [authTokenValue]
* @param {Object} obj
* @param {String} obj.authTokenValue - API key value
* @returns {APIKeyData} apiKeyData - API key data
*/
const getAuthAPIKeyPayload = async ({
authTokenValue
}: {
authTokenValue: string;
}) => {
let user;
try {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
const apiKeyData = await APIKeyData
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt')
.populate('user', '+publicKey');
if (!apiKeyData) {
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
} else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) {
// case: API key expired
await APIKeyData.findByIdAndDelete(apiKeyData._id);
throw UnauthorizedRequestError({
message: 'Failed to authenticate expired API key'
});
}
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash);
if (!isMatch) throw UnauthorizedRequestError({
message: 'Failed to authenticate API key'
});
user = apiKeyData.user;
} catch (err) {
throw UnauthorizedRequestError({
message: 'Failed to authenticate API key'
});
}
return user;
}
/** /**
* Return newly issued (JWT) auth and refresh tokens to user with id [userId] * Return newly issued (JWT) auth and refresh tokens to user with id [userId]
* @param {Object} obj * @param {Object} obj
@@ -229,6 +279,7 @@ export {
validateAuthMode, validateAuthMode,
getAuthUserPayload, getAuthUserPayload,
getAuthSTDPayload, getAuthSTDPayload,
getAuthAPIKeyPayload,
createToken, createToken,
issueTokens, issueTokens,
clearTokens clearTokens
+4 -16
View File
@@ -3,7 +3,7 @@ import { Membership, Key } from '../models';
/** /**
* 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]
* and has at least one of the roles in [acceptedRoles] and statuses in [acceptedStatuses] * and has at least one of the roles in [acceptedRoles]
* @param {Object} obj * @param {Object} obj
* @param {String} obj.userId - id of user to validate * @param {String} obj.userId - id of user to validate
* @param {String} obj.workspaceId - id of workspace * @param {String} obj.workspaceId - id of workspace
@@ -12,12 +12,10 @@ const validateMembership = async ({
userId, userId,
workspaceId, workspaceId,
acceptedRoles, acceptedRoles,
acceptedStatuses
}: { }: {
userId: string; userId: string;
workspaceId: string; workspaceId: string;
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
}) => { }) => {
let membership; let membership;
@@ -33,11 +31,6 @@ const validateMembership = async ({
if (!acceptedRoles.includes(membership.role)) { if (!acceptedRoles.includes(membership.role)) {
throw new Error('Failed to validate membership role'); throw new Error('Failed to validate membership role');
} }
if (!acceptedStatuses.includes(membership.status)) {
throw new Error('Failed to validate membership status');
}
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
@@ -72,18 +65,15 @@ const findMembership = async (queryObj: any) => {
* @param {String[]} obj.userIds - id of users. * @param {String[]} obj.userIds - id of users.
* @param {String} obj.workspaceId - id of workspace. * @param {String} obj.workspaceId - id of workspace.
* @param {String[]} obj.roles - roles of users. * @param {String[]} obj.roles - roles of users.
* @param {String[]} obj.statuses - statuses of users.
*/ */
const addMemberships = async ({ const addMemberships = async ({
userIds, userIds,
workspaceId, workspaceId,
roles, roles
statuses
}: { }: {
userIds: string[]; userIds: string[];
workspaceId: string; workspaceId: string;
roles: string[]; roles: string[];
statuses: string[];
}): Promise<void> => { }): Promise<void> => {
try { try {
const operations = userIds.map((userId, idx) => { const operations = userIds.map((userId, idx) => {
@@ -92,14 +82,12 @@ const addMemberships = async ({
filter: { filter: {
user: userId, user: userId,
workspace: workspaceId, workspace: workspaceId,
role: roles[idx], role: roles[idx]
status: statuses[idx]
}, },
update: { update: {
user: userId, user: userId,
workspace: workspaceId, workspace: workspaceId,
role: roles[idx], role: roles[idx]
status: statuses[idx]
}, },
upsert: true upsert: true
} }
+2 -2
View File
@@ -3,7 +3,7 @@ import rateLimit from 'express-rate-limit';
// 300 requests per 15 minutes // 300 requests per 15 minutes
const apiLimiter = rateLimit({ const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, windowMs: 15 * 60 * 1000,
max: 400, max: 450,
standardHeaders: true, standardHeaders: true,
legacyHeaders: false, legacyHeaders: false,
skip: (request) => request.path === '/healthcheck' skip: (request) => request.path === '/healthcheck'
@@ -20,7 +20,7 @@ const signupLimiter = rateLimit({
// 10 requests per hour // 10 requests per hour
const loginLimiter = rateLimit({ const loginLimiter = rateLimit({
windowMs: 60 * 60 * 1000, windowMs: 60 * 60 * 1000,
max: 20, max: 25,
standardHeaders: true, standardHeaders: true,
legacyHeaders: false legacyHeaders: false
}); });
+2 -3
View File
@@ -5,7 +5,7 @@ import { createOrganization } from './organization';
import { addMembershipsOrg } from './membershipOrg'; import { addMembershipsOrg } from './membershipOrg';
import { createWorkspace } from './workspace'; import { createWorkspace } from './workspace';
import { addMemberships } from './membership'; import { addMemberships } from './membership';
import { OWNER, ADMIN, ACCEPTED, GRANTED } from '../variables'; import { OWNER, ADMIN, ACCEPTED } from '../variables';
import { sendMail } from '../helpers/nodemailer'; import { sendMail } from '../helpers/nodemailer';
/** /**
@@ -113,8 +113,7 @@ const initializeDefaultOrg = async ({
await addMemberships({ await addMemberships({
userIds: [user._id.toString()], userIds: [user._id.toString()],
workspaceId: workspace._id.toString(), workspaceId: workspace._id.toString(),
roles: [ADMIN], roles: [ADMIN]
statuses: [GRANTED]
}); });
} catch (err) { } catch (err) {
throw new Error('Failed to initialize default organization and workspace'); throw new Error('Failed to initialize default organization and workspace');
+7 -1
View File
@@ -4,7 +4,8 @@ import { User, ServiceTokenData } from '../models';
import { import {
validateAuthMode, validateAuthMode,
getAuthUserPayload, getAuthUserPayload,
getAuthSTDPayload getAuthSTDPayload,
getAuthAPIKeyPayload
} from '../helpers/auth'; } from '../helpers/auth';
import { BadRequestError } from '../utils/errors'; import { BadRequestError } from '../utils/errors';
@@ -53,6 +54,11 @@ const requireAuth = ({
authTokenValue: AUTH_TOKEN_VALUE authTokenValue: AUTH_TOKEN_VALUE
}); });
break; break;
case 'apiKey':
req.user = await getAuthAPIKeyPayload({
authTokenValue: AUTH_TOKEN_VALUE
});
break;
default: default:
req.user = await getAuthUserPayload({ req.user = await getAuthUserPayload({
authTokenValue: AUTH_TOKEN_VALUE authTokenValue: AUTH_TOKEN_VALUE
+1 -4
View File
@@ -7,11 +7,9 @@ type req = 'params' | 'body' | 'query';
const requireBotAuth = ({ const requireBotAuth = ({
acceptedRoles, acceptedRoles,
acceptedStatuses,
location = 'params' location = 'params'
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
location?: req; location?: req;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
@@ -24,8 +22,7 @@ const requireBotAuth = ({
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: bot.workspace.toString(), workspaceId: bot.workspace.toString(),
acceptedRoles, acceptedRoles
acceptedStatuses
}); });
req.bot = bot; req.bot = bot;
@@ -9,14 +9,11 @@ import { IntegrationNotFoundError, UnauthorizedRequestError } from '../utils/err
* with the integration on request params. * with the integration on request params.
* @param {Object} obj * @param {Object} obj
* @param {String[]} obj.acceptedRoles - accepted workspace roles * @param {String[]} obj.acceptedRoles - accepted workspace roles
* @param {String[]} obj.acceptedStatuses - accepted workspace statuses
*/ */
const requireIntegrationAuth = ({ const requireIntegrationAuth = ({
acceptedRoles, acceptedRoles
acceptedStatuses
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
// integration authorization middleware // integration authorization middleware
@@ -35,8 +32,7 @@ const requireIntegrationAuth = ({
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: integration.workspace.toString(), workspaceId: integration.workspace.toString(),
acceptedRoles, acceptedRoles
acceptedStatuses
}); });
const integrationAuth = await IntegrationAuth.findOne({ const integrationAuth = await IntegrationAuth.findOne({
@@ -10,16 +10,13 @@ import { UnauthorizedRequestError } from '../utils/errors';
* with the integration authorization on request params. * with the integration authorization on request params.
* @param {Object} obj * @param {Object} obj
* @param {String[]} obj.acceptedRoles - accepted workspace roles * @param {String[]} obj.acceptedRoles - accepted workspace roles
* @param {String[]} obj.acceptedStatuses - accepted workspace statuses
* @param {Boolean} obj.attachAccessToken - whether or not to decrypt and attach integration authorization access token onto request * @param {Boolean} obj.attachAccessToken - whether or not to decrypt and attach integration authorization access token onto request
*/ */
const requireIntegrationAuthorizationAuth = ({ const requireIntegrationAuthorizationAuth = ({
acceptedRoles, acceptedRoles,
acceptedStatuses,
attachAccessToken = true attachAccessToken = true
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
attachAccessToken?: boolean; attachAccessToken?: boolean;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
@@ -38,8 +35,7 @@ const requireIntegrationAuthorizationAuth = ({
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: integrationAuth.workspace.toString(), workspaceId: integrationAuth.workspace.toString(),
acceptedRoles, acceptedRoles
acceptedStatuses
}); });
req.integrationAuth = integrationAuth; req.integrationAuth = integrationAuth;
+2 -6
View File
@@ -9,15 +9,12 @@ import {
* Validate if user on request has proper membership to modify secret. * Validate if user on request has proper membership to modify secret.
* @param {Object} obj * @param {Object} obj
* @param {String[]} obj.acceptedRoles - accepted workspace roles * @param {String[]} obj.acceptedRoles - accepted workspace roles
* @param {String[]} obj.acceptedStatuses - accepted workspace statuses
* @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing * @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing
*/ */
const requireSecretAuth = ({ const requireSecretAuth = ({
acceptedRoles, acceptedRoles
acceptedStatuses
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { try {
@@ -34,8 +31,7 @@ const requireSecretAuth = ({
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: secret.workspace.toString(), workspaceId: secret.workspace.toString(),
acceptedRoles, acceptedRoles
acceptedStatuses
}); });
req.secret = secret as any; req.secret = secret as any;
@@ -7,11 +7,9 @@ type req = 'params' | 'body' | 'query';
const requireServiceTokenDataAuth = ({ const requireServiceTokenDataAuth = ({
acceptedRoles, acceptedRoles,
acceptedStatuses,
location = 'params' location = 'params'
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
location?: req; location?: req;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
@@ -30,8 +28,7 @@ const requireServiceTokenDataAuth = ({
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId: serviceTokenData.workspace.toString(), workspaceId: serviceTokenData.workspace.toString(),
acceptedRoles, acceptedRoles
acceptedStatuses
}); });
} }
@@ -9,16 +9,13 @@ type req = 'params' | 'body' | 'query';
* on request params. * on request params.
* @param {Object} obj * @param {Object} obj
* @param {String[]} obj.acceptedRoles - accepted workspace roles for JWT auth * @param {String[]} obj.acceptedRoles - accepted workspace roles for JWT auth
* @param {String[]} obj.acceptedStatuses - accepted workspace statuses for JWT auth
* @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing * @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing
*/ */
const requireWorkspaceAuth = ({ const requireWorkspaceAuth = ({
acceptedRoles, acceptedRoles,
acceptedStatuses,
location = 'params' location = 'params'
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
acceptedStatuses: string[];
location?: req; location?: req;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
@@ -30,8 +27,7 @@ const requireWorkspaceAuth = ({
const membership = await validateMembership({ const membership = await validateMembership({
userId: req.user._id.toString(), userId: req.user._id.toString(),
workspaceId, workspaceId,
acceptedRoles, acceptedRoles
acceptedStatuses
}); });
req.membership = membership; req.membership = membership;
+37
View File
@@ -0,0 +1,37 @@
import { Schema, model, Types } from 'mongoose';
export interface IAPIKeyData {
name: string;
user: Types.ObjectId;
expiresAt: Date;
secretHash: string;
}
const apiKeyDataSchema = new Schema<IAPIKeyData>(
{
name: {
type: String,
required: true
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
expiresAt: {
type: Date
},
secretHash: {
type: String,
required: true,
select: false
}
},
{
timestamps: true
}
);
const APIKeyData = model<IAPIKeyData>('APIKeyData', apiKeyDataSchema);
export default APIKeyData;
+5 -2
View File
@@ -14,7 +14,8 @@ import Token, { IToken } from './token';
import User, { IUser } from './user'; import User, { IUser } from './user';
import UserAction, { IUserAction } from './userAction'; import UserAction, { IUserAction } from './userAction';
import Workspace, { IWorkspace } from './workspace'; import Workspace, { IWorkspace } from './workspace';
import ServiceTokenData, { IServiceTokenData } from './serviceTokenData '; import ServiceTokenData, { IServiceTokenData } from './serviceTokenData';
import APIKeyData, { IAPIKeyData } from './apiKeyData';
export { export {
BackupPrivateKey, BackupPrivateKey,
@@ -50,5 +51,7 @@ export {
Workspace, Workspace,
IWorkspace, IWorkspace,
ServiceTokenData, ServiceTokenData,
IServiceTokenData IServiceTokenData,
APIKeyData,
IAPIKeyData
}; };
+1 -8
View File
@@ -1,5 +1,5 @@
import { Schema, model, Types } from 'mongoose'; import { Schema, model, Types } from 'mongoose';
import { ADMIN, MEMBER, INVITED, COMPLETED, GRANTED } from '../variables'; import { ADMIN, MEMBER } from '../variables';
export interface IMembership { export interface IMembership {
_id: Types.ObjectId; _id: Types.ObjectId;
@@ -7,7 +7,6 @@ export interface IMembership {
inviteEmail?: string; inviteEmail?: string;
workspace: Types.ObjectId; workspace: Types.ObjectId;
role: 'admin' | 'member'; role: 'admin' | 'member';
status: 'invited' | 'completed' | 'granted';
} }
const membershipSchema = new Schema( const membershipSchema = new Schema(
@@ -28,12 +27,6 @@ const membershipSchema = new Schema(
type: String, type: String,
enum: [ADMIN, MEMBER], enum: [ADMIN, MEMBER],
required: true required: true
},
status: {
// INVITED, COMPLETED, GRANTED
type: String,
enum: [INVITED, COMPLETED, GRANTED],
required: true
} }
}, },
{ {
@@ -1,5 +1,4 @@
import { Schema, model, Types } from 'mongoose'; import { Schema, model, Types } from 'mongoose';
import { ENV_DEV, ENV_TESTING, ENV_STAGING, ENV_PROD } from '../variables';
export interface IServiceTokenData { export interface IServiceTokenData {
name: string; name: string;
@@ -38,7 +37,6 @@ const serviceTokenDataSchema = new Schema<IServiceTokenData>(
}, },
secretHash: { secretHash: {
type: String, type: String,
unique: true,
required: true, required: true,
select: false select: false
}, },
+3 -5
View File
@@ -8,7 +8,7 @@ import {
validateRequest validateRequest
} from '../../middleware'; } from '../../middleware';
import { botController } from '../../controllers/v1'; import { botController } from '../../controllers/v1';
import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
router.get( router.get(
'/:workspaceId', '/:workspaceId',
@@ -16,8 +16,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId').exists().trim().notEmpty(), param('workspaceId').exists().trim().notEmpty(),
validateRequest, validateRequest,
@@ -30,8 +29,7 @@ router.patch(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireBotAuth({ requireBotAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
body('isActive').isBoolean(), body('isActive').isBoolean(),
body('botKey'), body('botKey'),
+3 -5
View File
@@ -5,7 +5,7 @@ import {
requireIntegrationAuth, requireIntegrationAuth,
validateRequest validateRequest
} from '../../middleware'; } from '../../middleware';
import { ADMIN, MEMBER, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
import { body, param } from 'express-validator'; import { body, param } from 'express-validator';
import { integrationController } from '../../controllers/v1'; import { integrationController } from '../../controllers/v1';
@@ -15,8 +15,7 @@ router.patch(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireIntegrationAuth({ requireIntegrationAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('integrationId').exists().trim(), param('integrationId').exists().trim(),
body('app').exists().trim(), body('app').exists().trim(),
@@ -35,8 +34,7 @@ router.delete(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireIntegrationAuth({ requireIntegrationAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('integrationId').exists().trim(), param('integrationId').exists().trim(),
validateRequest, validateRequest,
+2 -5
View File
@@ -7,7 +7,7 @@ import {
requireIntegrationAuthorizationAuth, requireIntegrationAuthorizationAuth,
validateRequest validateRequest
} from '../../middleware'; } from '../../middleware';
import { ADMIN, MEMBER, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
import { integrationAuthController } from '../../controllers/v1'; import { integrationAuthController } from '../../controllers/v1';
router.get( router.get(
@@ -25,7 +25,6 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [GRANTED],
location: 'body' location: 'body'
}), }),
body('workspaceId').exists().trim().notEmpty(), body('workspaceId').exists().trim().notEmpty(),
@@ -41,8 +40,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireIntegrationAuthorizationAuth({ requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('integrationAuthId'), param('integrationAuthId'),
validateRequest, validateRequest,
@@ -56,7 +54,6 @@ router.delete(
}), }),
requireIntegrationAuthorizationAuth({ requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [GRANTED],
attachAccessToken: false attachAccessToken: false
}), }),
param('integrationAuthId'), param('integrationAuthId'),
+3 -5
View File
@@ -6,7 +6,7 @@ import {
validateRequest validateRequest
} from '../../middleware'; } from '../../middleware';
import { body, param } from 'express-validator'; import { body, param } from 'express-validator';
import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
import { keyController } from '../../controllers/v1'; import { keyController } from '../../controllers/v1';
router.post( router.post(
@@ -15,8 +15,7 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('key').exists(), body('key').exists(),
@@ -30,8 +29,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId'), param('workspaceId'),
validateRequest, validateRequest,
+3 -5
View File
@@ -8,7 +8,7 @@ import {
} from '../../middleware'; } from '../../middleware';
import { body, query, param } from 'express-validator'; import { body, query, param } from 'express-validator';
import { secretController } from '../../controllers/v1'; import { secretController } from '../../controllers/v1';
import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
router.post( router.post(
'/:workspaceId', '/:workspaceId',
@@ -16,8 +16,7 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
body('secrets').exists(), body('secrets').exists(),
body('keys').exists(), body('keys').exists(),
@@ -34,8 +33,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
query('environment').exists().trim(), query('environment').exists().trim(),
query('channel'), query('channel'),
+1 -2
View File
@@ -7,7 +7,7 @@ import {
validateRequest validateRequest
} from '../../middleware'; } from '../../middleware';
import { body } from 'express-validator'; import { body } from 'express-validator';
import { ADMIN, MEMBER, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
import { serviceTokenController } from '../../controllers/v1'; import { serviceTokenController } from '../../controllers/v1';
// note: deprecate service-token routes in favor of service-token data routes/structure // note: deprecate service-token routes in favor of service-token data routes/structure
@@ -25,7 +25,6 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [GRANTED],
location: 'body' location: 'body'
}), }),
body('name').exists().trim().notEmpty(), body('name').exists().trim().notEmpty(),
+9 -32
View File
@@ -6,7 +6,7 @@ import {
requireWorkspaceAuth, requireWorkspaceAuth,
validateRequest validateRequest
} from '../../middleware'; } from '../../middleware';
import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
import { workspaceController, membershipController } from '../../controllers/v1'; import { workspaceController, membershipController } from '../../controllers/v1';
router.get( router.get(
@@ -15,8 +15,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -30,7 +29,6 @@ router.get(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -51,8 +49,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -76,8 +73,7 @@ router.delete(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN], acceptedRoles: [ADMIN]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -90,8 +86,7 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('name').exists().trim().notEmpty(), body('name').exists().trim().notEmpty(),
@@ -105,8 +100,7 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('email').exists().trim().notEmpty(), body('email').exists().trim().notEmpty(),
@@ -120,8 +114,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -134,8 +127,7 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -148,26 +140,11 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
workspaceController.getWorkspaceServiceTokens workspaceController.getWorkspaceServiceTokens
); );
router.get(
'/:workspaceId/service-token-data',
requireAuth({
acceptedAuthModes: ['jwt']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [GRANTED]
}),
param('workspaceId').exists().trim(),
validateRequest,
workspaceController.getWorkspaceServiceTokenData
);
export default router; export default router;
+39
View File
@@ -0,0 +1,39 @@
import express from 'express';
const router = express.Router();
import {
requireAuth,
validateRequest
} from '../../middleware';
import { param, body } from 'express-validator';
import { apiKeyDataController } from '../../controllers/v2';
router.get(
'/',
requireAuth({
acceptedAuthModes: ['jwt']
}),
apiKeyDataController.getAPIKeyData
);
router.post(
'/',
requireAuth({
acceptedAuthModes: ['jwt']
}),
body('name').exists().trim(),
body('expiresIn'), // measured in ms
validateRequest,
apiKeyDataController.createAPIKeyData
);
router.delete(
'/:apiKeyDataId',
requireAuth({
acceptedAuthModes: ['jwt']
}),
param('apiKeyDataId').exists().trim(),
validateRequest,
apiKeyDataController.deleteAPIKeyData
);
export default router;
+3 -1
View File
@@ -1,9 +1,11 @@
import secret from './secret'; import secret from './secret';
import workspace from './workspace'; import workspace from './workspace';
import serviceTokenData from './serviceTokenData'; import serviceTokenData from './serviceTokenData';
import apiKeyData from './apiKeyData';
export { export {
secret, secret,
workspace, workspace,
serviceTokenData serviceTokenData,
apiKeyData
} }
+1 -5
View File
@@ -10,8 +10,6 @@ import { param, body } from 'express-validator';
import { import {
ADMIN, ADMIN,
MEMBER, MEMBER,
COMPLETED,
GRANTED
} from '../../variables'; } from '../../variables';
import { serviceTokenDataController } from '../../controllers/v2'; import { serviceTokenDataController } from '../../controllers/v2';
@@ -30,7 +28,6 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [COMPLETED, GRANTED],
location: 'body' location: 'body'
}), }),
body('name').exists().trim(), body('name').exists().trim(),
@@ -50,8 +47,7 @@ router.delete(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireServiceTokenDataAuth({ requireServiceTokenDataAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED],
}), }),
param('serviceTokenDataId').exists().trim(), param('serviceTokenDataId').exists().trim(),
validateRequest, validateRequest,
+17 -7
View File
@@ -6,7 +6,7 @@ import {
requireWorkspaceAuth, requireWorkspaceAuth,
validateRequest validateRequest
} from '../../middleware'; } from '../../middleware';
import { ADMIN, MEMBER, COMPLETED, GRANTED } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
import { workspaceController } from '../../controllers/v2'; import { workspaceController } from '../../controllers/v2';
router.post( router.post(
@@ -15,8 +15,7 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
body('secrets').exists(), body('secrets').exists(),
body('keys').exists(), body('keys').exists(),
@@ -33,8 +32,7 @@ router.get(
acceptedAuthModes: ['jwt', 'serviceToken'] acceptedAuthModes: ['jwt', 'serviceToken']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
query('environment').exists().trim(), query('environment').exists().trim(),
query('channel'), query('channel'),
@@ -49,12 +47,24 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER]
acceptedStatuses: [COMPLETED, GRANTED]
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
workspaceController.getWorkspaceKey workspaceController.getWorkspaceKey
); );
router.get(
'/:workspaceId/service-token-data',
requireAuth({
acceptedAuthModes: ['jwt']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('workspaceId').exists().trim(),
validateRequest,
workspaceController.getWorkspaceServiceTokenData
);
export default router; export default router;
+1
View File
@@ -17,6 +17,7 @@ declare global {
serviceToken: any; serviceToken: any;
accessToken: any; accessToken: any;
serviceTokenData: any; serviceTokenData: any;
apiKeyData: any;
query?: any; query?: any;
} }
} }
+10
View File
@@ -153,4 +153,14 @@ export const ServiceTokenDataNotFoundError = (error?: Partial<RequestErrorContex
stack: error?.stack stack: error?.stack
}) })
//* ----->[API KEY DATA ERRORS]<-----
export const APIKeyDataNotFoundError = (error?: Partial<RequestErrorContext>) => new RequestError({
logLevel: error?.logLevel ?? LogLevel.ERROR,
statusCode: error?.statusCode ?? 404,
type: error?.type ?? 'service_token_data_not_found_error',
message: error?.message ?? 'The requested service token data was not found',
context: error?.context,
stack: error?.stack
})
//* ----->[MISC ERRORS]<----- //* ----->[MISC ERRORS]<-----
-4
View File
@@ -28,8 +28,6 @@ import {
MEMBER, MEMBER,
INVITED, INVITED,
ACCEPTED, ACCEPTED,
COMPLETED,
GRANTED
} from './organization'; } from './organization';
import { SECRET_SHARED, SECRET_PERSONAL } from './secret'; import { SECRET_SHARED, SECRET_PERSONAL } from './secret';
import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event'; import { EVENT_PUSH_SECRETS, EVENT_PULL_SECRETS } from './event';
@@ -48,8 +46,6 @@ export {
MEMBER, MEMBER,
INVITED, INVITED,
ACCEPTED, ACCEPTED,
COMPLETED,
GRANTED,
SECRET_SHARED, SECRET_SHARED,
SECRET_PERSONAL, SECRET_PERSONAL,
ENV_DEV, ENV_DEV,
+1 -7
View File
@@ -9,16 +9,10 @@ const INVITED = 'invited';
// -- organization // -- organization
const ACCEPTED = 'accepted'; const ACCEPTED = 'accepted';
// -- workspace
const COMPLETED = 'completed';
const GRANTED = 'granted';
export { export {
OWNER, OWNER,
ADMIN, ADMIN,
MEMBER, MEMBER,
INVITED, INVITED,
ACCEPTED, ACCEPTED
COMPLETED,
GRANTED
} }