Begin service account middleware

This commit is contained in:
Tuan Dang
2023-03-30 23:55:24 +07:00
parent 54e099f8a8
commit 9c18adf35f
31 changed files with 279 additions and 146 deletions
@@ -79,7 +79,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
const secretId = Buffer.from(serviceAccount._id.toString(), 'hex').toString('base64'); const secretId = Buffer.from(serviceAccount._id.toString(), 'hex').toString('base64');
return res.status(200).send({ return res.status(200).send({
serviceAccountAccessKey: `SA.${secretId}.${secret}`, serviceAccountAccessKey: `sa.${secretId}.${secret}`,
serviceAccount: serviceAccountObj serviceAccount: serviceAccountObj
}); });
} }
@@ -211,7 +211,7 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
const existingPermission = await ServiceAccountWorkspacePermission.findOne({ const existingPermission = await ServiceAccountWorkspacePermission.findOne({
serviceAccount: new Types.ObjectId(serviceAccountId), serviceAccount: new Types.ObjectId(serviceAccountId),
workspaceId: new Types.ObjectId(workspaceId), workspace: new Types.ObjectId(workspaceId),
environment environment
}); });
@@ -508,3 +508,8 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
}); });
}; };
export const getAak = (req: Request, res: Response) => {
return res.status(200).send({
message: 'getAak'
});
}
@@ -30,8 +30,8 @@ const requireSecretSnapshotAuth = ({
} }
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: secretSnapshot.workspace.toString(), workspaceId: secretSnapshot.workspace,
acceptedRoles acceptedRoles
}); });
+8 -4
View File
@@ -15,7 +15,8 @@ router.get(
acceptedAuthModes: ['jwt', 'apiKey'] acceptedAuthModes: ['jwt', 'apiKey']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
query('offset').exists().isInt(), query('offset').exists().isInt(),
@@ -30,7 +31,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -43,7 +45,8 @@ router.post(
acceptedAuthModes: ['jwt', 'apiKey'] acceptedAuthModes: ['jwt', 'apiKey']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('version').exists().isInt(), body('version').exists().isInt(),
@@ -57,7 +60,8 @@ router.get(
acceptedAuthModes: ['jwt', 'apiKey'] acceptedAuthModes: ['jwt', 'apiKey']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
query('offset').exists().isInt(), query('offset').exists().isInt(),
+37
View File
@@ -5,11 +5,13 @@ import {
IUser, IUser,
User, User,
ServiceTokenData, ServiceTokenData,
ServiceAccount,
APIKeyData APIKeyData
} from '../models'; } from '../models';
import { import {
AccountNotFoundError, AccountNotFoundError,
ServiceTokenDataNotFoundError, ServiceTokenDataNotFoundError,
ServiceAccountNotFoundError,
APIKeyDataNotFoundError, APIKeyDataNotFoundError,
UnauthorizedRequestError, UnauthorizedRequestError,
BadRequestError BadRequestError
@@ -63,9 +65,13 @@ const validateAuthMode = ({
case 'st': case 'st':
authTokenType = 'serviceToken'; authTokenType = 'serviceToken';
break; break;
case 'sa':
authTokenType = 'serviceAccount';
break;
default: default:
authTokenType = 'jwt'; authTokenType = 'jwt';
} }
authTokenValue = tokenValue; authTokenValue = tokenValue;
} }
@@ -164,6 +170,36 @@ const getAuthSTDPayload = async ({
} }
/** /**
* Return service account access key payload
* @param {Object} obj
* @param {String} obj.authTokenValue - service account access token value
* @returns {ServiceAccount} serviceAccount
*/
const getAuthSAAKPayload = async ({
authTokenValue
}: {
authTokenValue: string;
}) => {
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
const serviceAccount = await ServiceAccount.findById(
Buffer.from(TOKEN_IDENTIFIER, 'base64').toString('hex')
).select('+secretHash');
if (!serviceAccount) {
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
}
const result = await bcrypt.compare(TOKEN_SECRET, serviceAccount.secretHash);
if (!result) throw UnauthorizedRequestError({
message: 'Failed to authenticate service account access key'
});
return serviceAccount;
}
/**
* TODO: deprecate API keys
* Return API key data payload corresponding to API key [authTokenValue] * Return API key data payload corresponding to API key [authTokenValue]
* @param {Object} obj * @param {Object} obj
* @param {String} obj.authTokenValue - API key value * @param {String} obj.authTokenValue - API key value
@@ -300,6 +336,7 @@ export {
validateAuthMode, validateAuthMode,
getAuthUserPayload, getAuthUserPayload,
getAuthSTDPayload, getAuthSTDPayload,
getAuthSAAKPayload,
getAuthAPIKeyPayload, getAuthAPIKeyPayload,
createToken, createToken,
issueAuthTokens, issueAuthTokens,
+8 -5
View File
@@ -1,4 +1,5 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import { Membership, Key } from '../models'; import { Membership, Key } from '../models';
import { import {
MembershipNotFoundError, MembershipNotFoundError,
@@ -18,9 +19,9 @@ const validateMembership = async ({
workspaceId, workspaceId,
acceptedRoles, acceptedRoles,
}: { }: {
userId: string; userId: Types.ObjectId;
workspaceId: string; workspaceId: Types.ObjectId;
acceptedRoles: string[]; acceptedRoles?: string[];
}) => { }) => {
const membership = await Membership.findOne({ const membership = await Membership.findOne({
@@ -32,8 +33,10 @@ const validateMembership = async ({
throw MembershipNotFoundError({ message: 'Failed to find workspace membership' }); throw MembershipNotFoundError({ message: 'Failed to find workspace membership' });
} }
if (!acceptedRoles.includes(membership.role)) { if (acceptedRoles) {
throw BadRequestError({ message: 'Failed to validate workspace membership role' }); if (!acceptedRoles.includes(membership.role)) {
throw BadRequestError({ message: 'Failed to validate workspace membership role' });
}
} }
return membership; return membership;
+50 -1
View File
@@ -1,4 +1,5 @@
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import { import {
Workspace, Workspace,
Bot, Bot,
@@ -7,6 +8,50 @@ import {
Secret Secret
} from '../models'; } from '../models';
import { createBot } from '../helpers/bot'; import { createBot } from '../helpers/bot';
import { validateMembership } from '../helpers/membership';
/**
* Validate accepted clients by id including [userId], [serviceAccountId],
* and [serviceTokenDataId] for workspace with id [workspaceId] based
* on any known permissions.
* @param {Object} obj
* @param {Types.ObjectId} obj.userId - id of user
*/
const validateClientForWorkspace = async ({
userId,
serviceAccountId,
serviceTokenDataId,
workspaceId,
environment
}: {
userId?: Types.ObjectId;
serviceAccountId?: Types.ObjectId;
serviceTokenDataId?: Types.ObjectId;
workspaceId: Types.ObjectId;
environment?: string;
}) => {
let membership;
if (userId) {
membership = await validateMembership({
userId,
workspaceId
});
}
if (serviceAccountId) {
// TODO
}
if (serviceTokenDataId) {
// TODO
}
return ({
membership
});
}
/** /**
* Create a workspace with name [name] in organization with id [organizationId] * Create a workspace with name [name] in organization with id [organizationId]
@@ -71,4 +116,8 @@ const deleteWorkspace = async ({ id }: { id: string }) => {
} }
}; };
export { createWorkspace, deleteWorkspace }; export {
validateClientForWorkspace,
createWorkspace,
deleteWorkspace
};
+12 -2
View File
@@ -4,7 +4,8 @@ import {
validateAuthMode, validateAuthMode,
getAuthUserPayload, getAuthUserPayload,
getAuthSTDPayload, getAuthSTDPayload,
getAuthAPIKeyPayload getAuthAPIKeyPayload,
getAuthSAAKPayload
} from '../helpers/auth'; } from '../helpers/auth';
import { import {
UnauthorizedRequestError UnauthorizedRequestError
@@ -41,14 +42,22 @@ const requireAuth = ({
acceptedAuthModes acceptedAuthModes
}); });
req.authTokenType = authTokenType;
// attach auth payloads // attach auth payloads
let serviceTokenData: any; let serviceTokenData: any;
switch (authTokenType) { switch (authTokenType) {
case 'serviceAccount':
req.serviceAccount = await getAuthSAAKPayload({
authTokenValue
});
break;
case 'serviceToken': case 'serviceToken':
serviceTokenData = await getAuthSTDPayload({ serviceTokenData = await getAuthSTDPayload({
authTokenValue authTokenValue
}); });
// TODO: bring this into a separate collection
requiredServiceTokenPermissions.forEach((requiredServiceTokenPermission) => { requiredServiceTokenPermissions.forEach((requiredServiceTokenPermission) => {
if (!serviceTokenData.permissions.includes(requiredServiceTokenPermission)) { if (!serviceTokenData.permissions.includes(requiredServiceTokenPermission)) {
return next(UnauthorizedRequestError({ message: 'Failed to authorize service token for endpoint' })); return next(UnauthorizedRequestError({ message: 'Failed to authorize service token for endpoint' }));
@@ -60,6 +69,7 @@ const requireAuth = ({
break; break;
case 'apiKey': case 'apiKey':
// TODO: deprecate API key
req.user = await getAuthAPIKeyPayload({ req.user = await getAuthAPIKeyPayload({
authTokenValue authTokenValue
}); });
@@ -70,7 +80,7 @@ const requireAuth = ({
}); });
break; break;
} }
return next(); return next();
} }
} }
+2 -2
View File
@@ -20,8 +20,8 @@ const requireBotAuth = ({
} }
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: bot.workspace.toString(), workspaceId: bot.workspace,
acceptedRoles acceptedRoles
}); });
@@ -30,8 +30,8 @@ const requireIntegrationAuth = ({
} }
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: integration.workspace.toString(), workspaceId: integration.workspace,
acceptedRoles acceptedRoles
}); });
@@ -38,8 +38,8 @@ const requireIntegrationAuthorizationAuth = ({
} }
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: integrationAuth.workspace._id.toString(), workspaceId: integrationAuth.workspace._id,
acceptedRoles acceptedRoles
}); });
@@ -40,8 +40,8 @@ const requireMembershipAuth = ({
if (!userMembership) throw new Error('Failed to validate own membership') if (!userMembership) throw new Error('Failed to validate own membership')
const targetMembership = await validateMembership({ const targetMembership = await validateMembership({
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: membership.workspace.toString(), workspaceId: membership.workspace,
acceptedRoles acceptedRoles
}); });
+2 -2
View File
@@ -32,8 +32,8 @@ const requireSecretAuth = ({
} }
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: secret.workspace.toString(), workspaceId: secret.workspace,
acceptedRoles acceptedRoles
}); });
@@ -26,8 +26,8 @@ const requireServiceTokenDataAuth = ({
if (req.user) { if (req.user) {
// case: jwt auth // case: jwt auth
await validateMembership({ await validateMembership({
userId: req.user._id.toString(), userId: req.user._id,
workspaceId: serviceTokenData.workspace.toString(), workspaceId: serviceTokenData.workspace,
acceptedRoles acceptedRoles
}); });
} }
+21 -12
View File
@@ -1,5 +1,7 @@
import { Request, Response, NextFunction } from 'express'; import { Request, Response, NextFunction } from 'express';
import { Types } from 'mongoose';
import { validateMembership } from '../helpers/membership'; import { validateMembership } from '../helpers/membership';
import { validateClientForWorkspace } from '../helpers/workspace';
import { UnauthorizedRequestError } from '../utils/errors'; import { UnauthorizedRequestError } from '../utils/errors';
type req = 'params' | 'body' | 'query'; type req = 'params' | 'body' | 'query';
@@ -13,26 +15,33 @@ type req = 'params' | 'body' | 'query';
*/ */
const requireWorkspaceAuth = ({ const requireWorkspaceAuth = ({
acceptedRoles, acceptedRoles,
location = 'params' locationWorkspaceId,
locationEnvironment = undefined
}: { }: {
acceptedRoles: string[]; acceptedRoles: string[];
location?: req; locationWorkspaceId: req;
locationEnvironment?: req | undefined;
}) => { }) => {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
try { try {
const { workspaceId } = req[location]; // TODO: throw errors if workspaceId or environemnt are not present
if (req.user) {
// case: jwt auth
const membership = await validateMembership({
userId: req.user._id.toString(),
workspaceId,
acceptedRoles
});
const workspaceId = req[locationWorkspaceId]?.workspaceId;
const environment = locationEnvironment ? req[locationEnvironment]?.environment : undefined;
// validate clients
const { membership } = await validateClientForWorkspace({
userId: req.user?._id,
serviceAccountId: req.serviceAccount?._id,
serviceTokenDataId: req.serviceTokenData?._id,
workspaceId: new Types.ObjectId(workspaceId),
environment
});
if (membership) {
req.membership = membership; req.membership = membership;
} }
if ( if (
req.serviceTokenData req.serviceTokenData
&& req.serviceTokenData.workspace.toString() !== workspaceId && req.serviceTokenData.workspace.toString() !== workspaceId
+2 -1
View File
@@ -16,7 +16,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim().notEmpty(), param('workspaceId').exists().trim().notEmpty(),
validateRequest, validateRequest,
+8 -8
View File
@@ -38,7 +38,7 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
location: 'body' locationWorkspaceId: 'body'
}), }),
body('workspaceId').exists().trim().notEmpty(), body('workspaceId').exists().trim().notEmpty(),
body('code').exists().trim().notEmpty(), body('code').exists().trim().notEmpty(),
@@ -49,18 +49,18 @@ router.post(
router.post( router.post(
'/access-token', '/access-token',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
location: 'body'
}),
body('workspaceId').exists().trim().notEmpty(), body('workspaceId').exists().trim().notEmpty(),
body('accessId').trim(), body('accessId').trim(),
body('accessToken').exists().trim().notEmpty(), body('accessToken').exists().trim().notEmpty(),
body('integration').exists().trim().notEmpty(), body('integration').exists().trim().notEmpty(),
validateRequest, validateRequest,
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'body'
}),
integrationAuthController.saveIntegrationAccessToken integrationAuthController.saveIntegrationAccessToken
); );
+4 -2
View File
@@ -15,7 +15,8 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('key').exists(), body('key').exists(),
@@ -29,7 +30,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId'), param('workspaceId'),
validateRequest, validateRequest,
+6 -2
View File
@@ -10,13 +10,16 @@ import { body, query, param } from 'express-validator';
import { secretController } from '../../controllers/v1'; import { secretController } from '../../controllers/v1';
import { ADMIN, MEMBER } from '../../variables'; import { ADMIN, MEMBER } from '../../variables';
// note to devs: these endpoints will be deprecated in favor of v2
router.post( router.post(
'/:workspaceId', '/:workspaceId',
requireAuth({ requireAuth({
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
body('secrets').exists(), body('secrets').exists(),
body('keys').exists(), body('keys').exists(),
@@ -33,7 +36,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
query('environment').exists().trim(), query('environment').exists().trim(),
query('channel'), query('channel'),
+1 -1
View File
@@ -25,7 +25,7 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
location: 'body' locationWorkspaceId: 'body'
}), }),
body('name').exists().trim().notEmpty(), body('name').exists().trim().notEmpty(),
body('workspaceId').exists().trim().notEmpty(), body('workspaceId').exists().trim().notEmpty(),
+17 -8
View File
@@ -15,7 +15,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -29,6 +30,7 @@ router.get(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -49,7 +51,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -73,7 +76,8 @@ router.delete(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN] acceptedRoles: [ADMIN],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -86,7 +90,8 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('name').exists().trim().notEmpty(), body('name').exists().trim().notEmpty(),
@@ -100,7 +105,8 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('email').exists().trim().notEmpty(), body('email').exists().trim().notEmpty(),
@@ -114,7 +120,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -127,7 +134,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -140,7 +148,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
+12 -7
View File
@@ -20,7 +20,8 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().isMongoId().trim(), param('workspaceId').exists().isMongoId().trim(),
param('environment').exists().trim(), param('environment').exists().trim(),
@@ -36,7 +37,8 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().isMongoId().trim(), param('workspaceId').exists().isMongoId().trim(),
param('environment').exists().trim(), param('environment').exists().trim(),
@@ -54,7 +56,8 @@ router.get(
acceptedAuthModes: ['jwt', 'serviceToken'] acceptedAuthModes: ['jwt', 'serviceToken']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
query('channel'), query('channel'),
validateRequest, validateRequest,
@@ -82,7 +85,8 @@ router.delete(
param('environmentName').exists().trim(), param('environmentName').exists().trim(),
body('secretIds').exists().isArray().custom(array => array.length > 0), body('secretIds').exists().isArray().custom(array => array.length > 0),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
validateRequest, validateRequest,
secretController.deleteSecrets secretController.deleteSecrets
@@ -110,13 +114,13 @@ router.patch(
param('workspaceId').exists().isMongoId().trim(), param('workspaceId').exists().isMongoId().trim(),
param('environmentName').exists().trim(), param('environmentName').exists().trim(),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
validateRequest, validateRequest,
secretController.updateSecrets secretController.updateSecrets
); );
router.patch( router.patch(
'/workspace/:workspaceId/environment/:environmentName', '/workspace/:workspaceId/environment/:environmentName',
requireAuth({ requireAuth({
@@ -126,7 +130,8 @@ router.patch(
param('workspaceId').exists().isMongoId().trim(), param('workspaceId').exists().isMongoId().trim(),
param('environmentName').exists().trim(), param('environmentName').exists().trim(),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
validateRequest, validateRequest,
secretController.updateSecret secretController.updateSecret
+3 -3
View File
@@ -27,7 +27,7 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
location: 'body' locationWorkspaceId: 'body'
}), }),
body('workspaceId').exists().isString().trim(), body('workspaceId').exists().isString().trim(),
body('environment').exists().isString().trim(), body('environment').exists().isString().trim(),
@@ -105,7 +105,7 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
location: 'body' locationWorkspaceId: 'body'
}), }),
secretsController.createSecrets secretsController.createSecrets
); );
@@ -122,7 +122,7 @@ router.get(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
location: 'query' locationWorkspaceId: 'query'
}), }),
secretsController.getSecrets secretsController.getSecrets
); );
+1 -1
View File
@@ -141,7 +141,7 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
location: 'body' locationWorkspaceId: 'body'
}), }),
serviceAccountsController.addServiceAccountWorkspacePermission serviceAccountsController.addServiceAccountWorkspacePermission
); );
+1 -1
View File
@@ -28,7 +28,7 @@ router.post(
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
location: 'body' locationWorkspaceId: 'body'
}), }),
body('name').exists().isString().trim(), body('name').exists().isString().trim(),
body('workspaceId').exists().isString().trim(), body('workspaceId').exists().isString().trim(),
+27 -5
View File
@@ -16,7 +16,8 @@ router.post(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
body('secrets').exists(), body('secrets').exists(),
body('keys').exists(), body('keys').exists(),
@@ -33,7 +34,8 @@ router.get(
acceptedAuthModes: ['jwt', 'serviceToken'] acceptedAuthModes: ['jwt', 'serviceToken']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
query('environment').exists().trim(), query('environment').exists().trim(),
query('channel'), query('channel'),
@@ -48,7 +50,8 @@ router.get(
acceptedAuthModes: ['jwt', 'apiKey'] acceptedAuthModes: ['jwt', 'apiKey']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -61,7 +64,8 @@ router.get(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
validateRequest, validateRequest,
@@ -79,6 +83,7 @@ router.get( // new - TODO: rewire dashboard to this route
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER], acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
workspaceController.getWorkspaceMemberships workspaceController.getWorkspaceMemberships
); );
@@ -94,6 +99,7 @@ router.patch( // TODO - rewire dashboard to this route
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN], acceptedRoles: [ADMIN],
locationWorkspaceId: 'params'
}), }),
requireMembershipAuth({ requireMembershipAuth({
acceptedRoles: [ADMIN] acceptedRoles: [ADMIN]
@@ -111,6 +117,7 @@ router.delete( // TODO - rewire dashboard to this route
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN], acceptedRoles: [ADMIN],
locationWorkspaceId: 'params'
}), }),
requireMembershipAuth({ requireMembershipAuth({
acceptedRoles: [ADMIN] acceptedRoles: [ADMIN]
@@ -124,7 +131,8 @@ router.patch(
acceptedAuthModes: ['jwt'] acceptedAuthModes: ['jwt']
}), }),
requireWorkspaceAuth({ requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}), }),
param('workspaceId').exists().trim(), param('workspaceId').exists().trim(),
body('autoCapitalization').exists().trim().notEmpty(), body('autoCapitalization').exists().trim().notEmpty(),
@@ -132,4 +140,18 @@ router.patch(
workspaceController.toggleAutoCapitalization workspaceController.toggleAutoCapitalization
); );
router.get(
'/:workspaceId/aak',
requireAuth({
acceptedAuthModes: ['serviceAccount']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER],
locationWorkspaceId: 'params'
}),
param('workspaceId').exists().trim(),
validateRequest,
workspaceController.getAak
);
export default router; export default router;
+1
View File
@@ -24,6 +24,7 @@ declare global {
serviceTokenData: any; serviceTokenData: any;
apiKeyData: any; apiKeyData: any;
query?: any; query?: any;
authTokenType: string;
} }
} }
} }
@@ -1,3 +1,5 @@
import { Workspace } from '../workspace/types';
export type ServiceAccount = { export type ServiceAccount = {
_id: string; _id: string;
name: string; name: string;
@@ -27,7 +29,7 @@ export type RenameServiceAccountDTO = {
export type ServiceAccountWorkspacePermission = { export type ServiceAccountWorkspacePermission = {
_id: string; _id: string;
serviceAccount: string; serviceAccount: string;
workspace: string; workspace: Workspace;
environment: string; environment: string;
canRead: boolean; canRead: boolean;
canWrite: boolean; canWrite: boolean;
@@ -1,20 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import Head from 'next/head';
export default function NewServiceAccountPage() {
console.log('NewServiceAccountPage');
return (
<div>
<Head>
<title>Some title</title>
<link rel="icon" href="/infisical.ico" />
</Head>
<div>
Hello!
</div>
{/* <OrgSettingsPage /> */}
</div>
);
}
// NewServiceAccountPage.requireAuth = true;
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'; import { useState } from 'react';
import { Controller,useForm } from 'react-hook-form'; import { Controller,useForm } from 'react-hook-form';
import { import {
faKey, faKey,
@@ -63,11 +63,10 @@ type Props = {
export const SAProjectLevelPermissionsTable = ({ export const SAProjectLevelPermissionsTable = ({
serviceAccountId serviceAccountId
}: Props) => { }: Props): JSX.Element => {
const { data: serviceAccount } = useGetServiceAccountById(serviceAccountId); const { data: serviceAccount } = useGetServiceAccountById(serviceAccountId);
const { data: userWorkspaces, isLoading: isUserWorkspacesLoading } = useGetUserWorkspaces(); const { data: userWorkspaces, isLoading: isUserWorkspacesLoading } = useGetUserWorkspaces();
const [searchPermissions, setSearchPermissions] = useState(''); const [searchPermissions, setSearchPermissions] = useState('');
const [defaultValues, setDefaultValues] = useState<CreateProjectLevelPermissionForm | undefined>(undefined);
const { data: serviceAccountWorkspacePermissions, isLoading: isPermissionsLoading } = useGetServiceAccountProjectLevelPermissions(serviceAccountId); const { data: serviceAccountWorkspacePermissions, isLoading: isPermissionsLoading } = useGetServiceAccountProjectLevelPermissions(serviceAccountId);
@@ -78,13 +77,15 @@ export const SAProjectLevelPermissionsTable = ({
'addProjectLevelPermission', 'addProjectLevelPermission',
'removeProjectLevelPermission', 'removeProjectLevelPermission',
] as const); ] as const);
const [, setSelectedWorkspace] = useState<undefined | string>(undefined);
const { const {
control, control,
handleSubmit, handleSubmit,
reset, reset,
formState: { isSubmitting } formState: { isSubmitting }
} = useForm<CreateProjectLevelPermissionForm>({ resolver: yupResolver(createProjectLevelPermissionSchema), defaultValues }) } = useForm<CreateProjectLevelPermissionForm>({ resolver: yupResolver(createProjectLevelPermissionSchema) })
const onAddProjectLevelPermission = async ({ const onAddProjectLevelPermission = async ({
privateKey, privateKey,
@@ -144,22 +145,6 @@ export const SAProjectLevelPermissionsTable = ({
handlePopUpClose('removeProjectLevelPermission'); handlePopUpClose('removeProjectLevelPermission');
} }
useEffect(() => {
if (userWorkspaces) {
setDefaultValues({
privateKey: '',
workspace: String(userWorkspaces?.[0]?._id),
environment: String(userWorkspaces?.[0]?.environments?.[0]?.slug),
permissions: {
canRead: true,
canWrite: false,
canUpdate: false,
canDelete: false,
}
});
}
}, [userWorkspaces]);
return ( return (
<div className="w-full bg-white/5 p-6"> <div className="w-full bg-white/5 p-6">
<p className="mb-4 text-xl font-semibold">Project-Level Permissions</p> <p className="mb-4 text-xl font-semibold">Project-Level Permissions</p>
@@ -217,28 +202,28 @@ export const SAProjectLevelPermissionsTable = ({
id="isReadPermissionEnabled" id="isReadPermissionEnabled"
isChecked={canRead} isChecked={canRead}
isDisabled isDisabled
/> >{/**/}</Checkbox>
</Td> </Td>
<Td> <Td>
<Checkbox <Checkbox
id="isWritePermissionEnabled" id="isWritePermissionEnabled"
isChecked={canWrite} isChecked={canWrite}
isDisabled isDisabled
/> >{/**/}</Checkbox>
</Td> </Td>
<Td> <Td>
<Checkbox <Checkbox
id="isUpdatePermissionEnabled" id="isUpdatePermissionEnabled"
isChecked={canUpdate} isChecked={canUpdate}
isDisabled isDisabled
/> >{/**/}</Checkbox>
</Td> </Td>
<Td> <Td>
<Checkbox <Checkbox
id="isDeletePermissionEnabled" id="isDeletePermissionEnabled"
isChecked={canDelete} isChecked={canDelete}
isDisabled isDisabled
/> >{/**/}</Checkbox>
</Td> </Td>
<Td> <Td>
<IconButton <IconButton
@@ -302,7 +287,10 @@ export const SAProjectLevelPermissionsTable = ({
<Select <Select
defaultValue={field.value} defaultValue={field.value}
{...field} {...field}
onValueChange={(e) => onChange(e)} onValueChange={(e) => {
onChange(e);
setSelectedWorkspace(e);
}}
className="w-full border border-mine-shaft-500" className="w-full border border-mine-shaft-500"
> >
{userWorkspaces && userWorkspaces.length > 0 ? ( {userWorkspaces && userWorkspaces.length > 0 ? (
@@ -104,12 +104,12 @@ export const OrgServiceAccountsTable = () => {
const keyPair = generateKeyPair(); const keyPair = generateKeyPair();
setPrivateKey(keyPair.privateKey); setPrivateKey(keyPair.privateKey);
const serviceAccountDetails = await createServiceAccount.mutateAsync({ const serviceAccountDetails = await createServiceAccount.mutateAsync({
name, name,
organizationId: currentOrg?._id, organizationId: currentOrg?._id,
publicKey: keyPair.publicKey, publicKey: keyPair.publicKey,
expiresIn expiresIn: Number(expiresIn)
}); });
setAccessKey(serviceAccountDetails.serviceAccountAccessKey); setAccessKey(serviceAccountDetails.serviceAccountAccessKey);
@@ -152,26 +152,28 @@ export const OrgServiceAccountsTable = () => {
control={control} control={control}
name="expiresIn" name="expiresIn"
defaultValue={String(serviceAccountExpiration?.[0]?.value)} defaultValue={String(serviceAccountExpiration?.[0]?.value)}
render={({ field: { onChange, ...field }, fieldState: { error } }) => ( render={({ field: { onChange, ...field }, fieldState: { error } }) => {
<FormControl return (
label="Expiration" <FormControl
errorText={error?.message} label="Expiration"
isError={Boolean(error)} errorText={error?.message}
> isError={Boolean(error)}
<Select
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
> >
{serviceAccountExpiration.map(({ label, value }) => ( <Select
<SelectItem value={String(value)} key={label}> defaultValue={field.value}
{label} {...field}
</SelectItem> onValueChange={(e) => onChange(e)}
))} className="w-full"
</Select> >
</FormControl> {serviceAccountExpiration.map(({ label, value }) => (
)} <SelectItem value={String(value)} key={label}>
{label}
</SelectItem>
))}
</Select>
</FormControl>
);
}}
/> />
<div className="mt-8 flex items-center"> <div className="mt-8 flex items-center">
<Button <Button