Clean up and weave crypto into service account permissions

This commit is contained in:
Tuan Dang
2023-03-29 18:07:15 +07:00
parent 729aacc154
commit 88c0a46de3
20 changed files with 360 additions and 240 deletions
@@ -5,8 +5,8 @@ import bcrypt from 'bcrypt';
import {
ServiceAccount,
ServiceAccountKey,
ServiceAccountOrganizationPermissions,
ServiceAccountWorkspacePermissions
ServiceAccountOrganizationPermission,
ServiceAccountWorkspacePermission
} from '../../models';
import {
CreateServiceAccountDto
@@ -71,8 +71,8 @@ export const createServiceAccount = async (req: Request, res: Response) => {
delete serviceAccountObj.secretHash;
// provision default org-level permissions for service account
const permissions = await new ServiceAccountOrganizationPermissions({
// provision default org-level permission for service account
await new ServiceAccountOrganizationPermission({
serviceAccount: serviceAccount._id
}).save();
@@ -136,57 +136,57 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
return serviceAccountKey;
}
/**
* Return organization-level permissions for service account with id [serviceAccountId]
* @param req
* @param res
*/
export const getServiceAccountOrganizationPermissions = async (req: Request, res: Response) => {
const { serviceAccountId } = req.params;
// /**
// * Return organization-level permissions for service account with id [serviceAccountId]
// * @param req
// * @param res
// */
// export const getServiceAccountOrganizationPermissions = async (req: Request, res: Response) => {
// const { serviceAccountId } = req.params;
const permissions = await ServiceAccountOrganizationPermissions.findOne({
serviceAccount: new Types.ObjectId(serviceAccountId),
});
// const permissions = await ServiceAccountOrganizationPermissions.findOne({
// serviceAccount: new Types.ObjectId(serviceAccountId),
// });
return res.status(200).send({
permissions
});
}
// return res.status(200).send({
// permissions
// });
// }
/**
* Return workspace-level permissions for service account with id [serviceAccountId]
* Return workspace-level permission for service account with id [serviceAccountId]
* @param req
* @param res
*/
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
const permissions = await ServiceAccountWorkspacePermissions.find({
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermission.find({
serviceAccount: req.serviceAccount._id
}).populate('workspace');
return res.status(200).send({
permissions
serviceAccountWorkspacePermissions
});
}
/**
* Add organization permissions to service account with id [serviceAccountId]
* @param req
* @param res
*/
export const addServiceAccountOrganizationPermission = async (req: Request, res: Response) => {
const permissions = ServiceAccountOrganizationPermissions.findOne({
serviceAccount: req.serviceAccount._id
});
// /**
// * Add organization permissions to service account with id [serviceAccountId]
// * @param req
// * @param res
// */
// export const addServiceAccountOrganizationPermission = async (req: Request, res: Response) => {
// const permissions = ServiceAccountOrganizationPermissions.findOne({
// serviceAccount: req.serviceAccount._id
// });
// TODO
// // TODO
return res.status(200).send({
permissions
});
}
// return res.status(200).send({
// permissions
// });
// }
/**
* Add a workspace permissions to service account with id [serviceAccountId]
* Add a workspace permission to service account with id [serviceAccountId]
* @param req
* @param res
*/
@@ -198,7 +198,9 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
canRead = false,
canWrite = false,
canUpdate = false,
canDelete = false
canDelete = false,
encryptedKey,
nonce
} = req.body;
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
@@ -207,7 +209,7 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
});
}
const existingPermission = await ServiceAccountWorkspacePermissions.findOne({
const existingPermission = await ServiceAccountWorkspacePermission.findOne({
serviceAccount: new Types.ObjectId(serviceAccountId),
workspaceId: new Types.ObjectId(workspaceId),
environment
@@ -215,7 +217,7 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
if (existingPermission) throw BadRequestError({ message: 'Failed to add workspace permission to service account due to already-existing ' });
const permissions = await new ServiceAccountWorkspacePermissions({
const serviceAccountWorkspacePermission = await new ServiceAccountWorkspacePermission({
serviceAccount: new Types.ObjectId(serviceAccountId),
workspace: new Types.ObjectId(workspaceId),
environment,
@@ -225,23 +227,52 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
canDelete
}).save();
const existingServiceAccountKey = await ServiceAccountKey.findOne({
serviceAccount: new Types.ObjectId(serviceAccountId),
workspace: new Types.ObjectId(workspaceId)
});
if (!existingServiceAccountKey) {
await new ServiceAccountKey({
encryptedKey,
nonce,
sender: req.user._id,
serviceAccount: new Types.ObjectId(serviceAccountId),
workspace: new Types.ObjectId(workspaceId)
}).save();
}
return res.status(200).send({
permissions
serviceAccountWorkspacePermission
});
}
/**
* Delete workspace permissions from service account with id [serviceAccountId]
* Delete workspace permission from service account with id [serviceAccountId]
* @param req
* @param res
*/
export const deleteServiceAccountWorkspacePermission = async (req: Request, res: Response) => {
const { serviceAccountWorkspacePermissionsId } = req.params;
const permissions = await ServiceAccountWorkspacePermissions.findByIdAndDelete(serviceAccountWorkspacePermissionsId);
const { serviceAccountWorkspacePermissionId } = req.params;
const serviceAccountWorkspacePermission = await ServiceAccountWorkspacePermission.findByIdAndDelete(serviceAccountWorkspacePermissionId);
if (serviceAccountWorkspacePermission) {
const { serviceAccount, workspace } = serviceAccountWorkspacePermission;
const count = await ServiceAccountWorkspacePermission.countDocuments({
serviceAccount,
workspace
});
if (count === 0) {
await ServiceAccountKey.findOneAndDelete({
serviceAccount,
workspace
});
}
}
return res.status(200).send({
permissions
serviceAccountWorkspacePermission
});
}
@@ -257,17 +288,15 @@ export const deleteServiceAccount = async (req: Request, res: Response) => {
const serviceAccount = await ServiceAccount.findByIdAndDelete(serviceAccountId);
if (serviceAccount) {
// case: service account with id [serviceAccountId] was deleted
await ServiceAccountKey.deleteMany({
serviceAccount: serviceAccount._id
});
await ServiceAccountOrganizationPermissions.deleteMany({
await ServiceAccountOrganizationPermission.deleteMany({
serviceAccount: new Types.ObjectId(serviceAccountId)
});
await ServiceAccountWorkspacePermissions.deleteMany({
await ServiceAccountWorkspacePermission.deleteMany({
serviceAccount: new Types.ObjectId(serviceAccountId)
});
}
+2 -2
View File
@@ -11,7 +11,7 @@ import requireIntegrationAuthorizationAuth from './requireIntegrationAuthorizati
import requireServiceTokenAuth from './requireServiceTokenAuth';
import requireServiceTokenDataAuth from './requireServiceTokenDataAuth';
import requireServiceAccountAuth from './requireServiceAccountAuth';
import requireServiceAccountWorkspacePermissionsAuth from './requireServiceAccountWorkspacePermissionsAuth';
import requireServiceAccountWorkspacePermissionAuth from './requireServiceAccountWorkspacePermissionAuth';
import requireSecretAuth from './requireSecretAuth';
import requireSecretsAuth from './requireSecretsAuth';
import validateRequest from './validateRequest';
@@ -30,7 +30,7 @@ export {
requireServiceTokenAuth,
requireServiceTokenDataAuth,
requireServiceAccountAuth,
requireServiceAccountWorkspacePermissionsAuth,
requireServiceAccountWorkspacePermissionAuth,
requireSecretAuth,
requireSecretsAuth,
validateRequest
@@ -1,5 +1,5 @@
import { Request, Response, NextFunction } from 'express';
import { ServiceAccount, ServiceAccountWorkspacePermissions } from '../models';
import { ServiceAccount, ServiceAccountWorkspacePermission } from '../models';
import {
ServiceAccountNotFoundError
} from '../utils/errors';
@@ -9,7 +9,7 @@ import {
type req = 'params' | 'body' | 'query';
const requireServiceAccountWorkspacePermissionsAuth = ({
const requireServiceAccountWorkspacePermissionAuth = ({
acceptedRoles,
acceptedStatuses,
location = 'params'
@@ -19,14 +19,14 @@ const requireServiceAccountWorkspacePermissionsAuth = ({
location?: req;
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
const serviceAccountWorkspacePermissionsId = req[location].serviceAccountWorkspacePermissionsId;
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermissions.findById(serviceAccountWorkspacePermissionsId);
const serviceAccountWorkspacePermissionId = req[location].serviceAccountWorkspacePermissionId;
const serviceAccountWorkspacePermission = await ServiceAccountWorkspacePermission.findById(serviceAccountWorkspacePermissionId);
if (!serviceAccountWorkspacePermissions) {
if (!serviceAccountWorkspacePermission) {
return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account workspace permission' }));
}
const serviceAccount = await ServiceAccount.findById(serviceAccountWorkspacePermissions.serviceAccount);
const serviceAccount = await ServiceAccount.findById(serviceAccountWorkspacePermission.serviceAccount);
if (!serviceAccount) {
return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account' }));
@@ -49,4 +49,4 @@ const requireServiceAccountWorkspacePermissionsAuth = ({
}
}
export default requireServiceAccountWorkspacePermissionsAuth;
export default requireServiceAccountWorkspacePermissionAuth;
+6 -6
View File
@@ -12,8 +12,8 @@ import Secret, { ISecret } from './secret';
import ServiceToken, { IServiceToken } from './serviceToken';
import ServiceAccount, { IServiceAccount } from './serviceAccount'; // new
import ServiceAccountKey, { IServiceAccountKey } from './serviceAccountKey'; // new
import ServiceAccountOrganizationPermissions, { IServiceAccountOrganizationPermissions } from './serviceAccountOrganizationPermission'; // new
import ServiceAccountWorkspacePermissions, { IServiceAccountWorkspacePermissions } from './serviceAccountWorkspacePermissions'; // new
import ServiceAccountOrganizationPermission, { IServiceAccountOrganizationPermission } from './serviceAccountOrganizationPermission'; // new
import ServiceAccountWorkspacePermission, { IServiceAccountWorkspacePermission } from './serviceAccountWorkspacePermission'; // new
import TokenData, { ITokenData } from './tokenData';
import User, { IUser } from './user';
import UserAction, { IUserAction } from './userAction';
@@ -51,10 +51,10 @@ export {
IServiceAccount,
ServiceAccountKey,
IServiceAccountKey,
ServiceAccountOrganizationPermissions,
IServiceAccountOrganizationPermissions,
ServiceAccountWorkspacePermissions,
IServiceAccountWorkspacePermissions,
ServiceAccountOrganizationPermission,
IServiceAccountOrganizationPermission,
ServiceAccountWorkspacePermission,
IServiceAccountWorkspacePermission,
TokenData,
ITokenData,
User,
@@ -1,21 +1,16 @@
import { Schema, model, Types, Document } from 'mongoose';
export interface IServiceAccountOrganizationPermissions extends Document {
export interface IServiceAccountOrganizationPermission extends Document {
_id: Types.ObjectId;
serviceAccount: Types.ObjectId;
canFoo: boolean;
}
const serviceAccountOrganizationPermissionsSchema = new Schema<IServiceAccountOrganizationPermissions>(
const serviceAccountOrganizationPermissionSchema = new Schema<IServiceAccountOrganizationPermission>(
{
serviceAccount: {
type: Schema.Types.ObjectId,
ref: 'ServiceAccount',
required: true
},
canFoo: {
type: Boolean,
default: false
}
},
{
@@ -23,6 +18,6 @@ const serviceAccountOrganizationPermissionsSchema = new Schema<IServiceAccountOr
}
);
const ServiceAccountOrganizationPermissions = model<IServiceAccountOrganizationPermissions>('ServiceAccountOrganizationPermissions', serviceAccountOrganizationPermissionsSchema);
const ServiceAccountOrganizationPermission = model<IServiceAccountOrganizationPermission>('ServiceAccountOrganizationPermission', serviceAccountOrganizationPermissionSchema);
export default ServiceAccountOrganizationPermissions;
export default ServiceAccountOrganizationPermission;
@@ -1,6 +1,6 @@
import { Schema, model, Types, Document } from 'mongoose';
export interface IServiceAccountWorkspacePermissions extends Document {
export interface IServiceAccountWorkspacePermission extends Document {
_id: Types.ObjectId;
serviceAccount: Types.ObjectId;
workspace: Types.ObjectId;
@@ -11,7 +11,7 @@ export interface IServiceAccountWorkspacePermissions extends Document {
canDelete: boolean;
}
const serviceAccountWorkspacePermissions = new Schema<IServiceAccountWorkspacePermissions>(
const serviceAccountWorkspacePermissionSchema = new Schema<IServiceAccountWorkspacePermission>(
{
serviceAccount: {
type: Schema.Types.ObjectId,
@@ -49,6 +49,6 @@ const serviceAccountWorkspacePermissions = new Schema<IServiceAccountWorkspacePe
}
);
const ServiceAccountWorkspacePermissions = model<IServiceAccountWorkspacePermissions>('ServiceAccountWorkspacePermissions', serviceAccountWorkspacePermissions);
const ServiceAccountWorkspacePermission = model<IServiceAccountWorkspacePermission>('ServiceAccountWorkspacePermission', serviceAccountWorkspacePermissionSchema);
export default ServiceAccountWorkspacePermissions;
export default ServiceAccountWorkspacePermission;
+34 -32
View File
@@ -5,7 +5,7 @@ import {
requireOrganizationAuth,
requireWorkspaceAuth,
requireServiceAccountAuth,
requireServiceAccountWorkspacePermissionsAuth,
requireServiceAccountWorkspacePermissionAuth,
validateRequest
} from '../../middleware';
import { param, query, body } from 'express-validator';
@@ -76,21 +76,21 @@ router.delete(
serviceAccountsController.deleteServiceAccount
);
router.get(
'/:serviceAccountId/permissions/organization',
param('serviceAccountId').exists().isString().trim(),
query('offset').exists(),
query('limit').exists(),
validateRequest,
requireAuth({
acceptedAuthModes: ['jwt']
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED]
}),
serviceAccountsController.getServiceAccountOrganizationPermissions
);
// router.get(
// '/:serviceAccountId/permissions/organization',
// param('serviceAccountId').exists().isString().trim(),
// query('offset').exists(),
// query('limit').exists(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: ['jwt']
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED]
// }),
// serviceAccountsController.getServiceAccountOrganizationPermission
// );
router.get(
'/:serviceAccountId/permissions/workspace',
@@ -106,19 +106,19 @@ router.get(
serviceAccountsController.getServiceAccountWorkspacePermissions
);
router.post(
'/:serviceAccountId/permissions/organization',
param('serviceAccountId').exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: ['jwt']
}),
requireServiceAccountAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED]
}),
serviceAccountsController.addServiceAccountOrganizationPermission
);
// router.post(
// '/:serviceAccountId/permissions/organization',
// param('serviceAccountId').exists().isString().trim(),
// validateRequest,
// requireAuth({
// acceptedAuthModes: ['jwt']
// }),
// requireServiceAccountAuth({
// acceptedRoles: [OWNER, ADMIN],
// acceptedStatuses: [ACCEPTED]
// }),
// serviceAccountsController.addServiceAccountOrganizationPermission
// );
router.post(
'/:serviceAccountId/permissions/workspace',
@@ -129,6 +129,8 @@ router.post(
body('canWrite').isBoolean().optional(),
body('canUpdate').isBoolean().optional(),
body('canDelete').isBoolean().optional(),
body('encryptedKey').exists().isString().notEmpty(),
body('nonce').exists().isString().notEmpty(),
validateRequest,
requireAuth({
acceptedAuthModes: ['jwt']
@@ -145,9 +147,9 @@ router.post(
);
router.delete(
'/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionsId',
'/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionId',
param('serviceAccountId').exists().isString().trim(),
param('serviceAccountWorkspacePermissionsId').exists().isString().trim(),
param('serviceAccountWorkspacePermissionId').exists().isString().trim(),
validateRequest,
requireAuth({
acceptedAuthModes: ['jwt']
@@ -156,7 +158,7 @@ router.delete(
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED]
}),
requireServiceAccountWorkspacePermissionsAuth({
requireServiceAccountWorkspacePermissionAuth({
acceptedRoles: [OWNER, ADMIN],
acceptedStatuses: [ACCEPTED]
}),