mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-12 14:29:34 +00:00
Checkpoint service account functionality, added UI and general backend structure
This commit is contained in:
@@ -295,6 +295,7 @@ return res.status(200).send({
|
||||
*/
|
||||
export const getOrganizationServiceAccounts = async (req: Request, res: Response) => {
|
||||
const { organizationId } = req.params;
|
||||
|
||||
const serviceAccounts = await ServiceAccount.find({
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
@@ -1,34 +1,49 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import crypto from 'crypto';
|
||||
import bcrypt from 'bcrypt';
|
||||
import {
|
||||
ServiceAccount,
|
||||
ServiceAccountKey,
|
||||
ServiceAccountPermission
|
||||
ServiceAccountOrganizationPermissions,
|
||||
ServiceAccountWorkspacePermissions
|
||||
} from '../../models';
|
||||
import {
|
||||
validateCreateServiceAccountPermission
|
||||
} from '../../helpers/serviceAccount';
|
||||
import {
|
||||
CreateServiceAccountDto,
|
||||
AddServiceAccountPermissionDto
|
||||
CreateServiceAccountDto
|
||||
} from '../../interfaces/serviceAccounts/dto';
|
||||
import {
|
||||
PERMISSION_SA_WORKSPACE_SET,
|
||||
PERMISSION_SA_SET
|
||||
} from '../../variables';
|
||||
import { ServiceAccountKeyNotFoundError, ValidationError } from '../../utils/errors';
|
||||
import { BadRequestError, ServiceAccountNotFoundError } from '../../utils/errors';
|
||||
import { getSaltRounds } from '../../config';
|
||||
|
||||
/**
|
||||
* Return service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getServiceAccount = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
|
||||
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
|
||||
|
||||
if (!serviceAccount) {
|
||||
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new service account under organization with id [organizationId]
|
||||
* that has access to workspaces [workspaces]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export const createServiceAccount = async (req: Request, res: Response) => {
|
||||
const {
|
||||
organizationId,
|
||||
name,
|
||||
organizationId,
|
||||
publicKey,
|
||||
expiresIn,
|
||||
}: CreateServiceAccountDto = req.body;
|
||||
@@ -38,15 +53,59 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
||||
expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
}
|
||||
|
||||
const secret = crypto.randomBytes(16).toString('base64');
|
||||
const secretHash = await bcrypt.hash(secret, getSaltRounds());
|
||||
|
||||
// create service account
|
||||
const serviceAccount = await new ServiceAccount({
|
||||
name,
|
||||
organization: new Types.ObjectId(organizationId),
|
||||
user: req.user,
|
||||
publicKey,
|
||||
expiresAt
|
||||
expiresAt,
|
||||
secretHash
|
||||
}).save();
|
||||
|
||||
const serviceAccountObj = serviceAccount.toObject();
|
||||
|
||||
delete serviceAccountObj.secretHash;
|
||||
|
||||
// provision default org-level permissions for service account
|
||||
const permissions = await new ServiceAccountOrganizationPermissions({
|
||||
serviceAccount: serviceAccount._id
|
||||
}).save();
|
||||
|
||||
const secretId = Buffer.from(serviceAccount._id.toString(), 'hex').toString('base64');
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountAccessKey: `SA.${secretId}.${secret}`,
|
||||
serviceAccount: serviceAccountObj
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Change name of service account with id [serviceAccountId] to [name]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const changeServiceAccountName = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
const { name } = req.body;
|
||||
|
||||
const serviceAccount = await ServiceAccount.findOneAndUpdate(
|
||||
{
|
||||
_id: new Types.ObjectId(serviceAccountId)
|
||||
},
|
||||
{
|
||||
name
|
||||
},
|
||||
{
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount
|
||||
});
|
||||
@@ -78,66 +137,111 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a permission to service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
* Return organization-level permissions for service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const addServiceAccountPermission = async (req: Request, res: Response) => {
|
||||
const {
|
||||
name,
|
||||
workspaceId,
|
||||
environment
|
||||
}: AddServiceAccountPermissionDto = req.body;
|
||||
export const getServiceAccountOrganizationPermissions = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
|
||||
if (PERMISSION_SA_WORKSPACE_SET.has(name)) {
|
||||
// case: permission named [name] is workspace-related
|
||||
|
||||
// some such permissions require workspaceId and environment to be present.
|
||||
|
||||
if (!workspaceId || !environment) {
|
||||
throw ValidationError({
|
||||
message: 'Failed validation that is workspace-related permission must specify a workspace and environment'
|
||||
});
|
||||
} else {
|
||||
const serviceAccountKey = await ServiceAccountKey.findOne({
|
||||
serviceAccount: req.serviceAccount._id,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
if (!serviceAccountKey) throw ServiceAccountKeyNotFoundError({ message: 'Failed to find service account key' });
|
||||
}
|
||||
}
|
||||
|
||||
const serviceAccountPermission = await new ServiceAccountPermission({
|
||||
serviceAccount: req.serviceAccount._id,
|
||||
name,
|
||||
workspace: workspaceId ? new Types.ObjectId(workspaceId) : undefined,
|
||||
environment
|
||||
const permissions = await ServiceAccountOrganizationPermissions.findOne({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountPermission
|
||||
permissions
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a permission from service account with id [serviceAccountId]
|
||||
* Return workspace-level permissions for service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const deleteServiceAccountPermission = async (req: Request, res: Response) => {
|
||||
const { serviceAccountPermissionId } = req.params;
|
||||
|
||||
// user must either be an admin/owner of the organization or they must
|
||||
// have created the service account in the first place to be able to delete it
|
||||
|
||||
// TODO: how to delete just 1 permission?
|
||||
|
||||
|
||||
const serviceAccountPermission = await ServiceAccountPermission.findByIdAndDelete(serviceAccountPermissionId);
|
||||
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
|
||||
const permissions = await ServiceAccountWorkspacePermissions.find({
|
||||
serviceAccount: req.serviceAccount._id
|
||||
}).populate('workspace');
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountPermission
|
||||
permissions
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
return res.status(200).send({
|
||||
permissions
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a workspace permissions to service account with id [serviceAccountId]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const addServiceAccountWorkspacePermission = async (req: Request, res: Response) => {
|
||||
const { serviceAccountId } = req.params;
|
||||
const {
|
||||
environment,
|
||||
workspaceId,
|
||||
canRead = false,
|
||||
canWrite = false,
|
||||
canUpdate = false,
|
||||
canDelete = false
|
||||
} = req.body;
|
||||
|
||||
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
|
||||
return res.status(400).send({
|
||||
message: 'Failed to validate workspace environment'
|
||||
});
|
||||
}
|
||||
|
||||
const existingPermission = await ServiceAccountWorkspacePermissions.findOne({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
environment
|
||||
});
|
||||
|
||||
if (existingPermission) throw BadRequestError({ message: 'Failed to add workspace permission to service account due to already-existing ' });
|
||||
|
||||
const permissions = await new ServiceAccountWorkspacePermissions({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
canRead,
|
||||
canWrite,
|
||||
canUpdate,
|
||||
canDelete
|
||||
}).save();
|
||||
|
||||
return res.status(200).send({
|
||||
permissions
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete workspace permissions 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);
|
||||
|
||||
return res.status(200).send({
|
||||
permissions
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,10 +260,14 @@ export const deleteServiceAccount = async (req: Request, res: Response) => {
|
||||
// case: service account with id [serviceAccountId] was deleted
|
||||
|
||||
await ServiceAccountKey.deleteMany({
|
||||
serviceAccount: serviceAccount?._id
|
||||
serviceAccount: serviceAccount._id
|
||||
});
|
||||
|
||||
await ServiceAccountPermission.deleteMany({
|
||||
await ServiceAccountOrganizationPermissions.deleteMany({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
||||
});
|
||||
|
||||
await ServiceAccountWorkspacePermissions.deleteMany({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { Types } from 'mongoose';
|
||||
import {
|
||||
Workspace,
|
||||
ServiceAccount,
|
||||
ServiceAccountKey
|
||||
} from '../models';
|
||||
import {
|
||||
WorkspaceNotFoundError,
|
||||
ServiceAccountNotFoundError,
|
||||
ServiceAccountKeyNotFoundError
|
||||
} from '../utils/errors';
|
||||
import {
|
||||
PERMISSION_SA_WORKSPACE_READ,
|
||||
PERMISSION_SA_WORKSPACE_WRITE,
|
||||
PERMISSION_SA_SET
|
||||
} from '../variables';
|
||||
|
||||
/**
|
||||
* Validate that user with id [userId] can provision the permission
|
||||
* named [name] for a service account with id [serviceAccountId] and
|
||||
* optionally workspace with id [workspaceId] and environment [environment]
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.name - name of permission to create
|
||||
* @param {Types.ObjectId} userId - id of user creating the permission
|
||||
* @param {Types.ObjectId} serviceAccountId - id of service account that permission will be bound to
|
||||
* @param {Types.ObjectId} workspaceId - id of workspace that permission concerns
|
||||
* @param {Types.ObjectId} workspaceId - id of service account that permission will be bound to
|
||||
*/
|
||||
const validateCreateServiceAccountPermission = async ({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
workspaceId,
|
||||
environment
|
||||
}: {
|
||||
name: string;
|
||||
userId: Types.ObjectId;
|
||||
serviceAccountId: Types.ObjectId,
|
||||
workspaceId?: Types.ObjectId;
|
||||
environment?: string;
|
||||
}) => {
|
||||
|
||||
// TODO: as we upgrade user permissions to be more global, then we should take into account
|
||||
// the user's permissions as it concerns to being able to interact with service accounts
|
||||
|
||||
if (!PERMISSION_SA_SET.has(name)) throw new Error(`${name} is not a valid permission name`);
|
||||
|
||||
if ([
|
||||
PERMISSION_SA_WORKSPACE_READ,
|
||||
PERMISSION_SA_WORKSPACE_WRITE
|
||||
].includes(name)) {
|
||||
if (workspaceId && environment) {
|
||||
// case: either workspace id [workspaceId] or environment name [environment] is being passed in
|
||||
// (i.e. validating a service account permission concerning a workspace and/or environment)
|
||||
const workspace = await Workspace.findById(workspaceId);
|
||||
|
||||
if (!workspace) {
|
||||
// case: workspace does not exist
|
||||
throw WorkspaceNotFoundError({ message: 'Failed to locate workspace' });
|
||||
}
|
||||
|
||||
if (!workspace.environments.some((env) => env.slug === environment)) {
|
||||
// case: environment name [environment] is not a valid environment slug in workspace
|
||||
throw Error('Failed to locate environment in workspace');
|
||||
}
|
||||
|
||||
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
|
||||
if (!serviceAccount) {
|
||||
// case: service account does not exist
|
||||
throw ServiceAccountNotFoundError({ message: 'Failed to locate service account' });
|
||||
}
|
||||
|
||||
const serviceAccountKey = await ServiceAccountKey.findOne({
|
||||
serviceAccount: serviceAccount._id,
|
||||
workspace: workspaceId
|
||||
});
|
||||
|
||||
if (!serviceAccountKey) {
|
||||
// case: service account key does not exist
|
||||
throw ServiceAccountKeyNotFoundError({ message: 'Failed to locate service account key' });
|
||||
}
|
||||
} else {
|
||||
throw new Error('Failed to validate workspace and environment for workspace-related permission');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const validateDeleteServiceAccountPermission = async ({
|
||||
userId,
|
||||
serviceAccountId,
|
||||
name,
|
||||
workspaceId,
|
||||
environment
|
||||
}: {
|
||||
userId: Types.ObjectId;
|
||||
serviceAccountId: Types.ObjectId;
|
||||
name: string;
|
||||
workspaceId: Types.ObjectId;
|
||||
environment: string;
|
||||
}) => {
|
||||
// does the user have the authority to delete the permission?
|
||||
// does the service account permission exist?
|
||||
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
validateCreateServiceAccountPermission
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import requireIntegrationAuthorizationAuth from './requireIntegrationAuthorizati
|
||||
import requireServiceTokenAuth from './requireServiceTokenAuth';
|
||||
import requireServiceTokenDataAuth from './requireServiceTokenDataAuth';
|
||||
import requireServiceAccountAuth from './requireServiceAccountAuth';
|
||||
import requireServiceAccountWorkspacePermissionsAuth from './requireServiceAccountWorkspacePermissionsAuth';
|
||||
import requireSecretAuth from './requireSecretAuth';
|
||||
import requireSecretsAuth from './requireSecretsAuth';
|
||||
import validateRequest from './validateRequest';
|
||||
@@ -29,6 +30,7 @@ export {
|
||||
requireServiceTokenAuth,
|
||||
requireServiceTokenDataAuth,
|
||||
requireServiceAccountAuth,
|
||||
requireServiceAccountWorkspacePermissionsAuth,
|
||||
requireSecretAuth,
|
||||
requireSecretsAuth,
|
||||
validateRequest
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { ServiceAccount, ServiceAccountWorkspacePermissions } from '../models';
|
||||
import {
|
||||
ServiceAccountNotFoundError
|
||||
} from '../utils/errors';
|
||||
import {
|
||||
validateMembershipOrg
|
||||
} from '../helpers/membershipOrg';
|
||||
|
||||
type req = 'params' | 'body' | 'query';
|
||||
|
||||
const requireServiceAccountWorkspacePermissionsAuth = ({
|
||||
acceptedRoles,
|
||||
acceptedStatuses,
|
||||
location = 'params'
|
||||
}: {
|
||||
acceptedRoles: string[];
|
||||
acceptedStatuses: string[];
|
||||
location?: req;
|
||||
}) => {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const serviceAccountWorkspacePermissionsId = req[location].serviceAccountWorkspacePermissionsId;
|
||||
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermissions.findById(serviceAccountWorkspacePermissionsId);
|
||||
|
||||
if (!serviceAccountWorkspacePermissions) {
|
||||
return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account workspace permission' }));
|
||||
}
|
||||
|
||||
const serviceAccount = await ServiceAccount.findById(serviceAccountWorkspacePermissions.serviceAccount);
|
||||
|
||||
if (!serviceAccount) {
|
||||
return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account' }));
|
||||
}
|
||||
|
||||
if (serviceAccount.user.toString() !== req.user.id.toString()) {
|
||||
// case: creator of the service account is different from
|
||||
// the user on the request -> apply middleware role/status validation
|
||||
await validateMembershipOrg({
|
||||
userId: req.user._id,
|
||||
organizationId: serviceAccount.organization,
|
||||
acceptedRoles,
|
||||
acceptedStatuses
|
||||
});
|
||||
}
|
||||
|
||||
req.serviceAccount = serviceAccount;
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
export default requireServiceAccountWorkspacePermissionsAuth;
|
||||
@@ -12,7 +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 ServiceAccountPermission, { IServiceAccountPermission } from './serviceAccountPermission';
|
||||
import ServiceAccountOrganizationPermissions, { IServiceAccountOrganizationPermissions } from './serviceAccountOrganizationPermission'; // new
|
||||
import ServiceAccountWorkspacePermissions, { IServiceAccountWorkspacePermissions } from './serviceAccountWorkspacePermissions'; // new
|
||||
import TokenData, { ITokenData } from './tokenData';
|
||||
import User, { IUser } from './user';
|
||||
import UserAction, { IUserAction } from './userAction';
|
||||
@@ -50,8 +51,10 @@ export {
|
||||
IServiceAccount,
|
||||
ServiceAccountKey,
|
||||
IServiceAccountKey,
|
||||
ServiceAccountPermission,
|
||||
IServiceAccountPermission,
|
||||
ServiceAccountOrganizationPermissions,
|
||||
IServiceAccountOrganizationPermissions,
|
||||
ServiceAccountWorkspacePermissions,
|
||||
IServiceAccountWorkspacePermissions,
|
||||
TokenData,
|
||||
ITokenData,
|
||||
User,
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface IServiceAccount extends Document {
|
||||
user: Types.ObjectId;
|
||||
publicKey: string;
|
||||
expiresAt: Date;
|
||||
secretHash: string;
|
||||
}
|
||||
|
||||
const serviceAccountSchema = new Schema<IServiceAccount>(
|
||||
@@ -31,6 +32,11 @@ const serviceAccountSchema = new Schema<IServiceAccount>(
|
||||
},
|
||||
expiresAt: {
|
||||
type: Date
|
||||
},
|
||||
secretHash: {
|
||||
type: String,
|
||||
required: true,
|
||||
select: false
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Schema, model, Types, Document } from 'mongoose';
|
||||
|
||||
export interface IServiceAccountOrganizationPermissions extends Document {
|
||||
_id: Types.ObjectId;
|
||||
serviceAccount: Types.ObjectId;
|
||||
canFoo: boolean;
|
||||
}
|
||||
|
||||
const serviceAccountOrganizationPermissionsSchema = new Schema<IServiceAccountOrganizationPermissions>(
|
||||
{
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'ServiceAccount',
|
||||
required: true
|
||||
},
|
||||
canFoo: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
const ServiceAccountOrganizationPermissions = model<IServiceAccountOrganizationPermissions>('ServiceAccountOrganizationPermissions', serviceAccountOrganizationPermissionsSchema);
|
||||
|
||||
export default ServiceAccountOrganizationPermissions;
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Schema, model, Types, Document } from 'mongoose';
|
||||
|
||||
export interface IServiceAccountPermission extends Document {
|
||||
_id: Types.ObjectId;
|
||||
serviceAccount: Types.ObjectId;
|
||||
name: string;
|
||||
workspace?: Types.ObjectId;
|
||||
environment?: string;
|
||||
}
|
||||
|
||||
const serviceAccountPermissionSchema = new Schema<IServiceAccountPermission>(
|
||||
{
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'ServiceAccount',
|
||||
required: true
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Workspace',
|
||||
default: null
|
||||
},
|
||||
environment: {
|
||||
type: 'String',
|
||||
default: null
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
const ServiceAccountPermission = model<IServiceAccountPermission>('ServiceAccountPermission', serviceAccountPermissionSchema);
|
||||
|
||||
export default ServiceAccountPermission;
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Schema, model, Types, Document } from 'mongoose';
|
||||
|
||||
export interface IServiceAccountWorkspacePermissions extends Document {
|
||||
_id: Types.ObjectId;
|
||||
serviceAccount: Types.ObjectId;
|
||||
workspace: Types.ObjectId;
|
||||
environment: string;
|
||||
canRead: boolean;
|
||||
canWrite: boolean;
|
||||
canUpdate: boolean;
|
||||
canDelete: boolean;
|
||||
}
|
||||
|
||||
const serviceAccountWorkspacePermissions = new Schema<IServiceAccountWorkspacePermissions>(
|
||||
{
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'ServiceAccount',
|
||||
required: true
|
||||
},
|
||||
workspace:{
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Workspace',
|
||||
required: true
|
||||
},
|
||||
environment: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
canRead: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
canWrite: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
canUpdate: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
canDelete: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
{
|
||||
timestamps: true
|
||||
}
|
||||
);
|
||||
|
||||
const ServiceAccountWorkspacePermissions = model<IServiceAccountWorkspacePermissions>('ServiceAccountWorkspacePermissions', serviceAccountWorkspacePermissions);
|
||||
|
||||
export default ServiceAccountWorkspacePermissions;
|
||||
@@ -1,27 +1,45 @@
|
||||
import express from 'express';
|
||||
const router = express.Router();
|
||||
import {
|
||||
requireAuth,
|
||||
requireOrganizationAuth,
|
||||
requireWorkspaceAuth,
|
||||
requireServiceAccountAuth,
|
||||
requireServiceAccountWorkspacePermissionsAuth,
|
||||
validateRequest
|
||||
} from '../../middleware';
|
||||
import { body } from 'express-validator';
|
||||
import { param, query, body } from 'express-validator';
|
||||
import {
|
||||
OWNER,
|
||||
ADMIN,
|
||||
MEMBER,
|
||||
ACCEPTED,
|
||||
PERMISSION_SA_SET
|
||||
ACCEPTED
|
||||
} from '../../variables';
|
||||
import { serviceAccountsController } from '../../controllers/v2';
|
||||
|
||||
router.get(
|
||||
'/:serviceAccountId',
|
||||
param('serviceAccountId').exists().isString().trim(),
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
}),
|
||||
requireServiceAccountAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
serviceAccountsController.getServiceAccount
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/',
|
||||
body('organizationId').exists().isString().trim(),
|
||||
body('name').exists().isString().trim(),
|
||||
body('publicKey').exists().isString().trim(),
|
||||
body('expiresIn'), // measured in ms
|
||||
body('expiresIn').isNumeric(), // measured in ms
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
@@ -30,17 +48,119 @@ router.post(
|
||||
serviceAccountsController.createServiceAccount
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/serviceAccountId/:serviceAccountId/permissions',
|
||||
body('name').exists().isString().trim().custom((value) => PERMISSION_SA_SET.has(value)),
|
||||
body('workspaceId').optional().isMongoId(),
|
||||
body('environment').optional(),
|
||||
router.patch(
|
||||
'/:serviceAccountId/name',
|
||||
param('serviceAccountId').exists().isString().trim(),
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
}),
|
||||
requireServiceAccountAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
serviceAccountsController.addServiceAccountPermission
|
||||
serviceAccountsController.changeServiceAccountName
|
||||
);
|
||||
|
||||
router.delete(
|
||||
'/:serviceAccountId',
|
||||
param('serviceAccountId').exists().isString().trim(),
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
}),
|
||||
requireServiceAccountAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
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/workspace',
|
||||
param('serviceAccountId').exists().isString().trim(),
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
}),
|
||||
requireServiceAccountAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
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/workspace',
|
||||
param('serviceAccountId').exists().isString().trim(),
|
||||
body('workspaceId').exists().isString().notEmpty(),
|
||||
body('environment').exists().isString().notEmpty(),
|
||||
body('canRead').isBoolean().optional(),
|
||||
body('canWrite').isBoolean().optional(),
|
||||
body('canUpdate').isBoolean().optional(),
|
||||
body('canDelete').isBoolean().optional(),
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
}),
|
||||
requireServiceAccountAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
requireWorkspaceAuth({
|
||||
acceptedRoles: [ADMIN, MEMBER],
|
||||
location: 'body'
|
||||
}),
|
||||
serviceAccountsController.addServiceAccountWorkspacePermission
|
||||
);
|
||||
|
||||
router.delete(
|
||||
'/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionsId',
|
||||
param('serviceAccountId').exists().isString().trim(),
|
||||
param('serviceAccountWorkspacePermissionsId').exists().isString().trim(),
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
}),
|
||||
requireServiceAccountAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
requireServiceAccountWorkspacePermissionsAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
serviceAccountsController.deleteServiceAccountWorkspacePermission
|
||||
);
|
||||
|
||||
// router.post(
|
||||
@@ -55,28 +175,4 @@ router.post(
|
||||
// serviceAccountsController.addServiceAccountKey
|
||||
// );
|
||||
|
||||
router.delete(
|
||||
'/:serviceAccountId/key/:serviceAccountKeyId',
|
||||
requireServiceAccountAuth({
|
||||
acceptedRoles: [OWNER, ADMIN],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
}),
|
||||
async (req, res) => {
|
||||
// TODO: delete service account key id
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: create service account permission
|
||||
// router.post(
|
||||
|
||||
// );
|
||||
|
||||
// TODO: delete service account permission
|
||||
|
||||
router.delete(
|
||||
'/:serviceAccountId/service-account-permission/:serviceAccountPermissionId',
|
||||
|
||||
)
|
||||
|
||||
|
||||
export default router;
|
||||
@@ -66,7 +66,7 @@ export const initSmtp = () => {
|
||||
const transporter = nodemailer.createTransport(mailOpts);
|
||||
transporter
|
||||
.verify()
|
||||
.then(() => {
|
||||
.then((err) => {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureMessage('SMTP - Successfully connected');
|
||||
})
|
||||
|
||||
@@ -63,12 +63,6 @@ import {
|
||||
TOKEN_EMAIL_ORG_INVITATION,
|
||||
TOKEN_EMAIL_PASSWORD_RESET
|
||||
} from './token';
|
||||
import {
|
||||
PERMISSION_SA_WORKSPACE_READ,
|
||||
PERMISSION_SA_WORKSPACE_WRITE,
|
||||
PERMISSION_SA_WORKSPACE_SET,
|
||||
PERMISSION_SA_SET
|
||||
} from './permissions';
|
||||
|
||||
export {
|
||||
OWNER,
|
||||
@@ -130,9 +124,5 @@ export {
|
||||
TOKEN_EMAIL_CONFIRMATION,
|
||||
TOKEN_EMAIL_MFA,
|
||||
TOKEN_EMAIL_ORG_INVITATION,
|
||||
TOKEN_EMAIL_PASSWORD_RESET,
|
||||
PERMISSION_SA_WORKSPACE_READ,
|
||||
PERMISSION_SA_WORKSPACE_WRITE,
|
||||
PERMISSION_SA_WORKSPACE_SET,
|
||||
PERMISSION_SA_SET
|
||||
TOKEN_EMAIL_PASSWORD_RESET
|
||||
};
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
const PERMISSION_SA_WORKSPACE_READ = 'read';
|
||||
const PERMISSION_SA_WORKSPACE_WRITE = 'write';
|
||||
|
||||
const PERMISSION_SA_WORKSPACE_SET = new Set([
|
||||
PERMISSION_SA_WORKSPACE_READ,
|
||||
PERMISSION_SA_WORKSPACE_WRITE
|
||||
]);
|
||||
|
||||
const PERMISSION_SA_SET = new Set([
|
||||
PERMISSION_SA_WORKSPACE_READ,
|
||||
PERMISSION_SA_WORKSPACE_WRITE
|
||||
]);
|
||||
|
||||
export {
|
||||
PERMISSION_SA_WORKSPACE_READ,
|
||||
PERMISSION_SA_WORKSPACE_WRITE,
|
||||
PERMISSION_SA_WORKSPACE_SET,
|
||||
PERMISSION_SA_SET
|
||||
}
|
||||
Reference in New Issue
Block a user