mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-02 23:25:51 +00:00
Clean up and weave crypto into service account permissions
This commit is contained in:
@@ -5,8 +5,8 @@ import bcrypt from 'bcrypt';
|
|||||||
import {
|
import {
|
||||||
ServiceAccount,
|
ServiceAccount,
|
||||||
ServiceAccountKey,
|
ServiceAccountKey,
|
||||||
ServiceAccountOrganizationPermissions,
|
ServiceAccountOrganizationPermission,
|
||||||
ServiceAccountWorkspacePermissions
|
ServiceAccountWorkspacePermission
|
||||||
} from '../../models';
|
} from '../../models';
|
||||||
import {
|
import {
|
||||||
CreateServiceAccountDto
|
CreateServiceAccountDto
|
||||||
@@ -71,8 +71,8 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
delete serviceAccountObj.secretHash;
|
delete serviceAccountObj.secretHash;
|
||||||
|
|
||||||
// provision default org-level permissions for service account
|
// provision default org-level permission for service account
|
||||||
const permissions = await new ServiceAccountOrganizationPermissions({
|
await new ServiceAccountOrganizationPermission({
|
||||||
serviceAccount: serviceAccount._id
|
serviceAccount: serviceAccount._id
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
@@ -136,57 +136,57 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
|||||||
return serviceAccountKey;
|
return serviceAccountKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Return organization-level permissions for service account with id [serviceAccountId]
|
// * Return organization-level permissions for service account with id [serviceAccountId]
|
||||||
* @param req
|
// * @param req
|
||||||
* @param res
|
// * @param res
|
||||||
*/
|
// */
|
||||||
export const getServiceAccountOrganizationPermissions = async (req: Request, res: Response) => {
|
// export const getServiceAccountOrganizationPermissions = async (req: Request, res: Response) => {
|
||||||
const { serviceAccountId } = req.params;
|
// const { serviceAccountId } = req.params;
|
||||||
|
|
||||||
const permissions = await ServiceAccountOrganizationPermissions.findOne({
|
// const permissions = await ServiceAccountOrganizationPermissions.findOne({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
// serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
});
|
// });
|
||||||
|
|
||||||
return res.status(200).send({
|
// return res.status(200).send({
|
||||||
permissions
|
// permissions
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return workspace-level permissions for service account with id [serviceAccountId]
|
* Return workspace-level permission for service account with id [serviceAccountId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
|
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
|
||||||
const permissions = await ServiceAccountWorkspacePermissions.find({
|
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermission.find({
|
||||||
serviceAccount: req.serviceAccount._id
|
serviceAccount: req.serviceAccount._id
|
||||||
}).populate('workspace');
|
}).populate('workspace');
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
permissions
|
serviceAccountWorkspacePermissions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Add organization permissions to service account with id [serviceAccountId]
|
// * Add organization permissions to service account with id [serviceAccountId]
|
||||||
* @param req
|
// * @param req
|
||||||
* @param res
|
// * @param res
|
||||||
*/
|
// */
|
||||||
export const addServiceAccountOrganizationPermission = async (req: Request, res: Response) => {
|
// export const addServiceAccountOrganizationPermission = async (req: Request, res: Response) => {
|
||||||
const permissions = ServiceAccountOrganizationPermissions.findOne({
|
// const permissions = ServiceAccountOrganizationPermissions.findOne({
|
||||||
serviceAccount: req.serviceAccount._id
|
// serviceAccount: req.serviceAccount._id
|
||||||
});
|
// });
|
||||||
|
|
||||||
// TODO
|
// // TODO
|
||||||
|
|
||||||
return res.status(200).send({
|
// return res.status(200).send({
|
||||||
permissions
|
// permissions
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a workspace permissions to service account with id [serviceAccountId]
|
* Add a workspace permission to service account with id [serviceAccountId]
|
||||||
* @param req
|
* @param req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
@@ -198,7 +198,9 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
|
|||||||
canRead = false,
|
canRead = false,
|
||||||
canWrite = false,
|
canWrite = false,
|
||||||
canUpdate = false,
|
canUpdate = false,
|
||||||
canDelete = false
|
canDelete = false,
|
||||||
|
encryptedKey,
|
||||||
|
nonce
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
|
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),
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
workspaceId: new Types.ObjectId(workspaceId),
|
workspaceId: new Types.ObjectId(workspaceId),
|
||||||
environment
|
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 ' });
|
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),
|
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
environment,
|
environment,
|
||||||
@@ -225,23 +227,52 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
|
|||||||
canDelete
|
canDelete
|
||||||
}).save();
|
}).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({
|
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 req
|
||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteServiceAccountWorkspacePermission = async (req: Request, res: Response) => {
|
export const deleteServiceAccountWorkspacePermission = async (req: Request, res: Response) => {
|
||||||
const { serviceAccountWorkspacePermissionsId } = req.params;
|
const { serviceAccountWorkspacePermissionId } = req.params;
|
||||||
|
const serviceAccountWorkspacePermission = await ServiceAccountWorkspacePermission.findByIdAndDelete(serviceAccountWorkspacePermissionId);
|
||||||
const permissions = await ServiceAccountWorkspacePermissions.findByIdAndDelete(serviceAccountWorkspacePermissionsId);
|
|
||||||
|
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({
|
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);
|
const serviceAccount = await ServiceAccount.findByIdAndDelete(serviceAccountId);
|
||||||
|
|
||||||
if (serviceAccount) {
|
if (serviceAccount) {
|
||||||
// case: service account with id [serviceAccountId] was deleted
|
|
||||||
|
|
||||||
await ServiceAccountKey.deleteMany({
|
await ServiceAccountKey.deleteMany({
|
||||||
serviceAccount: serviceAccount._id
|
serviceAccount: serviceAccount._id
|
||||||
});
|
});
|
||||||
|
|
||||||
await ServiceAccountOrganizationPermissions.deleteMany({
|
await ServiceAccountOrganizationPermission.deleteMany({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
serviceAccount: new Types.ObjectId(serviceAccountId)
|
||||||
});
|
});
|
||||||
|
|
||||||
await ServiceAccountWorkspacePermissions.deleteMany({
|
await ServiceAccountWorkspacePermission.deleteMany({
|
||||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
serviceAccount: new Types.ObjectId(serviceAccountId)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import requireIntegrationAuthorizationAuth from './requireIntegrationAuthorizati
|
|||||||
import requireServiceTokenAuth from './requireServiceTokenAuth';
|
import requireServiceTokenAuth from './requireServiceTokenAuth';
|
||||||
import requireServiceTokenDataAuth from './requireServiceTokenDataAuth';
|
import requireServiceTokenDataAuth from './requireServiceTokenDataAuth';
|
||||||
import requireServiceAccountAuth from './requireServiceAccountAuth';
|
import requireServiceAccountAuth from './requireServiceAccountAuth';
|
||||||
import requireServiceAccountWorkspacePermissionsAuth from './requireServiceAccountWorkspacePermissionsAuth';
|
import requireServiceAccountWorkspacePermissionAuth from './requireServiceAccountWorkspacePermissionAuth';
|
||||||
import requireSecretAuth from './requireSecretAuth';
|
import requireSecretAuth from './requireSecretAuth';
|
||||||
import requireSecretsAuth from './requireSecretsAuth';
|
import requireSecretsAuth from './requireSecretsAuth';
|
||||||
import validateRequest from './validateRequest';
|
import validateRequest from './validateRequest';
|
||||||
@@ -30,7 +30,7 @@ export {
|
|||||||
requireServiceTokenAuth,
|
requireServiceTokenAuth,
|
||||||
requireServiceTokenDataAuth,
|
requireServiceTokenDataAuth,
|
||||||
requireServiceAccountAuth,
|
requireServiceAccountAuth,
|
||||||
requireServiceAccountWorkspacePermissionsAuth,
|
requireServiceAccountWorkspacePermissionAuth,
|
||||||
requireSecretAuth,
|
requireSecretAuth,
|
||||||
requireSecretsAuth,
|
requireSecretsAuth,
|
||||||
validateRequest
|
validateRequest
|
||||||
|
|||||||
+7
-7
@@ -1,5 +1,5 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { ServiceAccount, ServiceAccountWorkspacePermissions } from '../models';
|
import { ServiceAccount, ServiceAccountWorkspacePermission } from '../models';
|
||||||
import {
|
import {
|
||||||
ServiceAccountNotFoundError
|
ServiceAccountNotFoundError
|
||||||
} from '../utils/errors';
|
} from '../utils/errors';
|
||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
|
|
||||||
type req = 'params' | 'body' | 'query';
|
type req = 'params' | 'body' | 'query';
|
||||||
|
|
||||||
const requireServiceAccountWorkspacePermissionsAuth = ({
|
const requireServiceAccountWorkspacePermissionAuth = ({
|
||||||
acceptedRoles,
|
acceptedRoles,
|
||||||
acceptedStatuses,
|
acceptedStatuses,
|
||||||
location = 'params'
|
location = 'params'
|
||||||
@@ -19,14 +19,14 @@ const requireServiceAccountWorkspacePermissionsAuth = ({
|
|||||||
location?: req;
|
location?: req;
|
||||||
}) => {
|
}) => {
|
||||||
return async (req: Request, res: Response, next: NextFunction) => {
|
return async (req: Request, res: Response, next: NextFunction) => {
|
||||||
const serviceAccountWorkspacePermissionsId = req[location].serviceAccountWorkspacePermissionsId;
|
const serviceAccountWorkspacePermissionId = req[location].serviceAccountWorkspacePermissionId;
|
||||||
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermissions.findById(serviceAccountWorkspacePermissionsId);
|
const serviceAccountWorkspacePermission = await ServiceAccountWorkspacePermission.findById(serviceAccountWorkspacePermissionId);
|
||||||
|
|
||||||
if (!serviceAccountWorkspacePermissions) {
|
if (!serviceAccountWorkspacePermission) {
|
||||||
return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account workspace permission' }));
|
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) {
|
if (!serviceAccount) {
|
||||||
return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account' }));
|
return next(ServiceAccountNotFoundError({ message: 'Failed to locate Service Account' }));
|
||||||
@@ -49,4 +49,4 @@ const requireServiceAccountWorkspacePermissionsAuth = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default requireServiceAccountWorkspacePermissionsAuth;
|
export default requireServiceAccountWorkspacePermissionAuth;
|
||||||
@@ -12,8 +12,8 @@ import Secret, { ISecret } from './secret';
|
|||||||
import ServiceToken, { IServiceToken } from './serviceToken';
|
import ServiceToken, { IServiceToken } from './serviceToken';
|
||||||
import ServiceAccount, { IServiceAccount } from './serviceAccount'; // new
|
import ServiceAccount, { IServiceAccount } from './serviceAccount'; // new
|
||||||
import ServiceAccountKey, { IServiceAccountKey } from './serviceAccountKey'; // new
|
import ServiceAccountKey, { IServiceAccountKey } from './serviceAccountKey'; // new
|
||||||
import ServiceAccountOrganizationPermissions, { IServiceAccountOrganizationPermissions } from './serviceAccountOrganizationPermission'; // new
|
import ServiceAccountOrganizationPermission, { IServiceAccountOrganizationPermission } from './serviceAccountOrganizationPermission'; // new
|
||||||
import ServiceAccountWorkspacePermissions, { IServiceAccountWorkspacePermissions } from './serviceAccountWorkspacePermissions'; // new
|
import ServiceAccountWorkspacePermission, { IServiceAccountWorkspacePermission } from './serviceAccountWorkspacePermission'; // new
|
||||||
import TokenData, { ITokenData } from './tokenData';
|
import TokenData, { ITokenData } from './tokenData';
|
||||||
import User, { IUser } from './user';
|
import User, { IUser } from './user';
|
||||||
import UserAction, { IUserAction } from './userAction';
|
import UserAction, { IUserAction } from './userAction';
|
||||||
@@ -51,10 +51,10 @@ export {
|
|||||||
IServiceAccount,
|
IServiceAccount,
|
||||||
ServiceAccountKey,
|
ServiceAccountKey,
|
||||||
IServiceAccountKey,
|
IServiceAccountKey,
|
||||||
ServiceAccountOrganizationPermissions,
|
ServiceAccountOrganizationPermission,
|
||||||
IServiceAccountOrganizationPermissions,
|
IServiceAccountOrganizationPermission,
|
||||||
ServiceAccountWorkspacePermissions,
|
ServiceAccountWorkspacePermission,
|
||||||
IServiceAccountWorkspacePermissions,
|
IServiceAccountWorkspacePermission,
|
||||||
TokenData,
|
TokenData,
|
||||||
ITokenData,
|
ITokenData,
|
||||||
User,
|
User,
|
||||||
|
|||||||
@@ -1,21 +1,16 @@
|
|||||||
import { Schema, model, Types, Document } from 'mongoose';
|
import { Schema, model, Types, Document } from 'mongoose';
|
||||||
|
|
||||||
export interface IServiceAccountOrganizationPermissions extends Document {
|
export interface IServiceAccountOrganizationPermission extends Document {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
serviceAccount: Types.ObjectId;
|
serviceAccount: Types.ObjectId;
|
||||||
canFoo: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceAccountOrganizationPermissionsSchema = new Schema<IServiceAccountOrganizationPermissions>(
|
const serviceAccountOrganizationPermissionSchema = new Schema<IServiceAccountOrganizationPermission>(
|
||||||
{
|
{
|
||||||
serviceAccount: {
|
serviceAccount: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: 'ServiceAccount',
|
ref: 'ServiceAccount',
|
||||||
required: true
|
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;
|
||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
import { Schema, model, Types, Document } from 'mongoose';
|
import { Schema, model, Types, Document } from 'mongoose';
|
||||||
|
|
||||||
export interface IServiceAccountWorkspacePermissions extends Document {
|
export interface IServiceAccountWorkspacePermission extends Document {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
serviceAccount: Types.ObjectId;
|
serviceAccount: Types.ObjectId;
|
||||||
workspace: Types.ObjectId;
|
workspace: Types.ObjectId;
|
||||||
@@ -11,7 +11,7 @@ export interface IServiceAccountWorkspacePermissions extends Document {
|
|||||||
canDelete: boolean;
|
canDelete: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceAccountWorkspacePermissions = new Schema<IServiceAccountWorkspacePermissions>(
|
const serviceAccountWorkspacePermissionSchema = new Schema<IServiceAccountWorkspacePermission>(
|
||||||
{
|
{
|
||||||
serviceAccount: {
|
serviceAccount: {
|
||||||
type: Schema.Types.ObjectId,
|
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;
|
||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
requireOrganizationAuth,
|
requireOrganizationAuth,
|
||||||
requireWorkspaceAuth,
|
requireWorkspaceAuth,
|
||||||
requireServiceAccountAuth,
|
requireServiceAccountAuth,
|
||||||
requireServiceAccountWorkspacePermissionsAuth,
|
requireServiceAccountWorkspacePermissionAuth,
|
||||||
validateRequest
|
validateRequest
|
||||||
} from '../../middleware';
|
} from '../../middleware';
|
||||||
import { param, query, body } from 'express-validator';
|
import { param, query, body } from 'express-validator';
|
||||||
@@ -76,21 +76,21 @@ router.delete(
|
|||||||
serviceAccountsController.deleteServiceAccount
|
serviceAccountsController.deleteServiceAccount
|
||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
// router.get(
|
||||||
'/:serviceAccountId/permissions/organization',
|
// '/:serviceAccountId/permissions/organization',
|
||||||
param('serviceAccountId').exists().isString().trim(),
|
// param('serviceAccountId').exists().isString().trim(),
|
||||||
query('offset').exists(),
|
// query('offset').exists(),
|
||||||
query('limit').exists(),
|
// query('limit').exists(),
|
||||||
validateRequest,
|
// validateRequest,
|
||||||
requireAuth({
|
// requireAuth({
|
||||||
acceptedAuthModes: ['jwt']
|
// acceptedAuthModes: ['jwt']
|
||||||
}),
|
// }),
|
||||||
requireServiceAccountAuth({
|
// requireServiceAccountAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN],
|
// acceptedRoles: [OWNER, ADMIN],
|
||||||
acceptedStatuses: [ACCEPTED]
|
// acceptedStatuses: [ACCEPTED]
|
||||||
}),
|
// }),
|
||||||
serviceAccountsController.getServiceAccountOrganizationPermissions
|
// serviceAccountsController.getServiceAccountOrganizationPermission
|
||||||
);
|
// );
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/:serviceAccountId/permissions/workspace',
|
'/:serviceAccountId/permissions/workspace',
|
||||||
@@ -106,19 +106,19 @@ router.get(
|
|||||||
serviceAccountsController.getServiceAccountWorkspacePermissions
|
serviceAccountsController.getServiceAccountWorkspacePermissions
|
||||||
);
|
);
|
||||||
|
|
||||||
router.post(
|
// router.post(
|
||||||
'/:serviceAccountId/permissions/organization',
|
// '/:serviceAccountId/permissions/organization',
|
||||||
param('serviceAccountId').exists().isString().trim(),
|
// param('serviceAccountId').exists().isString().trim(),
|
||||||
validateRequest,
|
// validateRequest,
|
||||||
requireAuth({
|
// requireAuth({
|
||||||
acceptedAuthModes: ['jwt']
|
// acceptedAuthModes: ['jwt']
|
||||||
}),
|
// }),
|
||||||
requireServiceAccountAuth({
|
// requireServiceAccountAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN],
|
// acceptedRoles: [OWNER, ADMIN],
|
||||||
acceptedStatuses: [ACCEPTED]
|
// acceptedStatuses: [ACCEPTED]
|
||||||
}),
|
// }),
|
||||||
serviceAccountsController.addServiceAccountOrganizationPermission
|
// serviceAccountsController.addServiceAccountOrganizationPermission
|
||||||
);
|
// );
|
||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/:serviceAccountId/permissions/workspace',
|
'/:serviceAccountId/permissions/workspace',
|
||||||
@@ -129,6 +129,8 @@ router.post(
|
|||||||
body('canWrite').isBoolean().optional(),
|
body('canWrite').isBoolean().optional(),
|
||||||
body('canUpdate').isBoolean().optional(),
|
body('canUpdate').isBoolean().optional(),
|
||||||
body('canDelete').isBoolean().optional(),
|
body('canDelete').isBoolean().optional(),
|
||||||
|
body('encryptedKey').exists().isString().notEmpty(),
|
||||||
|
body('nonce').exists().isString().notEmpty(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt']
|
acceptedAuthModes: ['jwt']
|
||||||
@@ -145,9 +147,9 @@ router.post(
|
|||||||
);
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
'/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionsId',
|
'/:serviceAccountId/permissions/workspace/:serviceAccountWorkspacePermissionId',
|
||||||
param('serviceAccountId').exists().isString().trim(),
|
param('serviceAccountId').exists().isString().trim(),
|
||||||
param('serviceAccountWorkspacePermissionsId').exists().isString().trim(),
|
param('serviceAccountWorkspacePermissionId').exists().isString().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
requireAuth({
|
requireAuth({
|
||||||
acceptedAuthModes: ['jwt']
|
acceptedAuthModes: ['jwt']
|
||||||
@@ -156,7 +158,7 @@ router.delete(
|
|||||||
acceptedRoles: [OWNER, ADMIN],
|
acceptedRoles: [OWNER, ADMIN],
|
||||||
acceptedStatuses: [ACCEPTED]
|
acceptedStatuses: [ACCEPTED]
|
||||||
}),
|
}),
|
||||||
requireServiceAccountWorkspacePermissionsAuth({
|
requireServiceAccountWorkspacePermissionAuth({
|
||||||
acceptedRoles: [OWNER, ADMIN],
|
acceptedRoles: [OWNER, ADMIN],
|
||||||
acceptedStatuses: [ACCEPTED]
|
acceptedStatuses: [ACCEPTED]
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -26,6 +26,31 @@ type EncryptAsymmetricProps = {
|
|||||||
privateKey: string;
|
privateKey: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that private key [privateKey] is the one that corresponds to
|
||||||
|
* the public key [publicKey]
|
||||||
|
* @param {Object}
|
||||||
|
* @param {String} - base64-encoded Nacl private key
|
||||||
|
* @param {String} - base64-encoded Nacl public key
|
||||||
|
*/
|
||||||
|
const verifyPrivateKey = ({
|
||||||
|
privateKey,
|
||||||
|
publicKey
|
||||||
|
}: {
|
||||||
|
privateKey: string;
|
||||||
|
publicKey: string;
|
||||||
|
}) => {
|
||||||
|
const derivedPublicKey = nacl.util.encodeBase64(
|
||||||
|
nacl.box.keyPair.fromSecretKey(
|
||||||
|
nacl.util.decodeBase64(privateKey)
|
||||||
|
).publicKey
|
||||||
|
);
|
||||||
|
|
||||||
|
if (derivedPublicKey !== publicKey) {
|
||||||
|
throw new Error('Failed to verify private key');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derive a key from password [password] and salt [salt] using Argon2id
|
* Derive a key from password [password] and salt [salt] using Argon2id
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
@@ -204,5 +229,6 @@ export {
|
|||||||
decryptSymmetric,
|
decryptSymmetric,
|
||||||
deriveArgonKey,
|
deriveArgonKey,
|
||||||
encryptAssymmetric,
|
encryptAssymmetric,
|
||||||
encryptSymmetric,
|
encryptSymmetric,
|
||||||
generateKeyPair};
|
generateKeyPair,
|
||||||
|
verifyPrivateKey};
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
export {
|
export {
|
||||||
useCreateServiceAccount,
|
useCreateServiceAccount,
|
||||||
useCreateServiceAccountProjectLevelPermissions,
|
useCreateServiceAccountProjectLevelPermission,
|
||||||
useDeleteServiceAccount,
|
useDeleteServiceAccount,
|
||||||
useDeleteServiceAccountProjectLevelPermissions,
|
useDeleteServiceAccountProjectLevelPermission,
|
||||||
useGetServiceAccountById,
|
useGetServiceAccountById,
|
||||||
useGetServiceAccountProjectLevelPermissions,
|
useGetServiceAccountProjectLevelPermissions,
|
||||||
useGetServiceAccounts,
|
useGetServiceAccounts,
|
||||||
useRenameServiceAccount} from './queries';
|
useRenameServiceAccount
|
||||||
|
} from './queries';
|
||||||
@@ -5,14 +5,12 @@ import { apiRequest } from '@app/config/request';
|
|||||||
import {
|
import {
|
||||||
CreateServiceAccountDTO,
|
CreateServiceAccountDTO,
|
||||||
CreateServiceAccountRes,
|
CreateServiceAccountRes,
|
||||||
CreateServiceAccountWorkspacePermissionsDTO,
|
CreateServiceAccountWorkspacePermissionDTO,
|
||||||
DeleteServiceAccountRes,
|
DeleteServiceAccountWorkspacePermissionDTO,
|
||||||
DeleteServiceAccountWorkspacePermissionsDTO,
|
|
||||||
DeleteServiceAccountWorkspacePermissionsRes,
|
|
||||||
RenameServiceAccountDTO,
|
RenameServiceAccountDTO,
|
||||||
RenameServiceAccountRes,
|
|
||||||
ServiceAccount,
|
ServiceAccount,
|
||||||
ServiceAccountWorkspacePermissions} from './types';
|
ServiceAccountWorkspacePermission
|
||||||
|
} from './types';
|
||||||
|
|
||||||
const serviceAccountKeys = {
|
const serviceAccountKeys = {
|
||||||
getServiceAccountById: (serviceAccountId: string) => [{ serviceAccountId }, 'service-account'] as const,
|
getServiceAccountById: (serviceAccountId: string) => [{ serviceAccountId }, 'service-account'] as const,
|
||||||
@@ -68,7 +66,7 @@ export const useCreateServiceAccount = () => {
|
|||||||
export const useRenameServiceAccount = () => {
|
export const useRenameServiceAccount = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<RenameServiceAccountRes, {}, RenameServiceAccountDTO>({
|
return useMutation<ServiceAccount, {}, RenameServiceAccountDTO>({
|
||||||
mutationFn: async ({ serviceAccountId, name }) => {
|
mutationFn: async ({ serviceAccountId, name }) => {
|
||||||
const { data: { serviceAccount } } = await apiRequest.patch(`/api/v2/service-accounts/${serviceAccountId}/name`, { name });
|
const { data: { serviceAccount } } = await apiRequest.patch(`/api/v2/service-accounts/${serviceAccountId}/name`, { name });
|
||||||
return serviceAccount;
|
return serviceAccount;
|
||||||
@@ -83,7 +81,7 @@ export const useRenameServiceAccount = () => {
|
|||||||
export const useDeleteServiceAccount = () => {
|
export const useDeleteServiceAccount = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<DeleteServiceAccountRes, {}, string>({
|
return useMutation<ServiceAccount, {}, string>({
|
||||||
mutationFn: async (serviceAccountId) => {
|
mutationFn: async (serviceAccountId) => {
|
||||||
const { data: { serviceAccount } } = await apiRequest.delete(`/api/v2/service-accounts/${serviceAccountId}`);
|
const { data: { serviceAccount } } = await apiRequest.delete(`/api/v2/service-accounts/${serviceAccountId}`);
|
||||||
return serviceAccount;
|
return serviceAccount;
|
||||||
@@ -95,14 +93,11 @@ export const useDeleteServiceAccount = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchServiceAccountProjectLevelPermissions = async (serviceAccountId: string) => {
|
const fetchServiceAccountProjectLevelPermissions = async (serviceAccountId: string) => {
|
||||||
const { data: { permissions } } = await apiRequest.get<{ permissions: ServiceAccountWorkspacePermissions[] }>(
|
const { data: { serviceAccountWorkspacePermissions } } = await apiRequest.get<{ serviceAccountWorkspacePermissions: ServiceAccountWorkspacePermission[] }>(
|
||||||
`/api/v2/service-accounts/${serviceAccountId}/permissions/workspace`
|
`/api/v2/service-accounts/${serviceAccountId}/permissions/workspace`
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('fetchServiceAccountProjectLevelPermissions');
|
return serviceAccountWorkspacePermissions;
|
||||||
console.log('prrr: ', permissions);
|
|
||||||
|
|
||||||
return permissions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useGetServiceAccountProjectLevelPermissions = (serviceAccountId: string) => {
|
export const useGetServiceAccountProjectLevelPermissions = (serviceAccountId: string) => {
|
||||||
@@ -113,13 +108,13 @@ export const useGetServiceAccountProjectLevelPermissions = (serviceAccountId: st
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useCreateServiceAccountProjectLevelPermissions = () => {
|
export const useCreateServiceAccountProjectLevelPermission = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<CreateServiceAccountRes, {}, CreateServiceAccountWorkspacePermissionsDTO>({
|
return useMutation<ServiceAccountWorkspacePermission, {}, CreateServiceAccountWorkspacePermissionDTO>({
|
||||||
mutationFn: async (body) => {
|
mutationFn: async (body) => {
|
||||||
const { data: { permissions } } = await apiRequest.post(`/api/v2/service-accounts/${body.serviceAccountId}/permissions/workspace`, body);
|
const { data: { serviceAccountWorkspacePermission } } = await apiRequest.post(`/api/v2/service-accounts/${body.serviceAccountId}/permissions/workspace`, body);
|
||||||
return permissions;
|
return serviceAccountWorkspacePermission;
|
||||||
},
|
},
|
||||||
onSuccess: ({ serviceAccount }) => {
|
onSuccess: ({ serviceAccount }) => {
|
||||||
queryClient.invalidateQueries(serviceAccountKeys.getServiceAccountProjectLevelPermissions(serviceAccount));
|
queryClient.invalidateQueries(serviceAccountKeys.getServiceAccountProjectLevelPermissions(serviceAccount));
|
||||||
@@ -127,20 +122,16 @@ export const useCreateServiceAccountProjectLevelPermissions = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useDeleteServiceAccountProjectLevelPermissions = () => {
|
export const useDeleteServiceAccountProjectLevelPermission = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<DeleteServiceAccountWorkspacePermissionsRes, {}, DeleteServiceAccountWorkspacePermissionsDTO>({
|
return useMutation<ServiceAccountWorkspacePermission, {}, DeleteServiceAccountWorkspacePermissionDTO>({
|
||||||
mutationFn: async ({ serviceAccountId, serviceAccountWorkspacePermissionsId }) => {
|
mutationFn: async ({ serviceAccountId, serviceAccountWorkspacePermissionId }) => {
|
||||||
const { data: { permissions } } = await apiRequest.delete(`/api/v2/service-accounts/${serviceAccountId}/permissions/workspace/${serviceAccountWorkspacePermissionsId}`);
|
const { data: { serviceAccountWorkspacePermission} } = await apiRequest.delete(`/api/v2/service-accounts/${serviceAccountId}/permissions/workspace/${serviceAccountWorkspacePermissionId}`);
|
||||||
console.log('useDeleteServiceAccountProjectLevelPermissions');
|
return serviceAccountWorkspacePermission;
|
||||||
console.log('permissions: ', permissions);
|
|
||||||
return permissions;
|
|
||||||
},
|
},
|
||||||
onSuccess: ({ serviceAccount }) => {
|
onSuccess: (serviceAccountWorkspacePermission) => {
|
||||||
console.log('onSuccess3: ', serviceAccount);
|
queryClient.invalidateQueries(serviceAccountKeys.getServiceAccountProjectLevelPermissions(serviceAccountWorkspacePermission.serviceAccount));
|
||||||
queryClient.invalidateQueries(serviceAccountKeys.getServiceAccountProjectLevelPermissions(serviceAccount));
|
|
||||||
// queryClient.invalidateQueries(serviceAccountKeys.getServiceAccounts(organization));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -19,20 +19,13 @@ export type CreateServiceAccountRes = {
|
|||||||
serviceAccountAccessKey: string;
|
serviceAccountAccessKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DeleteServiceAccountRes = {
|
|
||||||
serviceAccount: ServiceAccount;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RenameServiceAccountDTO = {
|
export type RenameServiceAccountDTO = {
|
||||||
serviceAccountId: string;
|
serviceAccountId: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RenameServiceAccountRes = {
|
export type ServiceAccountWorkspacePermission = {
|
||||||
serviceAccount: ServiceAccount;
|
_id: string;
|
||||||
}
|
|
||||||
|
|
||||||
export type ServiceAccountWorkspacePermissions = {
|
|
||||||
serviceAccount: string;
|
serviceAccount: string;
|
||||||
workspace: string;
|
workspace: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
@@ -42,7 +35,7 @@ export type ServiceAccountWorkspacePermissions = {
|
|||||||
canDelete: boolean;
|
canDelete: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateServiceAccountWorkspacePermissionsDTO = {
|
export type CreateServiceAccountWorkspacePermissionDTO = {
|
||||||
serviceAccountId: string;
|
serviceAccountId: string;
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
@@ -50,17 +43,11 @@ export type CreateServiceAccountWorkspacePermissionsDTO = {
|
|||||||
canWrite: boolean;
|
canWrite: boolean;
|
||||||
canUpdate: boolean;
|
canUpdate: boolean;
|
||||||
canDelete: boolean;
|
canDelete: boolean;
|
||||||
|
encryptedKey: string;
|
||||||
|
nonce: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CreateServiceAccountWorkspacePermissionsRes = {
|
export type DeleteServiceAccountWorkspacePermissionDTO = {
|
||||||
permissions: ServiceAccountWorkspacePermissions
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DeleteServiceAccountWorkspacePermissionsDTO = {
|
|
||||||
serviceAccountId: string;
|
serviceAccountId: string;
|
||||||
serviceAccountWorkspacePermissionsId: string;
|
serviceAccountWorkspacePermissionId: string;
|
||||||
}
|
|
||||||
|
|
||||||
export type DeleteServiceAccountWorkspacePermissionsRes = {
|
|
||||||
permissions: ServiceAccountWorkspacePermissions
|
|
||||||
}
|
}
|
||||||
@@ -136,8 +136,13 @@ export const AppLayout = ({ children }: LayoutProps) => {
|
|||||||
} else if (pathSegments.length >= 3 && pathSegments[0] === 'settings') {
|
} else if (pathSegments.length >= 3 && pathSegments[0] === 'settings') {
|
||||||
[, , intendedWorkspaceId] = pathSegments;
|
[, , intendedWorkspaceId] = pathSegments;
|
||||||
} else {
|
} else {
|
||||||
const lastPathSegment = router.asPath.split('/').pop().split('?');
|
const lastPathSegments = router.asPath.split('/').pop();
|
||||||
[intendedWorkspaceId] = lastPathSegment;
|
if (lastPathSegments !== undefined) {
|
||||||
|
[intendedWorkspaceId] = lastPathSegments.split('?');
|
||||||
|
}
|
||||||
|
|
||||||
|
// const lastPathSegment = router.asPath.split('/').pop().split('?');
|
||||||
|
// [intendedWorkspaceId] = lastPathSegment;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!intendedWorkspaceId) return;
|
if (!intendedWorkspaceId) return;
|
||||||
|
|||||||
@@ -1,22 +1,13 @@
|
|||||||
import SecurityClient from '@app/components/utilities/SecurityClient';
|
import { apiRequest } from '@app/config/request';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the latest key pairs from a certain workspace
|
* Get the latest key pairs from a certain workspace
|
||||||
* @param {string} workspaceId
|
* @param {string} workspaceId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const getLatestFileKey = ({ workspaceId }: { workspaceId: string }) =>
|
const getLatestFileKey = async ({ workspaceId }: { workspaceId: string }) => {
|
||||||
SecurityClient.fetchCall(`/api/v1/key/${workspaceId}/latest`, {
|
const { data } = await apiRequest.get(`/api/v1/key/${workspaceId}/latest`);
|
||||||
method: 'GET',
|
return data;
|
||||||
headers: {
|
}
|
||||||
'Content-Type': 'application/json'
|
|
||||||
}
|
|
||||||
}).then(async (res) => {
|
|
||||||
if (res?.status === 200) {
|
|
||||||
return res.json();
|
|
||||||
}
|
|
||||||
console.log('Failed to get the latest key pairs for a certain project');
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getLatestFileKey;
|
export default getLatestFileKey;
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
import { getTranslatedServerSideProps } from '@app/components/utilities/withTranslateProps';
|
import { getTranslatedServerSideProps } from '@app/components/utilities/withTranslateProps';
|
||||||
import { CreateServiceAccountPage } from '@app/views/Settings/CreateServiceAccountPage';
|
import { CreateServiceAccountPage } from '@app/views/Settings/CreateServiceAccountPage';
|
||||||
|
|
||||||
export default function ServiceAccountPage() {
|
export default function ServiceAccountPage() {
|
||||||
const router = useRouter();
|
|
||||||
// const { orgId, serviceAccountId } = router.query;
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Edit Service Account</title>
|
<title>Edit Service Account</title>
|
||||||
<link rel="icon" href="/infisical.ico" />
|
<link rel="icon" href="/infisical.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<div />
|
|
||||||
<CreateServiceAccountPage />
|
<CreateServiceAccountPage />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -25,7 +19,7 @@ export default function ServiceAccountPage() {
|
|||||||
|
|
||||||
ServiceAccountPage.requireAuth = true;
|
ServiceAccountPage.requireAuth = true;
|
||||||
|
|
||||||
export const getServerSidePros = getTranslatedServerSideProps([
|
export const getServerSideProps = getTranslatedServerSideProps([
|
||||||
'settings',
|
'settings',
|
||||||
'settings-org',
|
'settings-org',
|
||||||
'section-incident',
|
'section-incident',
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import { useRouter } from 'next/router';
|
|||||||
import NavHeader from '@app/components/navigation/NavHeader';
|
import NavHeader from '@app/components/navigation/NavHeader';
|
||||||
|
|
||||||
import { SAProjectLevelPermissionsTable } from './components/SAProjectLevelPermissionsTable';
|
import { SAProjectLevelPermissionsTable } from './components/SAProjectLevelPermissionsTable';
|
||||||
import { ServiceAccountNameChangeSection } from './components';
|
import {
|
||||||
|
CopyServiceAccountIDSection,
|
||||||
|
ServiceAccountNameChangeSection} from './components';
|
||||||
|
|
||||||
export const CreateServiceAccountPage = () => {
|
export const CreateServiceAccountPage = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { serviceAccountId }: { serviceAccountId: string } = router.query;
|
const {serviceAccountId} = router.query;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
@@ -21,23 +23,23 @@ export const CreateServiceAccountPage = () => {
|
|||||||
A service account represents a machine identity such as a VM or application client.
|
A service account represents a machine identity such as a VM or application client.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-8xl mx-6">
|
{typeof serviceAccountId === 'string' && (
|
||||||
{typeof serviceAccountId === 'string' && (
|
<div className="max-w-8xl mx-6">
|
||||||
<ServiceAccountNameChangeSection
|
<ServiceAccountNameChangeSection
|
||||||
serviceAccountId={serviceAccountId}
|
serviceAccountId={serviceAccountId}
|
||||||
/>
|
/>
|
||||||
)}
|
<div className="mt-8">
|
||||||
{/* <div className="rounded-md bg-white/5 p-6 mt-6">
|
<CopyServiceAccountIDSection
|
||||||
<p className="mb-4 text-xl font-semibold">Organization-Level Permissions</p>
|
serviceAccountId={serviceAccountId}
|
||||||
<SAProjectLevelPermissionsTable />
|
/>
|
||||||
</div> */}
|
</div>
|
||||||
<div className="rounded-md bg-white/5 p-6 mt-6">
|
<div className="mt-8">
|
||||||
<p className="mb-4 text-xl font-semibold">Project-Level Permissions</p>
|
<SAProjectLevelPermissionsTable
|
||||||
<SAProjectLevelPermissionsTable
|
serviceAccountId={serviceAccountId}
|
||||||
serviceAccountId={serviceAccountId}
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { faCheck, faCopy } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
|
import { IconButton } from '@app/components/v2';
|
||||||
|
import { useToggle } from '@app/hooks';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
serviceAccountId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CopyServiceAccountIDSection = ({ serviceAccountId }: Props): JSX.Element => {
|
||||||
|
const [isServiceAccountIdCopied, setIsServiceAccountIdCopied] = useToggle(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let timer: NodeJS.Timeout;
|
||||||
|
|
||||||
|
if (isServiceAccountIdCopied) {
|
||||||
|
timer = setTimeout(() => setIsServiceAccountIdCopied.off(), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, [isServiceAccountIdCopied]);
|
||||||
|
|
||||||
|
const copyServiceAccountIdToClipboard = () => {
|
||||||
|
navigator.clipboard.writeText(serviceAccountId);
|
||||||
|
setIsServiceAccountIdCopied.on();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex w-full flex-col items-start rounded-md bg-white/5 px-6 p-6">
|
||||||
|
<p className="text-xl font-semibold">Service Account ID</p>
|
||||||
|
<div className="mt-4 flex items-center justify-end rounded-md bg-white/[0.07] text-base text-gray-400 p-2">
|
||||||
|
<p className="mr-4 break-all">{serviceAccountId}</p>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="copy icon"
|
||||||
|
colorSchema="secondary"
|
||||||
|
className="group relative"
|
||||||
|
onClick={() => copyServiceAccountIdToClipboard()}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={isServiceAccountIdCopied ? faCheck : faCopy} />
|
||||||
|
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-bunker-800 py-2 pl-3 text-center text-sm text-gray-400 group-hover:flex group-hover:animate-fadeIn">
|
||||||
|
Copy
|
||||||
|
</span>
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export { CopyServiceAccountIDSection } from './CopyServiceAccountIDSection';
|
||||||
+85
-29
@@ -9,6 +9,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import * as yup from 'yup';
|
import * as yup from 'yup';
|
||||||
|
|
||||||
|
import {
|
||||||
|
decryptAssymmetric,
|
||||||
|
encryptAssymmetric,
|
||||||
|
verifyPrivateKey} from '@app/components/utilities/cryptography/crypto';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
@@ -32,12 +36,15 @@ import {
|
|||||||
Tr} from '@app/components/v2';
|
Tr} from '@app/components/v2';
|
||||||
import { usePopUp } from '@app/hooks';
|
import { usePopUp } from '@app/hooks';
|
||||||
import {
|
import {
|
||||||
useCreateServiceAccountProjectLevelPermissions,
|
useCreateServiceAccountProjectLevelPermission,
|
||||||
useDeleteServiceAccountProjectLevelPermissions,
|
useDeleteServiceAccountProjectLevelPermission,
|
||||||
|
useGetServiceAccountById,
|
||||||
useGetServiceAccountProjectLevelPermissions,
|
useGetServiceAccountProjectLevelPermissions,
|
||||||
useGetUserWorkspaces} from '@app/hooks/api';
|
useGetUserWorkspaces} from '@app/hooks/api';
|
||||||
|
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
||||||
|
|
||||||
const createProjectLevelPermissionSchema = yup.object({
|
const createProjectLevelPermissionSchema = yup.object({
|
||||||
|
privateKey: yup.string().required().label('Private Key'),
|
||||||
workspace: yup.string().required().label('Workspace'),
|
workspace: yup.string().required().label('Workspace'),
|
||||||
environment: yup.string().required().label('Environment'),
|
environment: yup.string().required().label('Environment'),
|
||||||
permissions: yup.object().shape({
|
permissions: yup.object().shape({
|
||||||
@@ -57,18 +64,19 @@ type Props = {
|
|||||||
export const SAProjectLevelPermissionsTable = ({
|
export const SAProjectLevelPermissionsTable = ({
|
||||||
serviceAccountId
|
serviceAccountId
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
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 [defaultValues, setDefaultValues] = useState<CreateProjectLevelPermissionForm | undefined>(undefined);
|
||||||
|
|
||||||
const { data: permissions, isLoading: isPermissionsLoading } = useGetServiceAccountProjectLevelPermissions(serviceAccountId);
|
const { data: serviceAccountWorkspacePermissions, isLoading: isPermissionsLoading } = useGetServiceAccountProjectLevelPermissions(serviceAccountId);
|
||||||
|
|
||||||
const createServiceAccountProjectLevelPermissions = useCreateServiceAccountProjectLevelPermissions();
|
const createServiceAccountProjectLevelPermission = useCreateServiceAccountProjectLevelPermission();
|
||||||
const deleteServiceAccountProjectLevelPermissions = useDeleteServiceAccountProjectLevelPermissions();
|
const deleteServiceAccountProjectLevelPermission = useDeleteServiceAccountProjectLevelPermission();
|
||||||
|
|
||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
'addProjectLevelPermissions',
|
'addProjectLevelPermission',
|
||||||
'removeProjectLevelPermissions',
|
'removeProjectLevelPermission',
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -78,35 +86,68 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
} = useForm<CreateProjectLevelPermissionForm>({ resolver: yupResolver(createProjectLevelPermissionSchema), defaultValues })
|
} = useForm<CreateProjectLevelPermissionForm>({ resolver: yupResolver(createProjectLevelPermissionSchema), defaultValues })
|
||||||
|
|
||||||
const onAddProjectLevelPermissions = async ({
|
const onAddProjectLevelPermission = async ({
|
||||||
|
privateKey,
|
||||||
workspace,
|
workspace,
|
||||||
environment,
|
environment,
|
||||||
permissions: { canRead, canWrite, canUpdate, canDelete }
|
permissions: { canRead, canWrite, canUpdate, canDelete }
|
||||||
}: CreateProjectLevelPermissionForm) => {
|
}: CreateProjectLevelPermissionForm) => {
|
||||||
await createServiceAccountProjectLevelPermissions.mutateAsync({
|
|
||||||
|
// TODO: clean up / modularize this function
|
||||||
|
|
||||||
|
if (!serviceAccount) return;
|
||||||
|
|
||||||
|
const { latestKey } = await getLatestFileKey({
|
||||||
|
workspaceId: workspace
|
||||||
|
});
|
||||||
|
|
||||||
|
verifyPrivateKey({
|
||||||
|
privateKey,
|
||||||
|
publicKey: serviceAccount.publicKey
|
||||||
|
});
|
||||||
|
|
||||||
|
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||||
|
|
||||||
|
const key = decryptAssymmetric({
|
||||||
|
ciphertext: latestKey.encryptedKey,
|
||||||
|
nonce: latestKey.nonce,
|
||||||
|
publicKey: latestKey.sender.publicKey,
|
||||||
|
privateKey: PRIVATE_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
const { ciphertext, nonce } = encryptAssymmetric({
|
||||||
|
plaintext: key,
|
||||||
|
publicKey: serviceAccount.publicKey,
|
||||||
|
privateKey: PRIVATE_KEY
|
||||||
|
});
|
||||||
|
|
||||||
|
await createServiceAccountProjectLevelPermission.mutateAsync({
|
||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
workspaceId: workspace,
|
workspaceId: workspace,
|
||||||
environment,
|
environment,
|
||||||
canRead,
|
canRead,
|
||||||
canWrite,
|
canWrite,
|
||||||
canUpdate,
|
canUpdate,
|
||||||
canDelete
|
canDelete,
|
||||||
|
encryptedKey: ciphertext,
|
||||||
|
nonce
|
||||||
});
|
});
|
||||||
handlePopUpClose('addProjectLevelPermissions');
|
handlePopUpClose('addProjectLevelPermission');
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRemoveProjectLevelPermissions = async () => {
|
const onRemoveProjectLevelPermission = async () => {
|
||||||
const serviceAccountWorkspacePermissionsId = (popUp?.removeProjectLevelPermissions?.data as { _id: string })?._id;
|
const serviceAccountWorkspacePermissionId = (popUp?.removeProjectLevelPermission?.data as { _id: string })?._id;
|
||||||
await deleteServiceAccountProjectLevelPermissions.mutateAsync({
|
await deleteServiceAccountProjectLevelPermission.mutateAsync({
|
||||||
serviceAccountId,
|
serviceAccountId,
|
||||||
serviceAccountWorkspacePermissionsId
|
serviceAccountWorkspacePermissionId
|
||||||
});
|
});
|
||||||
handlePopUpClose('removeProjectLevelPermissions');
|
handlePopUpClose('removeProjectLevelPermission');
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userWorkspaces) {
|
if (userWorkspaces) {
|
||||||
setDefaultValues({
|
setDefaultValues({
|
||||||
|
privateKey: '',
|
||||||
workspace: String(userWorkspaces?.[0]?._id),
|
workspace: String(userWorkspaces?.[0]?._id),
|
||||||
environment: String(userWorkspaces?.[0]?.environments?.[0]?.slug),
|
environment: String(userWorkspaces?.[0]?.environments?.[0]?.slug),
|
||||||
permissions: {
|
permissions: {
|
||||||
@@ -118,10 +159,10 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [userWorkspaces]);
|
}, [userWorkspaces]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full bg-white/5 p-6">
|
||||||
|
<p className="mb-4 text-xl font-semibold">Project-Level Permissions</p>
|
||||||
<div className="mb-4 flex">
|
<div className="mb-4 flex">
|
||||||
<div className="mr-4 flex-1">
|
<div className="mr-4 flex-1">
|
||||||
<Input
|
<Input
|
||||||
@@ -134,7 +175,7 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
<Button
|
<Button
|
||||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handlePopUpOpen('addProjectLevelPermissions')
|
handlePopUpOpen('addProjectLevelPermission')
|
||||||
reset();
|
reset();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -156,8 +197,8 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
</THead>
|
</THead>
|
||||||
<TBody>
|
<TBody>
|
||||||
{isPermissionsLoading && <TableSkeleton columns={6} key="service-account-project-level-permissions" />}
|
{isPermissionsLoading && <TableSkeleton columns={6} key="service-account-project-level-permissions" />}
|
||||||
{!isPermissionsLoading && permissions && (
|
{!isPermissionsLoading && serviceAccountWorkspacePermissions && (
|
||||||
permissions.map(({
|
serviceAccountWorkspacePermissions.map(({
|
||||||
_id,
|
_id,
|
||||||
workspace,
|
workspace,
|
||||||
environment,
|
environment,
|
||||||
@@ -196,13 +237,14 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
id="isDeletePermissionEnabled"
|
id="isDeletePermissionEnabled"
|
||||||
isChecked={canDelete}
|
isChecked={canDelete}
|
||||||
|
isDisabled
|
||||||
/>
|
/>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<IconButton
|
<IconButton
|
||||||
ariaLabel="delete"
|
ariaLabel="delete"
|
||||||
colorSchema="danger"
|
colorSchema="danger"
|
||||||
onClick={() => handlePopUpOpen('removeProjectLevelPermissions', { _id })}
|
onClick={() => handlePopUpOpen('removeProjectLevelPermission', { _id })}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faTrash} />
|
<FontAwesomeIcon icon={faTrash} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@@ -211,7 +253,7 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
)}
|
)}
|
||||||
{!isPermissionsLoading && permissions?.length === 0 && (
|
{!isPermissionsLoading && serviceAccountWorkspacePermissions?.length === 0 && (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td colSpan={7} className="py-6 text-center text-bunker-400">
|
<Td colSpan={7} className="py-6 text-center text-bunker-400">
|
||||||
<EmptyState title="No permissions found" icon={faKey} />
|
<EmptyState title="No permissions found" icon={faKey} />
|
||||||
@@ -222,18 +264,32 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={popUp?.addProjectLevelPermissions?.isOpen}
|
isOpen={popUp?.addProjectLevelPermission?.isOpen}
|
||||||
onOpenChange={(isOpen) => {
|
onOpenChange={(isOpen) => {
|
||||||
handlePopUpToggle('addProjectLevelPermissions', isOpen);
|
handlePopUpToggle('addProjectLevelPermission', isOpen);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Add a Project-Level Permission"
|
title="Add a Project-Level Permission"
|
||||||
subTitle="The service account will be granted scoped access to the specified project and environment"
|
subTitle="The service account will be granted scoped access to the specified project and environment"
|
||||||
>
|
>
|
||||||
<form onSubmit={handleSubmit(onAddProjectLevelPermissions)}>
|
<form onSubmit={handleSubmit(onAddProjectLevelPermission)}>
|
||||||
{!isUserWorkspacesLoading && userWorkspaces && (
|
{!isUserWorkspacesLoading && userWorkspaces && (
|
||||||
<>
|
<>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="privateKey"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Service Account Private Key"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input {...field} />
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="workspace"
|
name="workspace"
|
||||||
@@ -383,11 +439,11 @@ export const SAProjectLevelPermissionsTable = ({
|
|||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.removeProjectLevelPermissions.isOpen}
|
isOpen={popUp.removeProjectLevelPermission.isOpen}
|
||||||
deleteKey="remove"
|
deleteKey="remove"
|
||||||
title="Do you want to remove this permission from the service account?"
|
title="Do you want to remove this permission from the service account?"
|
||||||
onChange={(isOpen) => handlePopUpToggle('removeProjectLevelPermissions', isOpen)}
|
onChange={(isOpen) => handlePopUpToggle('removeProjectLevelPermission', isOpen)}
|
||||||
onDeleteApproved={onRemoveProjectLevelPermissions}
|
onDeleteApproved={onRemoveProjectLevelPermission}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
|
export { CopyServiceAccountIDSection } from './CopyServiceAccountIDSection';
|
||||||
export { SAProjectLevelPermissionsTable } from './SAProjectLevelPermissionsTable';
|
export { SAProjectLevelPermissionsTable } from './SAProjectLevelPermissionsTable';
|
||||||
export { ServiceAccountNameChangeSection } from './ServiceAccountNameChangeSection';
|
export { ServiceAccountNameChangeSection } from './ServiceAccountNameChangeSection';
|
||||||
-10
@@ -62,8 +62,6 @@ export const OrgServiceAccountsTable = () => {
|
|||||||
const { currentOrg } = useOrganization();
|
const { currentOrg } = useOrganization();
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
|
|
||||||
console.log('currentWorkspace: ', currentWorkspace);
|
|
||||||
|
|
||||||
const orgId = currentOrg?._id || '';
|
const orgId = currentOrg?._id || '';
|
||||||
const [step, setStep] = useState(0);
|
const [step, setStep] = useState(0);
|
||||||
const [isAccessKeyCopied, setIsAccessKeyCopied] = useToggle(false);
|
const [isAccessKeyCopied, setIsAccessKeyCopied] = useToggle(false);
|
||||||
@@ -114,8 +112,6 @@ export const OrgServiceAccountsTable = () => {
|
|||||||
expiresIn
|
expiresIn
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('serviceAccountDetails: ', serviceAccountDetails);
|
|
||||||
|
|
||||||
setAccessKey(serviceAccountDetails.serviceAccountAccessKey);
|
setAccessKey(serviceAccountDetails.serviceAccountAccessKey);
|
||||||
|
|
||||||
setStep(1);
|
setStep(1);
|
||||||
@@ -123,11 +119,7 @@ export const OrgServiceAccountsTable = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onRemoveServiceAccount = async () => {
|
const onRemoveServiceAccount = async () => {
|
||||||
console.log('onRemoveServiceAccount');
|
|
||||||
|
|
||||||
const serviceAccountId = (popUp?.removeServiceAccount?.data as { _id: string })?._id;
|
const serviceAccountId = (popUp?.removeServiceAccount?.data as { _id: string })?._id;
|
||||||
console.log('serviceAccountId: ', serviceAccountId);
|
|
||||||
|
|
||||||
await removeServiceAccount.mutateAsync(serviceAccountId);
|
await removeServiceAccount.mutateAsync(serviceAccountId);
|
||||||
handlePopUpClose('removeServiceAccount');
|
handlePopUpClose('removeServiceAccount');
|
||||||
}
|
}
|
||||||
@@ -247,8 +239,6 @@ export const OrgServiceAccountsTable = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('serviceAccounts: ', serviceAccounts);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="mb-4 flex">
|
<div className="mb-4 flex">
|
||||||
|
|||||||
Reference in New Issue
Block a user