diff --git a/backend/src/controllers/v2/organizationsController.ts b/backend/src/controllers/v2/organizationsController.ts index a1bc7bdd3..4c6a72823 100644 --- a/backend/src/controllers/v2/organizationsController.ts +++ b/backend/src/controllers/v2/organizationsController.ts @@ -1,9 +1,9 @@ import { Request, Response } from "express"; import { Types } from "mongoose"; import { + MachineMembershipOrg, Membership, - MembershipOrg, - ServiceMembershipOrg, + MembershipOrg, Workspace } from "../../models"; import { Role } from "../../ee/models"; @@ -389,16 +389,16 @@ export const deleteOrganizationById = async (req: Request, res: Response) => { * @param res * @returns */ -export const getOrganizationServiceMemberships = async (req: Request, res: Response) => { +export const getOrganizationMachineMemberships = async (req: Request, res: Response) => { const { params: { organizationId } } = await validateRequest(reqValidator.GetOrgServiceMembersV2, req); - const serviceMemberships = await ServiceMembershipOrg.find({ + const machineMemberships = await MachineMembershipOrg.find({ organization: new Types.ObjectId(organizationId) - }).populate("service customRole"); - + }).populate("machineIdentity customRole"); + return res.status(200).send({ - serviceMemberships + machineMemberships }); } \ No newline at end of file diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts index f7daff0db..279f957ae 100644 --- a/backend/src/controllers/v2/workspaceController.ts +++ b/backend/src/controllers/v2/workspaceController.ts @@ -2,10 +2,10 @@ import { Request, Response } from "express"; import { Types } from "mongoose"; import { Key, - Membership, - ServiceMembership, + MachineIdentity, + MachineMembership, + Membership, ServiceTokenData, - ServiceTokenDataV3, Workspace } from "../../models"; import { Role } from "../../ee/models"; @@ -503,16 +503,15 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => { }; /** - * Add service account with id [serviceId] to workspace + * Add machine identity with id [machineId] to workspace * with id [workspaceId] * @param req * @param res */ -export const addWorkspaceServiceMembership = async (req: Request, res: Response) => { +export const addMachineToWorkspace = async (req: Request, res: Response) => { const { - params: { workspaceId }, + params: { workspaceId, machineId }, body: { - serviceId, role } } = await validateRequest(reqValidator.AddWorkspaceServiceMemberV2, req); @@ -527,22 +526,22 @@ export const addWorkspaceServiceMembership = async (req: Request, res: Response) ProjectPermissionSub.ServiceTokens ); - let serviceMembership = await ServiceMembership.findOne({ - service: new Types.ObjectId(serviceId), + let machineMembership = await MachineMembership.findOne({ + machineIdentity: new Types.ObjectId(machineId), workspace: new Types.ObjectId(workspaceId) }); - if (serviceMembership) throw BadRequestError({ + if (machineMembership) throw BadRequestError({ message: "Service account already exists in workspace" }); - const serviceTokenData = await ServiceTokenDataV3.findById(serviceId); - if (!serviceTokenData) throw ResourceNotFoundError(); + const machineIdentity = await MachineIdentity.findById(machineId); + if (!machineIdentity) throw ResourceNotFoundError(); const workspace = await Workspace.findById(workspaceId); if (!workspace) throw ResourceNotFoundError(); - if (!serviceTokenData.organization.equals(workspace.organization)) throw BadRequestError({ + if (!machineIdentity.organization.equals(workspace.organization)) throw BadRequestError({ message: "Failed to add service account to workspace in another organization" }); @@ -560,27 +559,27 @@ export const addWorkspaceServiceMembership = async (req: Request, res: Response) } } - serviceMembership = await new ServiceMembership({ - service: serviceTokenData._id, + machineMembership = await new MachineMembership({ + machineIdentity: machineIdentity._id, workspace: new Types.ObjectId(workspaceId), role, customRole }).save(); return res.status(200).send({ - serviceMembership + machineMembership }); } /** - * Add service account with id [serviceId] to workspace + * Add service account with id [machineId] to workspace * with id [workspaceId] * @param req * @param res */ - export const deleteWorkspaceServiceMembership = async (req: Request, res: Response) => { + export const deleteMachineFromWorkspace = async (req: Request, res: Response) => { const { - params: { workspaceId, serviceId } + params: { workspaceId, machineId } } = await validateRequest(reqValidator.DeleteWorkspaceServiceMemberV2, req); const { permission } = await getAuthDataProjectPermissions({ @@ -593,29 +592,29 @@ export const addWorkspaceServiceMembership = async (req: Request, res: Response) ProjectPermissionSub.ServiceTokens ); - const serviceMembership = await ServiceMembership.findOneAndDelete({ - service: new Types.ObjectId(serviceId), + const machineMembership = await MachineMembership.findOneAndDelete({ + machineIdentity: new Types.ObjectId(machineId), workspace: new Types.ObjectId(workspaceId) }); - if (!serviceMembership) throw ResourceNotFoundError(); + if (!machineMembership) throw ResourceNotFoundError(); return res.status(200).send({ - serviceMembership + machineMembership }); } /** - * Return list of service memberships for workspace with id [workspaceId] + * Return list of machine identity memberships for workspace with id [workspaceId] * @param req * @param res * @returns */ - export const getWorkspaceServiceMemberships = async (req: Request, res: Response) => { + export const getWorkspaceMachineMemberships = async (req: Request, res: Response) => { const { params: { workspaceId } } = await validateRequest(reqValidator.GetWorkspaceServiceMembersV2, req); - + const { permission } = await getAuthDataProjectPermissions({ authData: req.authData, workspaceId: new Types.ObjectId(workspaceId) @@ -626,11 +625,11 @@ export const addWorkspaceServiceMembership = async (req: Request, res: Response) ProjectPermissionSub.ServiceTokens ); - const serviceMemberships = await ServiceMembership.find({ + const machineMemberships = await MachineMembership.find({ workspace: new Types.ObjectId(workspaceId) - }).populate("service customRole"); + }).populate("machineIdentity customRole"); return res.status(200).send({ - serviceMemberships + machineMemberships }); } \ No newline at end of file diff --git a/backend/src/ee/controllers/v1/workspaceController.ts b/backend/src/ee/controllers/v1/workspaceController.ts index fd068373b..68d8c541b 100644 --- a/backend/src/ee/controllers/v1/workspaceController.ts +++ b/backend/src/ee/controllers/v1/workspaceController.ts @@ -2,10 +2,10 @@ import { Request, Response } from "express"; import { PipelineStage, Types } from "mongoose"; import { Folder, + MachineIdentity, Membership, Secret, ServiceTokenData, - ServiceTokenDataV3, TFolderSchema, User, Workspace @@ -758,14 +758,14 @@ export const getWorkspaceAuditLogActorFilterOpts = async (req: Request, res: Res })); const serviceV3Actors: ServiceActorV3[] = ( - await ServiceTokenDataV3.find({ + await MachineIdentity.find({ workspace: new Types.ObjectId(workspaceId) }) - ).map((serviceTokenData) => ({ + ).map((machineIdentity) => ({ type: ActorType.SERVICE_V3, metadata: { - serviceId: serviceTokenData._id.toString(), - name: serviceTokenData.name + serviceId: machineIdentity._id.toString(), + name: machineIdentity.name } })); diff --git a/backend/src/ee/controllers/v3/index.ts b/backend/src/ee/controllers/v3/index.ts index 454e0c446..72706ea6e 100644 --- a/backend/src/ee/controllers/v3/index.ts +++ b/backend/src/ee/controllers/v3/index.ts @@ -1,7 +1,7 @@ -import * as serviceTokenDataController from "./serviceTokenDataController"; +import * as machineIdentityController from "./machineIdentityController"; import * as apiKeyDataController from "./apiKeyDataController"; export { - serviceTokenDataController, + machineIdentityController, apiKeyDataController } \ No newline at end of file diff --git a/backend/src/ee/controllers/v3/serviceTokenDataController.ts b/backend/src/ee/controllers/v3/machineIdentityController.ts similarity index 70% rename from backend/src/ee/controllers/v3/serviceTokenDataController.ts rename to backend/src/ee/controllers/v3/machineIdentityController.ts index f3f1874d1..96292962c 100644 --- a/backend/src/ee/controllers/v3/serviceTokenDataController.ts +++ b/backend/src/ee/controllers/v3/machineIdentityController.ts @@ -2,20 +2,19 @@ import jwt from "jsonwebtoken"; import { Request, Response } from "express"; import { Types } from "mongoose"; import { - ServiceTokenDataV3, - ServiceTokenDataV3Key, // TODO: remove + IMachineIdentityTrustedIp, + MachineIdentity, + MachineMembership, + MachineMembershipOrg, Organization, - ServiceMembershipOrg, - ServiceMembership } from "../../../models"; -import { IServiceTokenV3TrustedIp } from "../../../models/serviceTokenDataV3"; import { ActorType, EventType, Role } from "../../models"; import { validateRequest } from "../../../helpers/validation"; -import * as reqValidator from "../../../validation/serviceTokenDataV3"; +import * as reqValidator from "../../../validation/machineIdentity"; import { createToken } from "../../../helpers/auth"; @@ -26,7 +25,7 @@ import { getAuthSecret } from "../../../config"; import { ADMIN, AuthTokenType, CUSTOM, MEMBER } from "../../../variables"; /** - * Return access and refresh token as per refresh operation + * Return machine identity access and refresh token as per refresh operation * @param req * @param res */ @@ -43,14 +42,14 @@ import { ADMIN, AuthTokenType, CUSTOM, MEMBER } from "../../../variables"; if (decodedToken.authTokenType !== AuthTokenType.SERVICE_REFRESH_TOKEN) throw UnauthorizedRequestError(); - let serviceTokenData = await ServiceTokenDataV3.findOne({ + let machineIdentity = await MachineIdentity.findOne({ _id: new Types.ObjectId(decodedToken.serviceTokenDataId), isActive: true }); - if (!serviceTokenData) throw UnauthorizedRequestError(); + if (!machineIdentity) throw UnauthorizedRequestError(); - if (decodedToken.tokenVersion !== serviceTokenData.tokenVersion) { + if (decodedToken.tokenVersion !== machineIdentity.tokenVersion) { // raise alarm throw UnauthorizedRequestError(); } @@ -67,9 +66,9 @@ import { ADMIN, AuthTokenType, CUSTOM, MEMBER } from "../../../variables"; token_type: "Bearer" }; - if (serviceTokenData.isRefreshTokenRotationEnabled) { - serviceTokenData = await ServiceTokenDataV3.findByIdAndUpdate( - serviceTokenData._id, + if (machineIdentity.isRefreshTokenRotationEnabled) { + machineIdentity = await MachineIdentity.findByIdAndUpdate( + machineIdentity._id, { $inc: { tokenVersion: 1 @@ -80,13 +79,13 @@ import { ADMIN, AuthTokenType, CUSTOM, MEMBER } from "../../../variables"; } ); - if (!serviceTokenData) throw BadRequestError(); + if (!machineIdentity) throw BadRequestError(); response.refresh_token = createToken({ payload: { - serviceTokenDataId: serviceTokenData._id.toString(), + serviceTokenDataId: machineIdentity._id.toString(), authTokenType: AuthTokenType.SERVICE_REFRESH_TOKEN, - tokenVersion: serviceTokenData.tokenVersion + tokenVersion: machineIdentity.tokenVersion }, secret: await getAuthSecret() }); @@ -94,18 +93,18 @@ import { ADMIN, AuthTokenType, CUSTOM, MEMBER } from "../../../variables"; response.access_token = createToken({ payload: { - serviceTokenDataId: serviceTokenData._id.toString(), + serviceTokenDataId: machineIdentity._id.toString(), authTokenType: AuthTokenType.SERVICE_ACCESS_TOKEN, - tokenVersion: serviceTokenData.tokenVersion + tokenVersion: machineIdentity.tokenVersion }, - expiresIn: serviceTokenData.accessTokenTTL, + expiresIn: machineIdentity.accessTokenTTL, secret: await getAuthSecret() }); - response.expires_in = serviceTokenData.accessTokenTTL; + response.expires_in = machineIdentity.accessTokenTTL; - await ServiceTokenDataV3.findByIdAndUpdate( - serviceTokenData._id, + await MachineIdentity.findByIdAndUpdate( + machineIdentity._id, { refreshTokenLastUsed: new Date(), $inc: { refreshTokenUsageCount: 1 } @@ -119,12 +118,12 @@ import { ADMIN, AuthTokenType, CUSTOM, MEMBER } from "../../../variables"; } /** - * Create service token data V3 + * Create machine identity * @param req * @param res * @returns */ -export const createServiceTokenData = async (req: Request, res: Response) => { +export const createMachineIdentity = async (req: Request, res: Response) => { const { body: { name, @@ -135,7 +134,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => { accessTokenTTL, isRefreshTokenRotationEnabled } - } = await validateRequest(reqValidator.CreateServiceTokenV3, req); + } = await validateRequest(reqValidator.CreateMachineIdentityV3, req); // const { permission } = await getAuthDataProjectPermissions({ // authData: req.authData, @@ -195,7 +194,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => { } const isActive = true; - const serviceTokenData = await new ServiceTokenDataV3({ + const machineIdentity = await new MachineIdentity({ name, user, organization: new Types.ObjectId(organizationId), @@ -209,18 +208,18 @@ export const createServiceTokenData = async (req: Request, res: Response) => { isRefreshTokenRotationEnabled }).save(); - await new ServiceMembershipOrg({ - service: serviceTokenData._id, - organization: serviceTokenData.organization, + await new MachineMembershipOrg({ + machineIdentity: machineIdentity._id, + organization: machineIdentity.organization, role: isCustomRole ? CUSTOM : role, customRole }).save(); const refreshToken = createToken({ payload: { - serviceTokenDataId: serviceTokenData._id.toString(), + serviceTokenDataId: machineIdentity._id.toString(), authTokenType: AuthTokenType.SERVICE_REFRESH_TOKEN, - tokenVersion: serviceTokenData.tokenVersion + tokenVersion: machineIdentity.tokenVersion }, secret: await getAuthSecret() }); @@ -228,12 +227,12 @@ export const createServiceTokenData = async (req: Request, res: Response) => { await EEAuditLogService.createAuditLog( req.authData, { - type: EventType.CREATE_SERVICE_TOKEN_V3, + type: EventType.CREATE_MACHINE_IDENTITY, metadata: { name, isActive, role, - trustedIps: reformattedTrustedIps as Array, + trustedIps: reformattedTrustedIps as Array, expiresAt } }, @@ -243,7 +242,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => { ); return res.status(200).send({ - serviceTokenData, + machineIdentity, refreshToken }); } @@ -254,22 +253,22 @@ export const createServiceTokenData = async (req: Request, res: Response) => { * @param res * @returns */ -export const updateServiceTokenData = async (req: Request, res: Response) => { +export const updateMachineIdentity = async (req: Request, res: Response) => { const { - params: { serviceTokenDataId }, + params: { machineId }, body: { name, isActive, - role, // bring this somewhere else? + role, trustedIps, expiresIn, accessTokenTTL, isRefreshTokenRotationEnabled } - } = await validateRequest(reqValidator.UpdateServiceTokenV3, req); + } = await validateRequest(reqValidator.UpdateMachineIdentityV3, req); - let serviceTokenData = await ServiceTokenDataV3.findById(serviceTokenDataId); - if (!serviceTokenData) throw ResourceNotFoundError({ + let machineIdentity = await MachineIdentity.findById(machineId); + if (!machineIdentity) throw ResourceNotFoundError({ message: "Service token not found" }); @@ -293,14 +292,14 @@ export const updateServiceTokenData = async (req: Request, res: Response) => { customRole = await Role.findOne({ slug: role, isOrgRole: true, - organization: serviceTokenData.organization + organization: machineIdentity.organization }); if (!customRole) throw BadRequestError({ message: "Role not found" }); } } - const plan = await EELicenseService.getPlan(serviceTokenData.organization); + const plan = await EELicenseService.getPlan(machineIdentity.organization); // validate trusted ips let reformattedTrustedIps; @@ -326,12 +325,11 @@ export const updateServiceTokenData = async (req: Request, res: Response) => { expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); } - serviceTokenData = await ServiceTokenDataV3.findByIdAndUpdate( - serviceTokenDataId, + machineIdentity = await MachineIdentity.findByIdAndUpdate( + machineId, { name, isActive, - trustedIps: reformattedTrustedIps, expiresAt, accessTokenTTL, @@ -342,13 +340,13 @@ export const updateServiceTokenData = async (req: Request, res: Response) => { } ); - if (!serviceTokenData) throw BadRequestError({ + if (!machineIdentity) throw BadRequestError({ message: "Failed to update service token" }); - await ServiceMembershipOrg.findOneAndUpdate( + await MachineMembershipOrg.findOneAndUpdate( { - service: serviceTokenData._id + machineIdentity: machineIdentity._id }, { role: customRole ? CUSTOM : role, @@ -369,22 +367,22 @@ export const updateServiceTokenData = async (req: Request, res: Response) => { await EEAuditLogService.createAuditLog( req.authData, { - type: EventType.UPDATE_SERVICE_TOKEN_V3, + type: EventType.UPDATE_MACHINE_IDENTITY, metadata: { - name: serviceTokenData.name, + name: machineIdentity.name, isActive, role, - trustedIps: reformattedTrustedIps as Array, + trustedIps: reformattedTrustedIps as Array, expiresAt } }, { - organizationId: serviceTokenData.organization + organizationId: machineIdentity.organization } ); return res.status(200).send({ - serviceTokenData + machineIdentity }); } @@ -394,13 +392,13 @@ export const updateServiceTokenData = async (req: Request, res: Response) => { * @param res * @returns */ -export const deleteServiceTokenData = async (req: Request, res: Response) => { +export const deleteMachineIdentity = async (req: Request, res: Response) => { const { - params: { serviceTokenDataId } - } = await validateRequest(reqValidator.DeleteServiceTokenV3, req); + params: { machineId } + } = await validateRequest(reqValidator.DeleteMachineIdentityV3, req); - let serviceTokenData = await ServiceTokenDataV3.findById(serviceTokenDataId); - if (!serviceTokenData) throw ResourceNotFoundError({ + let machineIdentity = await MachineIdentity.findById(machineId); + if (!machineIdentity) throw ResourceNotFoundError({ message: "Service token not found" }); @@ -414,46 +412,42 @@ export const deleteServiceTokenData = async (req: Request, res: Response) => { // ProjectPermissionSub.ServiceTokens // ); - serviceTokenData = await ServiceTokenDataV3.findByIdAndDelete(serviceTokenDataId); + machineIdentity = await MachineIdentity.findByIdAndDelete(machineId); - if (!serviceTokenData) throw BadRequestError({ + if (!machineIdentity) throw BadRequestError({ message: "Failed to delete service token" }); - const serviceMembershipOrg = await ServiceMembershipOrg.findOneAndDelete({ - service: serviceTokenData._id, + const machineMembershipOrg = await MachineMembershipOrg.findOneAndDelete({ + machineIdentity: machineIdentity._id, }); - if (!serviceMembershipOrg) throw BadRequestError({ + if (!machineMembershipOrg) throw BadRequestError({ message: "Failed to delete service token" }); - - await ServiceTokenDataV3Key.findOneAndDelete({ - serviceTokenData: serviceTokenData._id - }); - await ServiceMembership.deleteMany({ - service: serviceTokenData._id, + await MachineMembership.deleteMany({ + machineIdentity: machineIdentity._id, }); await EEAuditLogService.createAuditLog( req.authData, { - type: EventType.DELETE_SERVICE_TOKEN_V3, + type: EventType.DELETE_MACHINE_IDENTITY, metadata: { - name: serviceTokenData.name, - isActive: serviceTokenData.isActive, - role: serviceMembershipOrg.role, - trustedIps: serviceTokenData.trustedIps as Array, - expiresAt: serviceTokenData.expiresAt + name: machineIdentity.name, + isActive: machineIdentity.isActive, + role: machineMembershipOrg.role, + trustedIps: machineIdentity.trustedIps as Array, + expiresAt: machineIdentity.expiresAt } }, { - organizationId: serviceTokenData.organization + organizationId: machineIdentity.organization } ); return res.status(200).send({ - serviceTokenData + machineIdentity }); } \ No newline at end of file diff --git a/backend/src/ee/models/auditLog/enums.ts b/backend/src/ee/models/auditLog/enums.ts index 48147db08..08c0c2ff7 100644 --- a/backend/src/ee/models/auditLog/enums.ts +++ b/backend/src/ee/models/auditLog/enums.ts @@ -32,9 +32,9 @@ export enum EventType { DELETE_TRUSTED_IP = "delete-trusted-ip", CREATE_SERVICE_TOKEN = "create-service-token", // v2 DELETE_SERVICE_TOKEN = "delete-service-token", // v2 - CREATE_SERVICE_TOKEN_V3 = "create-service-token-v3", // v3 - UPDATE_SERVICE_TOKEN_V3 = "update-service-token-v3", // v3 - DELETE_SERVICE_TOKEN_V3 = "delete-service-token-v3", // v3 + CREATE_MACHINE_IDENTITY = "create-machine-identity", + UPDATE_MACHINE_IDENTITY = "update-machine-identity", + DELETE_MACHINE_IDENTITY = "delete-machine-identity", CREATE_ENVIRONMENT = "create-environment", UPDATE_ENVIRONMENT = "update-environment", DELETE_ENVIRONMENT = "delete-environment", diff --git a/backend/src/ee/models/auditLog/types.ts b/backend/src/ee/models/auditLog/types.ts index bae4e2f96..859606901 100644 --- a/backend/src/ee/models/auditLog/types.ts +++ b/backend/src/ee/models/auditLog/types.ts @@ -1,5 +1,5 @@ import { ActorType, EventType } from "./enums"; -import { IServiceTokenV3TrustedIp } from "../../../models/serviceTokenDataV3"; +import { IMachineIdentityTrustedIp } from "../../../models/machineIdentity"; interface UserActorMetadata { userId: string; @@ -220,36 +220,36 @@ interface DeleteServiceTokenEvent { }; } -interface CreateServiceTokenV3Event { - type: EventType.CREATE_SERVICE_TOKEN_V3; +interface CreateMachineIdentityEvent { + type: EventType.CREATE_MACHINE_IDENTITY; metadata: { name: string; isActive: boolean; role: string; - trustedIps: Array; + trustedIps: Array; expiresAt?: Date; }; } -interface UpdateServiceTokenV3Event { - type: EventType.UPDATE_SERVICE_TOKEN_V3; +interface UpdateMachineIdentityEvent { + type: EventType.UPDATE_MACHINE_IDENTITY; metadata: { name?: string; isActive?: boolean; role?: string; - trustedIps?: Array; + trustedIps?: Array; expiresAt?: Date; }; } -interface DeleteServiceTokenV3Event { - type: EventType.DELETE_SERVICE_TOKEN_V3; +interface DeleteMachineIdentityEvent { + type: EventType.DELETE_MACHINE_IDENTITY; metadata: { name: string; isActive: boolean; role: string; expiresAt?: Date; - trustedIps: Array; + trustedIps: Array; }; } @@ -495,9 +495,9 @@ export type Event = | DeleteTrustedIPEvent | CreateServiceTokenEvent | DeleteServiceTokenEvent - | CreateServiceTokenV3Event - | UpdateServiceTokenV3Event - | DeleteServiceTokenV3Event + | CreateMachineIdentityEvent + | UpdateMachineIdentityEvent + | DeleteMachineIdentityEvent | CreateEnvironmentEvent | UpdateEnvironmentEvent | DeleteEnvironmentEvent diff --git a/backend/src/ee/routes/v3/index.ts b/backend/src/ee/routes/v3/index.ts index dd8c13427..2569a9d2a 100644 --- a/backend/src/ee/routes/v3/index.ts +++ b/backend/src/ee/routes/v3/index.ts @@ -1,7 +1,7 @@ -import serviceTokenData from "./serviceTokenData"; +import machineIdentity from "./machineIdentity"; import apiKeyData from "./apiKeyData"; export { - serviceTokenData, + machineIdentity, apiKeyData } \ No newline at end of file diff --git a/backend/src/ee/routes/v3/machineIdentity.ts b/backend/src/ee/routes/v3/machineIdentity.ts new file mode 100644 index 000000000..6f0ac8e7c --- /dev/null +++ b/backend/src/ee/routes/v3/machineIdentity.ts @@ -0,0 +1,36 @@ +import express from "express"; +const router = express.Router(); +import { requireAuth } from "../../../middleware"; +import { AuthMode } from "../../../variables"; +import { machineIdentityController } from "../../controllers/v3"; + +router.post( + "/me/token", + machineIdentityController.refreshToken +); + +router.post( + "/", + requireAuth({ + acceptedAuthModes: [AuthMode.JWT] + }), + machineIdentityController.createMachineIdentity +); + +router.patch( + "/:machineId", + requireAuth({ + acceptedAuthModes: [AuthMode.JWT] + }), + machineIdentityController.updateMachineIdentity +); + +router.delete( + "/:machineId", + requireAuth({ + acceptedAuthModes: [AuthMode.JWT] + }), + machineIdentityController.deleteMachineIdentity +); + +export default router; \ No newline at end of file diff --git a/backend/src/ee/routes/v3/serviceTokenData.ts b/backend/src/ee/routes/v3/serviceTokenData.ts deleted file mode 100644 index 6b82abe3d..000000000 --- a/backend/src/ee/routes/v3/serviceTokenData.ts +++ /dev/null @@ -1,36 +0,0 @@ -import express from "express"; -const router = express.Router(); -import { requireAuth } from "../../../middleware"; -import { AuthMode } from "../../../variables"; -import { serviceTokenDataController } from "../../controllers/v3"; - -router.post( - "/me/token", - serviceTokenDataController.refreshToken -); - -router.post( - "/", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - serviceTokenDataController.createServiceTokenData -); - -router.patch( - "/:serviceTokenDataId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - serviceTokenDataController.updateServiceTokenData -); - -router.delete( - "/:serviceTokenDataId", - requireAuth({ - acceptedAuthModes: [AuthMode.JWT] - }), - serviceTokenDataController.deleteServiceTokenData -); - -export default router; \ No newline at end of file diff --git a/backend/src/ee/services/ProjectRoleService.ts b/backend/src/ee/services/ProjectRoleService.ts index 0a0d22b28..3eb4c9f05 100644 --- a/backend/src/ee/services/ProjectRoleService.ts +++ b/backend/src/ee/services/ProjectRoleService.ts @@ -13,9 +13,9 @@ import picomatch from "picomatch"; import { AuthData } from "../../interfaces/middleware"; import { ActorType, IRole } from "../models"; import { - IServiceTokenDataV3, - Membership, - ServiceMembership, + IMachineIdentity, + MachineMembership, + Membership, ServiceTokenData } from "../../models"; import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../../variables"; @@ -290,13 +290,13 @@ export const getAuthDataProjectPermissions = async ({ break; } case ActorType.SERVICE_V3: { - const serviceMembership = await ServiceMembership.findOne({ - service: authData.authPayload._id, + const serviceMembership = await MachineMembership.findOne({ + machineIdentity: authData.authPayload._id, workspace: workspaceId }) .populate<{ customRole: IRole & { permissions: RawRuleOf>[] }; - service: IServiceTokenDataV3 + machineIdentity: IMachineIdentity }>("customRole service") .exec(); @@ -306,7 +306,7 @@ export const getAuthDataProjectPermissions = async ({ checkIPAgainstBlocklist({ ipAddress: authData.ipAddress, - trustedIps: serviceMembership.service.trustedIps + trustedIps: serviceMembership.machineIdentity.trustedIps }); role = serviceMembership.role; diff --git a/backend/src/helpers/organization.ts b/backend/src/helpers/organization.ts index 4b2ac369b..d0d494890 100644 --- a/backend/src/helpers/organization.ts +++ b/backend/src/helpers/organization.ts @@ -8,6 +8,9 @@ import { Integration, IntegrationAuth, Key, + MachineIdentity, + MachineMembership, + MachineMembershipOrg, Membership, MembershipOrg, Organization, @@ -16,8 +19,6 @@ import { SecretImport, ServiceToken, ServiceTokenData, - ServiceTokenDataV3, - ServiceTokenDataV3Key, Tag, Webhook, Workspace @@ -124,6 +125,14 @@ export const deleteOrganization = async ({ organization: organization._id }); + await MachineIdentity.deleteMany({ + organization: organization._id + }); + + await MachineMembershipOrg.deleteMany({ + organization: organization._id + }); + await BotOrg.deleteMany({ organization: organization._id }); @@ -268,13 +277,7 @@ export const deleteOrganization = async ({ } }); - await ServiceTokenDataV3.deleteMany({ - workspace: { - $in: workspaceIds - } - }); - - await ServiceTokenDataV3Key.deleteMany({ + await MachineMembership.deleteMany({ workspace: { $in: workspaceIds } diff --git a/backend/src/helpers/workspace.ts b/backend/src/helpers/workspace.ts index 60e4955b5..77d1eea3d 100644 --- a/backend/src/helpers/workspace.ts +++ b/backend/src/helpers/workspace.ts @@ -6,14 +6,13 @@ import { Integration, IntegrationAuth, Key, + MachineMembership, Membership, Secret, SecretBlindIndexData, SecretImport, ServiceToken, ServiceTokenData, - ServiceTokenDataV3, - ServiceTokenDataV3Key, Tag, Webhook, Workspace @@ -178,12 +177,8 @@ export const deleteWorkspace = async ({ await ServiceTokenData.deleteMany({ workspace: workspace._id }); - - await ServiceTokenDataV3.deleteMany({ - workspace: workspace._id - }); - - await ServiceTokenDataV3Key.deleteMany({ + + await MachineMembership.deleteMany({ workspace: workspace._id }); diff --git a/backend/src/index.ts b/backend/src/index.ts index ad25f7821..9d50af3ab 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -33,7 +33,7 @@ import { secretScanning as v1SecretScanningRouter } from "./ee/routes/v1"; import { apiKeyData as v3apiKeyDataRouter } from "./ee/routes/v3"; -import { serviceTokenData as v3ServiceTokenDataRouter } from "./ee/routes/v3"; +import { machineIdentity as v3MachineIdentityRouter } from "./ee/routes/v3"; import { admin as v1AdminRouter, auth as v1AuthRouter, @@ -205,8 +205,8 @@ const main = async () => { app.use("/api/v1/organizations", eeOrganizationsRouter); app.use("/api/v1/sso", eeSSORouter); app.use("/api/v1/cloud-products", eeCloudProductsRouter); - app.use("/api/v3/api-key", v3apiKeyDataRouter); // new - app.use("/api/v3/service-token", v3ServiceTokenDataRouter); // new + app.use("/api/v3/api-key", v3apiKeyDataRouter); + app.use("/api/v3/machines", v3MachineIdentityRouter); app.use("/api/v1/secret-rotation-providers", v1SecretRotationProviderRouter); app.use("/api/v1/secret-rotations", v1SecretRotation); @@ -220,7 +220,7 @@ const main = async () => { app.use("/api/v1/organization", v1OrganizationRouter); app.use("/api/v1/workspace", v1WorkspaceRouter); app.use("/api/v1/membership-org", v1MembershipOrgRouter); - app.use("/api/v1/membership", v1MembershipRouter); // + app.use("/api/v1/membership", v1MembershipRouter); app.use("/api/v1/key", v1KeyRouter); app.use("/api/v1/invite-org", v1InviteOrgRouter); app.use("/api/v1/secret", v1SecretRouter); // deprecate diff --git a/backend/src/interfaces/middleware/index.ts b/backend/src/interfaces/middleware/index.ts index 0100d3ff6..3bccaa20e 100644 --- a/backend/src/interfaces/middleware/index.ts +++ b/backend/src/interfaces/middleware/index.ts @@ -1,5 +1,5 @@ import { Types } from "mongoose"; -import { IServiceTokenData, IServiceTokenDataV3, IUser } from "../../models"; +import { IMachineIdentity, IServiceTokenData, IUser } from "../../models"; import { ServiceActor, ServiceActorV3, UserActor, UserAgentType } from "../../ee/models"; interface BaseAuthData { @@ -14,9 +14,9 @@ export interface UserAuthData extends BaseAuthData { authPayload: IUser; } -export interface ServiceTokenV3AuthData extends BaseAuthData { +export interface MachineIdentityAuthData extends BaseAuthData { actor: ServiceActorV3; - authPayload: IServiceTokenDataV3; + authPayload: IMachineIdentity; } export interface ServiceTokenAuthData extends BaseAuthData { @@ -24,4 +24,4 @@ export interface ServiceTokenAuthData extends BaseAuthData { authPayload: IServiceTokenData; } -export type AuthData = UserAuthData | ServiceTokenV3AuthData | ServiceTokenAuthData; +export type AuthData = UserAuthData | MachineIdentityAuthData | ServiceTokenAuthData; diff --git a/backend/src/models/index.ts b/backend/src/models/index.ts index f4af92dd8..dae46bbf7 100644 --- a/backend/src/models/index.ts +++ b/backend/src/models/index.ts @@ -20,10 +20,9 @@ export * from "./user"; export * from "./userAction"; export * from "./workspace"; export * from "./serviceTokenData"; // TODO: deprecate -export * from "./serviceTokenDataV3"; -export * from "./serviceMembershipOrg"; -export * from "./serviceMembership"; -export * from "./serviceTokenDataV3Key"; /// TODO: remove +export * from "./machineIdentity"; +export * from "./machineMembershipOrg"; +export * from "./machineMembership"; export * from "./apiKeyData"; // TODO: deprecate export * from "./apiKeyDataV2"; export * from "./loginSRPDetail"; diff --git a/backend/src/models/serviceTokenDataV3.ts b/backend/src/models/machineIdentity.ts similarity index 90% rename from backend/src/models/serviceTokenDataV3.ts rename to backend/src/models/machineIdentity.ts index 6c1518e22..7277244ce 100644 --- a/backend/src/models/serviceTokenDataV3.ts +++ b/backend/src/models/machineIdentity.ts @@ -1,13 +1,13 @@ import { Document, Schema, Types, model } from "mongoose"; import { IPType } from "../ee/models"; -export interface IServiceTokenV3TrustedIp { +export interface IMachineIdentityTrustedIp { ipAddress: string; type: IPType; prefix: number; } -export interface IServiceTokenDataV3 extends Document { +export interface IMachineIdentity extends Document { _id: Types.ObjectId; name: string; organization: Types.ObjectId; @@ -21,10 +21,10 @@ export interface IServiceTokenDataV3 extends Document { isRefreshTokenRotationEnabled: boolean; expiresAt?: Date; accessTokenTTL: number; - trustedIps: Array; + trustedIps: Array; } -const serviceTokenDataV3Schema = new Schema( +const machineIdentitySchema = new Schema( { name: { type: String, @@ -117,4 +117,4 @@ const serviceTokenDataV3Schema = new Schema( } ); -export const ServiceTokenDataV3 = model("ServiceTokenDataV3", serviceTokenDataV3Schema); \ No newline at end of file +export const MachineIdentity = model("MachineIdentity", machineIdentitySchema); \ No newline at end of file diff --git a/backend/src/models/serviceMembership.ts b/backend/src/models/machineMembership.ts similarity index 66% rename from backend/src/models/serviceMembership.ts rename to backend/src/models/machineMembership.ts index 0a68700c5..95dcb749d 100644 --- a/backend/src/models/serviceMembership.ts +++ b/backend/src/models/machineMembership.ts @@ -1,19 +1,19 @@ import { Schema, Types, model } from "mongoose"; import { ADMIN, CUSTOM, MEMBER, VIEWER } from "../variables"; -export interface IServiceMembership { +export interface IMachineMembership { _id: Types.ObjectId; - service: Types.ObjectId; + machineIdentity: Types.ObjectId; workspace: Types.ObjectId; role: "admin" | "member" | "viewer" | "custom"; customRole: Types.ObjectId; } -const serviceMembershipSchema = new Schema( +const machineMembershipSchema = new Schema( { - service: { // TODO: consider renaming + machineIdentity: { type: Schema.Types.ObjectId, - ref: "ServiceTokenDataV3" + ref: "MachineIdentity" }, workspace: { type: Schema.Types.ObjectId, @@ -35,4 +35,4 @@ const serviceMembershipSchema = new Schema( } ); -export const ServiceMembership = model("ServiceMembership", serviceMembershipSchema); \ No newline at end of file +export const MachineMembership = model("MachineMembership", machineMembershipSchema); \ No newline at end of file diff --git a/backend/src/models/serviceMembershipOrg.ts b/backend/src/models/machineMembershipOrg.ts similarity index 64% rename from backend/src/models/serviceMembershipOrg.ts rename to backend/src/models/machineMembershipOrg.ts index 242b9ebd9..ae455e6c8 100644 --- a/backend/src/models/serviceMembershipOrg.ts +++ b/backend/src/models/machineMembershipOrg.ts @@ -1,19 +1,19 @@ import { Schema, Types, model } from "mongoose"; import { ADMIN, CUSTOM, MEMBER } from "../variables"; -export interface IServiceMembershipOrg { +export interface IMachineMembershipOrg { _id: Types.ObjectId; - service: Types.ObjectId; + machineIdentity: Types.ObjectId; organization: Types.ObjectId; role: "admin" | "member" | "viewer" | "custom"; customRole: Types.ObjectId; } -const serviceMembershipOrgSchema = new Schema( +const machineMembershipOrgSchema = new Schema( { - service: { // TODO: consider renaming + machineIdentity: { type: Schema.Types.ObjectId, - ref: "ServiceTokenDataV3" + ref: "MachineIdentity" }, organization: { type: Schema.Types.ObjectId, @@ -34,4 +34,4 @@ const serviceMembershipOrgSchema = new Schema( } ); -export const ServiceMembershipOrg = model("ServiceMembershipOrg", serviceMembershipOrgSchema); \ No newline at end of file +export const MachineMembershipOrg = model("MachineMembershipOrg", machineMembershipOrgSchema); \ No newline at end of file diff --git a/backend/src/models/serviceTokenDataV3Key.ts b/backend/src/models/serviceTokenDataV3Key.ts deleted file mode 100644 index 7a69abc1e..000000000 --- a/backend/src/models/serviceTokenDataV3Key.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Document, Schema, Types, model } from "mongoose"; - -export interface IServiceTokenDataV3Key extends Document { - _id: Types.ObjectId; - encryptedKey: string; - nonce: string; - sender: Types.ObjectId; - serviceTokenData: Types.ObjectId; - workspace: Types.ObjectId; -} - -const serviceTokenDataV3KeySchema = new Schema( - { - encryptedKey: { - type: String, - required: true - }, - nonce: { - type: String, - required: true - }, - sender: { - type: Schema.Types.ObjectId, - ref: "User", - required: true - }, - serviceTokenData: { - type: Schema.Types.ObjectId, - ref: "ServiceTokenDataV3", - required: true, - }, - workspace: { - type: Schema.Types.ObjectId, - ref: "Workspace", - required: true, - } - }, - { - timestamps: true - } -); - -export const ServiceTokenDataV3Key = model("ServiceTokenDataV3Key", serviceTokenDataV3KeySchema); \ No newline at end of file diff --git a/backend/src/routes/v2/organizations.ts b/backend/src/routes/v2/organizations.ts index 456635f75..f367d4bce 100644 --- a/backend/src/routes/v2/organizations.ts +++ b/backend/src/routes/v2/organizations.ts @@ -55,11 +55,11 @@ router.delete( ); router.get( - "/:organizationId/service-memberships", + "/:organizationId/machine-memberships", requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), - organizationsController.getOrganizationServiceMemberships + organizationsController.getOrganizationMachineMemberships ); export default router; diff --git a/backend/src/routes/v2/serviceTokenData.ts b/backend/src/routes/v2/serviceTokenData.ts index c959cfa56..7b3dcaa06 100644 --- a/backend/src/routes/v2/serviceTokenData.ts +++ b/backend/src/routes/v2/serviceTokenData.ts @@ -6,7 +6,7 @@ import { import { AuthMode } from "../../variables"; import { serviceTokenDataController } from "../../controllers/v2"; -router.get( // TODO: deprecate (moving to ST V3) +router.get( // TODO: deprecate (moving to machine identity) "/", requireAuth({ acceptedAuthModes: [AuthMode.SERVICE_TOKEN] @@ -14,7 +14,7 @@ router.get( // TODO: deprecate (moving to ST V3) serviceTokenDataController.getServiceTokenData ); -router.post( // TODO: deprecate (moving to ST V3) +router.post( // TODO: deprecate (moving to machine identity) "/", requireAuth({ acceptedAuthModes: [AuthMode.JWT] @@ -22,7 +22,7 @@ router.post( // TODO: deprecate (moving to ST V3) serviceTokenDataController.createServiceTokenData ); -router.delete( // TODO: deprecate (moving to ST V3) +router.delete( // TODO: deprecate (moving to machine identity) "/:serviceTokenDataId", requireAuth({ acceptedAuthModes: [AuthMode.JWT] diff --git a/backend/src/routes/v2/workspace.ts b/backend/src/routes/v2/workspace.ts index 095a0d1fc..24d31f96e 100644 --- a/backend/src/routes/v2/workspace.ts +++ b/backend/src/routes/v2/workspace.ts @@ -93,29 +93,28 @@ router.patch( workspaceController.toggleAutoCapitalization ); -// TODO: consider endpoint restructuring router.post( - "/:workspaceId/service-memberships", + "/:workspaceId/machine-memberships/:machineId", requireAuth({ acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] }), - workspaceController.addWorkspaceServiceMembership + workspaceController.addMachineToWorkspace ); router.delete( - "/:workspaceId/service-memberships/:serviceId", + "/:workspaceId/machine-memberships/:machineId", requireAuth({ acceptedAuthModes: [AuthMode.JWT, AuthMode.API_KEY] }), - workspaceController.deleteWorkspaceServiceMembership + workspaceController.deleteMachineFromWorkspace ); router.get( - "/:workspaceId/service-memberships", + "/:workspaceId/machine-memberships", requireAuth({ acceptedAuthModes: [AuthMode.JWT] }), - workspaceController.getWorkspaceServiceMemberships + workspaceController.getWorkspaceMachineMemberships ); export default router; diff --git a/backend/src/utils/authn/authModeValidators/index.ts b/backend/src/utils/authn/authModeValidators/index.ts index 0ac4c3c1c..6a8ab3ada 100644 --- a/backend/src/utils/authn/authModeValidators/index.ts +++ b/backend/src/utils/authn/authModeValidators/index.ts @@ -2,4 +2,4 @@ export * from "./apiKey"; export * from "./apiKeyV2"; export * from "./jwt"; export * from "./serviceTokenV2"; -export * from "./serviceTokenV3"; \ No newline at end of file +export * from "./machineIdentity"; \ No newline at end of file diff --git a/backend/src/utils/authn/authModeValidators/serviceTokenV3.ts b/backend/src/utils/authn/authModeValidators/machineIdentity.ts similarity index 63% rename from backend/src/utils/authn/authModeValidators/serviceTokenV3.ts rename to backend/src/utils/authn/authModeValidators/machineIdentity.ts index 330f60c1c..effb657ae 100644 --- a/backend/src/utils/authn/authModeValidators/serviceTokenV3.ts +++ b/backend/src/utils/authn/authModeValidators/machineIdentity.ts @@ -1,36 +1,36 @@ import jwt from "jsonwebtoken"; import { Types } from "mongoose"; -import { ServiceTokenDataV3 } from "../../../models"; +import { MachineIdentity } from "../../../models"; import { getAuthSecret } from "../../../config"; import { AuthTokenType } from "../../../variables"; import { UnauthorizedRequestError } from "../../errors"; -interface ValidateServiceTokenV3Params { +interface ValidateMachineIdentityParams { authTokenValue: string; } -export const validateServiceTokenV3 = async ({ +export const validateMachineIdentity = async ({ authTokenValue -}: ValidateServiceTokenV3Params) => { +}: ValidateMachineIdentityParams) => { const decodedToken = ( jwt.verify(authTokenValue, await getAuthSecret()) ); if (decodedToken.authTokenType !== AuthTokenType.SERVICE_ACCESS_TOKEN) throw UnauthorizedRequestError(); - const serviceTokenData = await ServiceTokenDataV3.findOne({ + const machineIdentity = await MachineIdentity.findOne({ _id: new Types.ObjectId(decodedToken.serviceTokenDataId), isActive: true }); - if (!serviceTokenData) { + if (!machineIdentity) { throw UnauthorizedRequestError({ message: "Failed to authenticate" }); - } else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) { + } else if (machineIdentity?.expiresAt && new Date(machineIdentity.expiresAt) < new Date()) { // case: service token expired - await ServiceTokenDataV3.findByIdAndUpdate( - serviceTokenData._id, + await MachineIdentity.findByIdAndUpdate( + machineIdentity._id, { isActive: false }, @@ -42,15 +42,15 @@ export const validateServiceTokenV3 = async ({ throw UnauthorizedRequestError({ message: "Failed to authenticate", }); - } else if (decodedToken.tokenVersion !== serviceTokenData.tokenVersion) { + } else if (decodedToken.tokenVersion !== machineIdentity.tokenVersion) { // TODO: raise alarm throw UnauthorizedRequestError({ message: "Failed to authenticate", }); } - await ServiceTokenDataV3.findByIdAndUpdate( - serviceTokenData._id, + await MachineIdentity.findByIdAndUpdate( + machineIdentity._id, { accessTokenLastUsed: new Date(), $inc: { accessTokenUsageCount: 1 } @@ -60,5 +60,5 @@ export const validateServiceTokenV3 = async ({ } ); - return serviceTokenData; + return machineIdentity; } \ No newline at end of file diff --git a/backend/src/utils/authn/helpers/authDataExtractors.ts b/backend/src/utils/authn/helpers/authDataExtractors.ts index d35163e03..5491549db 100644 --- a/backend/src/utils/authn/helpers/authDataExtractors.ts +++ b/backend/src/utils/authn/helpers/authDataExtractors.ts @@ -1,7 +1,7 @@ import { AuthData } from "../../../interfaces/middleware"; import { + MachineIdentity, ServiceTokenData, - ServiceTokenDataV3, User } from "../../../models"; @@ -19,7 +19,7 @@ import { return { serviceTokenDataId: authData.authPayload._id }; } - if (authData.authPayload instanceof ServiceTokenDataV3) { + if (authData.authPayload instanceof MachineIdentity) { return { serviceTokenDataId: authData.authPayload._id }; } }; @@ -38,7 +38,7 @@ export const getAuthDataPayloadUserObj = (authData: AuthData) => { return { user: authData.authPayload.user }; } - if (authData.authPayload instanceof ServiceTokenDataV3) { + if (authData.authPayload instanceof MachineIdentity) { return { user: authData.authPayload.user }; } } \ No newline at end of file diff --git a/backend/src/utils/authn/helpers/index.ts b/backend/src/utils/authn/helpers/index.ts index 064efe133..1ae7449b4 100644 --- a/backend/src/utils/authn/helpers/index.ts +++ b/backend/src/utils/authn/helpers/index.ts @@ -8,8 +8,8 @@ import { validateAPIKey, validateAPIKeyV2, validateJWT, - validateServiceTokenV2, - validateServiceTokenV3 + validateMachineIdentity, + validateServiceTokenV2 } from "../authModeValidators"; import { getUserAgentType } from "../../posthog"; @@ -36,7 +36,7 @@ interface GetAuthDataParams { * - SERVICE_TOKEN * - API_KEY * - JWT - * - SERVICE_ACCESS_TOKEN (from ST V3) + * - SERVICE_ACCESS_TOKEN (from machine identity) * - API_KEY_V2 * @param {Object} params * @param {Object.} params.headers - The HTTP request headers, usually from Express's `req.headers`. @@ -116,7 +116,7 @@ export const getAuthData = async ({ } } case AuthMode.SERVICE_ACCESS_TOKEN: { - const serviceTokenData = await validateServiceTokenV3({ + const serviceTokenData = await validateMachineIdentity({ authTokenValue }); diff --git a/backend/src/validation/index.ts b/backend/src/validation/index.ts index 002eda34d..a9b8a963f 100644 --- a/backend/src/validation/index.ts +++ b/backend/src/validation/index.ts @@ -8,5 +8,5 @@ export * from "./membershipOrg"; export * from "./organization"; export * from "./secrets"; export * from "./serviceTokenData"; -export * from "./serviceTokenDataV3"; +export * from "./machineIdentity"; export * from "./apiKeyDataV3"; diff --git a/backend/src/validation/serviceTokenDataV3.ts b/backend/src/validation/machineIdentity.ts similarity index 84% rename from backend/src/validation/serviceTokenDataV3.ts rename to backend/src/validation/machineIdentity.ts index f025b5bfa..6536f874e 100644 --- a/backend/src/validation/serviceTokenDataV3.ts +++ b/backend/src/validation/machineIdentity.ts @@ -7,7 +7,7 @@ export const RefreshTokenV3 = z.object({ }) }); -export const CreateServiceTokenV3 = z.object({ +export const CreateMachineIdentityV3 = z.object({ body: z.object({ name: z.string().trim(), organizationId: z.string().trim(), @@ -25,9 +25,9 @@ export const CreateServiceTokenV3 = z.object({ }) }); -export const UpdateServiceTokenV3 = z.object({ +export const UpdateMachineIdentityV3 = z.object({ params: z.object({ - serviceTokenDataId: z.string() + machineId: z.string() }), body: z.object({ name: z.string().trim().optional(), @@ -46,8 +46,8 @@ export const UpdateServiceTokenV3 = z.object({ }), }); -export const DeleteServiceTokenV3 = z.object({ +export const DeleteMachineIdentityV3 = z.object({ params: z.object({ - serviceTokenDataId: z.string() + machineId: z.string() }), }); \ No newline at end of file diff --git a/backend/src/validation/organization.ts b/backend/src/validation/organization.ts index 25c547f92..aab05e97a 100644 --- a/backend/src/validation/organization.ts +++ b/backend/src/validation/organization.ts @@ -216,8 +216,4 @@ export const DeleteOrgv2 = z.object({ export const GetOrgServiceMembersV2 = z.object({ params: z.object({ organizationId: z.string().trim() }) -}); - -export const GetOrgServiceTokenDataV3V2 = z.object({ - params: z.object({ organizationId: z.string().trim() }) }); \ No newline at end of file diff --git a/backend/src/validation/workspace.ts b/backend/src/validation/workspace.ts index 36238e408..b7d3b56ba 100644 --- a/backend/src/validation/workspace.ts +++ b/backend/src/validation/workspace.ts @@ -282,10 +282,10 @@ export const ToggleAutoCapitalizationV2 = z.object({ export const AddWorkspaceServiceMemberV2 = z.object({ params: z.object({ - workspaceId: z.string().trim() + workspaceId: z.string().trim(), + machineId: z.string().trim() }), body: z.object({ - serviceId: z.string().trim(), // TODO: consider renaming role: z.string().trim().min(1).default(MEMBER), }) }); @@ -293,7 +293,7 @@ export const AddWorkspaceServiceMemberV2 = z.object({ export const DeleteWorkspaceServiceMemberV2 = z.object({ params: z.object({ workspaceId: z.string().trim(), - serviceId: z.string().trim() + machineId: z.string().trim() }) }); diff --git a/frontend/src/hooks/api/auditLogs/constants.tsx b/frontend/src/hooks/api/auditLogs/constants.tsx index 9d31f812d..be43a2112 100644 --- a/frontend/src/hooks/api/auditLogs/constants.tsx +++ b/frontend/src/hooks/api/auditLogs/constants.tsx @@ -16,9 +16,9 @@ export const eventToNameMap: { [K in EventType]: string } = { [EventType.DELETE_TRUSTED_IP]: "Delete trusted IP", [EventType.CREATE_SERVICE_TOKEN]: "Create service token", [EventType.DELETE_SERVICE_TOKEN]: "Delete service token", - [EventType.CREATE_SERVICE_TOKEN_V3]: "Create (new) service token", - [EventType.UPDATE_SERVICE_TOKEN_V3]: "Update (new) service token", - [EventType.DELETE_SERVICE_TOKEN_V3]: "Delete (new) service token", + [EventType.CREATE_MACHINE_IDENTITY]: "Create machine identity", + [EventType.UPDATE_MACHINE_IDENTITY]: "Update machine identity", + [EventType.DELETE_MACHINE_IDENTITY]: "Delete machine identity", [EventType.CREATE_ENVIRONMENT]: "Create environment", [EventType.UPDATE_ENVIRONMENT]: "Update environment", [EventType.DELETE_ENVIRONMENT]: "Delete environment", diff --git a/frontend/src/hooks/api/auditLogs/enums.tsx b/frontend/src/hooks/api/auditLogs/enums.tsx index c292fdc24..0be03e452 100644 --- a/frontend/src/hooks/api/auditLogs/enums.tsx +++ b/frontend/src/hooks/api/auditLogs/enums.tsx @@ -27,9 +27,9 @@ export enum EventType { DELETE_TRUSTED_IP = "delete-trusted-ip", CREATE_SERVICE_TOKEN = "create-service-token", // v2 DELETE_SERVICE_TOKEN = "delete-service-token", // v2 - CREATE_SERVICE_TOKEN_V3 = "create-service-token-v3", // v3 - UPDATE_SERVICE_TOKEN_V3 = "update-service-token-v3", // v3 - DELETE_SERVICE_TOKEN_V3 = "delete-service-token-v3", // v3 + CREATE_MACHINE_IDENTITY = "create-machine-identity", + UPDATE_MACHINE_IDENTITY = "update-machine-identity", + DELETE_MACHINE_IDENTITY = "delete-machine-identity", CREATE_ENVIRONMENT = "create-environment", UPDATE_ENVIRONMENT = "update-environment", DELETE_ENVIRONMENT = "delete-environment", diff --git a/frontend/src/hooks/api/auditLogs/types.tsx b/frontend/src/hooks/api/auditLogs/types.tsx index 579dd8fc5..ff6dceffc 100644 --- a/frontend/src/hooks/api/auditLogs/types.tsx +++ b/frontend/src/hooks/api/auditLogs/types.tsx @@ -188,8 +188,8 @@ interface DeleteServiceTokenEvent { }; } -interface CreateServiceTokenV3Event { - type: EventType.CREATE_SERVICE_TOKEN_V3; +interface CreateMachineIdentityEvent { + type: EventType.CREATE_MACHINE_IDENTITY; metadata: { name: string; isActive: boolean; @@ -198,8 +198,8 @@ interface CreateServiceTokenV3Event { } } -interface UpdateServiceTokenV3Event { - type: EventType.UPDATE_SERVICE_TOKEN_V3; +interface UpdateMachineIdentityEvent { + type: EventType.UPDATE_MACHINE_IDENTITY; metadata: { name?: string; isActive?: boolean; @@ -208,8 +208,8 @@ interface UpdateServiceTokenV3Event { } } -interface DeleteServiceTokenV3Event { - type: EventType.DELETE_SERVICE_TOKEN_V3; +interface DeleteMachineIdentityEvent { + type: EventType.DELETE_MACHINE_IDENTITY; metadata: { name: string; isActive: boolean; @@ -414,9 +414,9 @@ export type Event = | DeleteTrustedIPEvent | CreateServiceTokenEvent | DeleteServiceTokenEvent - | CreateServiceTokenV3Event - | UpdateServiceTokenV3Event - | DeleteServiceTokenV3Event + | CreateMachineIdentityEvent + | UpdateMachineIdentityEvent + | DeleteMachineIdentityEvent | CreateEnvironmentEvent | UpdateEnvironmentEvent | DeleteEnvironmentEvent diff --git a/frontend/src/hooks/api/index.tsx b/frontend/src/hooks/api/index.tsx index f2e141f71..f3f1e4eee 100644 --- a/frontend/src/hooks/api/index.tsx +++ b/frontend/src/hooks/api/index.tsx @@ -7,6 +7,7 @@ export * from "./incidentContacts"; export * from "./integrationAuth"; export * from "./integrations"; export * from "./keys"; +export * from "./machineIdentities"; export * from "./organization"; export * from "./roles"; export * from "./secretApproval"; @@ -24,4 +25,4 @@ export * from "./tags"; export * from "./trustedIps"; export * from "./users"; export * from "./webhooks"; -export * from "./workspace"; +export * from "./workspace"; \ No newline at end of file diff --git a/frontend/src/hooks/api/machineIdentities/index.tsx b/frontend/src/hooks/api/machineIdentities/index.tsx new file mode 100644 index 000000000..3be974866 --- /dev/null +++ b/frontend/src/hooks/api/machineIdentities/index.tsx @@ -0,0 +1,4 @@ +export { + useCreateMachineIdentity, + useDeleteMachineIdentity, + useUpdateMachineIdentity} from "./mutations"; \ No newline at end of file diff --git a/frontend/src/hooks/api/machineIdentities/mutations.tsx b/frontend/src/hooks/api/machineIdentities/mutations.tsx new file mode 100644 index 000000000..f9d000ef3 --- /dev/null +++ b/frontend/src/hooks/api/machineIdentities/mutations.tsx @@ -0,0 +1,71 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { organizationKeys } from "../organization/queries"; +import { + CreateMachineIdentityDTO, + CreateMachineIdentityRes, + DeleteMachineIdentityDTO, + MachineIdentity, + UpdateMachineIdentityDTO} from "./types"; + +export const useCreateMachineIdentity = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (body) => { + const { data } = await apiRequest.post("/api/v3/machines/", body); + return data; + }, + onSuccess: ({ machineIdentity }) => { + queryClient.invalidateQueries(organizationKeys.getOrgServiceMemberships(machineIdentity.organization)); + } + }); +}; + +export const useUpdateMachineIdentity = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + machineId, + name, + role, + isActive, + trustedIps, + expiresIn, + accessTokenTTL, + isRefreshTokenRotationEnabled + }) => { + + const { data: { machineIdentity } } = await apiRequest.patch(`/api/v3/machines/${machineId}`, { + name, + role, + isActive, + trustedIps, + expiresIn, + accessTokenTTL, + isRefreshTokenRotationEnabled + }); + + return machineIdentity; + }, + onSuccess: ({ organization }) => { + queryClient.invalidateQueries(organizationKeys.getOrgServiceMemberships(organization)); + } + }); +}; + +export const useDeleteMachineIdentity = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ + machineId + }) => { + const { data: { machineIdentity } } = await apiRequest.delete(`/api/v3/machines/${machineId}`); + return machineIdentity; + }, + onSuccess: ({ organization }) => { + queryClient.invalidateQueries(organizationKeys.getOrgServiceMemberships(organization)); + } + }); +}; \ No newline at end of file diff --git a/frontend/src/hooks/api/machineIdentities/types.ts b/frontend/src/hooks/api/machineIdentities/types.ts new file mode 100644 index 000000000..cd0c6faf6 --- /dev/null +++ b/frontend/src/hooks/api/machineIdentities/types.ts @@ -0,0 +1,79 @@ +import { TRole } from "../roles/types"; + +export type MachineTrustedIp = { + _id: string; + ipAddress: string; + type: "ipv4" | "ipv6"; + prefix?: number; +} + +export type MachineIdentity = { + _id: string; + name: string; + organization: string; + isActive: boolean; + refreshTokenLastUsed?: string; + accessTokenLastUsed?: string; + refreshTokenUsageCount: number; + accessTokenUsageCount: number; + trustedIps: MachineTrustedIp[]; + expiresAt?: string; + accessTokenTTL: number; + isRefreshTokenRotationEnabled: boolean; + createdAt: string; + updatedAt: string; +}; + +export type MachineMembershipOrg = { + _id: string; + machineIdentity: MachineIdentity; + organization: string; + role: "admin" | "member" | "viewer" | "custom"; + customRole?: TRole; + createdAt: string; + updatedAt: string; +} + +export type MachineMembership = { + _id: string; + machineIdentity: MachineIdentity; + organization: string; + role: "admin" | "member" | "viewer" | "custom"; + customRole?: TRole; + createdAt: string; + updatedAt: string; +} + +export type CreateMachineIdentityDTO = { + name: string; + organizationId: string; + role?: string; + trustedIps: { + ipAddress: string; + }[]; + expiresIn?: number; + accessTokenTTL: number; + isRefreshTokenRotationEnabled: boolean; +} + +export type CreateMachineIdentityRes = { + refreshToken: string; + machineIdentity: MachineIdentity; +} + +export type UpdateMachineIdentityDTO = { + machineId: string; + isActive?: boolean; + name?: string; + role?: string; + trustedIps?: { + ipAddress: string; + }[]; + expiresIn?: number; + accessTokenTTL?: number; + isRefreshTokenRotationEnabled?: boolean; +} + +export type DeleteMachineIdentityDTO = { + machineId: string; +} \ No newline at end of file diff --git a/frontend/src/hooks/api/organization/index.ts b/frontend/src/hooks/api/organization/index.ts index 576d59292..dcc5b7daf 100644 --- a/frontend/src/hooks/api/organization/index.ts +++ b/frontend/src/hooks/api/organization/index.ts @@ -6,6 +6,7 @@ export { useDeleteOrgById, useDeleteOrgPmtMethod, useDeleteOrgTaxId, + useGetMachineMembershipOrgs, useGetOrganizations, useGetOrgBillingDetails, useGetOrgInvoices, @@ -14,7 +15,6 @@ export { useGetOrgPlansTable, useGetOrgPlanTable, useGetOrgPmtMethods, - useGetOrgServiceMemberships, useGetOrgTaxIds, useGetOrgTrialUrl, useRenameOrg, diff --git a/frontend/src/hooks/api/organization/queries.tsx b/frontend/src/hooks/api/organization/queries.tsx index 77d6655ff..1e8f06e8e 100644 --- a/frontend/src/hooks/api/organization/queries.tsx +++ b/frontend/src/hooks/api/organization/queries.tsx @@ -2,7 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; -import { ServiceMembershipOrg } from "../serviceTokens/types"; +import { MachineMembershipOrg } from "../machineIdentities/types"; import { BillingDetails, Invoice, @@ -351,17 +351,17 @@ export const useGetOrgLicenses = (organizationId: string) => { }); }; -export const useGetOrgServiceMemberships = (organizationId: string) => { +export const useGetMachineMembershipOrgs = (organizationId: string) => { return useQuery({ queryKey: organizationKeys.getOrgServiceMemberships(organizationId), queryFn: async () => { const { - data: { serviceMemberships } - } = await apiRequest.get<{ serviceMemberships: ServiceMembershipOrg[] }>( - `/api/v2/organizations/${organizationId}/service-memberships` + data: { machineMemberships } + } = await apiRequest.get<{ machineMemberships: MachineMembershipOrg[] }>( + `/api/v2/organizations/${organizationId}/machine-memberships` ); - return serviceMemberships; + return machineMemberships; }, enabled: true }); diff --git a/frontend/src/hooks/api/serviceTokens/index.ts b/frontend/src/hooks/api/serviceTokens/index.ts index 01422d833..033e6ff1a 100644 --- a/frontend/src/hooks/api/serviceTokens/index.ts +++ b/frontend/src/hooks/api/serviceTokens/index.ts @@ -1,8 +1,5 @@ export { useCreateServiceToken, - useCreateServiceTokenV3, useDeleteServiceToken, - useDeleteServiceTokenV3, useGetUserWsServiceTokens, - useUpdateServiceTokenV3 } from "./queries"; diff --git a/frontend/src/hooks/api/serviceTokens/queries.tsx b/frontend/src/hooks/api/serviceTokens/queries.tsx index ccb93f925..0f67af47b 100644 --- a/frontend/src/hooks/api/serviceTokens/queries.tsx +++ b/frontend/src/hooks/api/serviceTokens/queries.tsx @@ -2,17 +2,12 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { apiRequest } from "@app/config/request"; -import { organizationKeys } from "../organization/queries"; import { - CreateServiceTokenDataV3DTO, - CreateServiceTokenDataV3Res, CreateServiceTokenDTO, CreateServiceTokenRes, - DeleteServiceTokenDataV3DTO, DeleteServiceTokenRes, - ServiceToken, - ServiceTokenDataV3, - UpdateServiceTokenDataV3DTO} from "./types"; + ServiceToken +} from "./types"; const serviceTokenKeys = { getAllWorkspaceServiceToken: (workspaceID: string) => [{ workspaceID }, "service-tokens"] as const @@ -64,63 +59,4 @@ export const useDeleteServiceToken = () => { queryClient.invalidateQueries(serviceTokenKeys.getAllWorkspaceServiceToken(workspace)); } }); -}; - -export const useCreateServiceTokenV3 = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async (body) => { - const { data } = await apiRequest.post("/api/v3/service-token/", body); - return data; - }, - onSuccess: ({ serviceTokenData }) => { - queryClient.invalidateQueries(organizationKeys.getOrgServiceMemberships(serviceTokenData.organization)); - } - }); -}; - -export const useUpdateServiceTokenV3 = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async ({ - serviceTokenDataId, - name, - role, - isActive, - trustedIps, - expiresIn, - accessTokenTTL, - isRefreshTokenRotationEnabled - }) => { - const { data: { serviceTokenData } } = await apiRequest.patch(`/api/v3/service-token/${serviceTokenDataId}`, { - name, - role, - isActive, - trustedIps, - expiresIn, - accessTokenTTL, - isRefreshTokenRotationEnabled - }); - - return serviceTokenData; - }, - onSuccess: ({ organization }) => { - queryClient.invalidateQueries(organizationKeys.getOrgServiceMemberships(organization)); - } - }); -}; - -export const useDeleteServiceTokenV3 = () => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async ({ - serviceTokenDataId - }) => { - const { data: { serviceTokenData } } = await apiRequest.delete(`/api/v3/service-token/${serviceTokenDataId}`); - return serviceTokenData; - }, - onSuccess: ({ organization }) => { - queryClient.invalidateQueries(organizationKeys.getOrgServiceMemberships(organization)); - } - }); }; \ No newline at end of file diff --git a/frontend/src/hooks/api/serviceTokens/types.ts b/frontend/src/hooks/api/serviceTokens/types.ts index 599ae950d..ea3223660 100644 --- a/frontend/src/hooks/api/serviceTokens/types.ts +++ b/frontend/src/hooks/api/serviceTokens/types.ts @@ -1,5 +1,3 @@ -import { TRole } from "../roles/types"; - export type ServiceTokenScope = { environment: string; secretPath: string; @@ -34,84 +32,4 @@ export type CreateServiceTokenRes = { serviceTokenData: ServiceToken; }; -export type DeleteServiceTokenRes = { serviceTokenData: ServiceToken }; - -// --- v3 - -export type ServiceTokenV3TrustedIp = { - _id: string; - ipAddress: string; - type: "ipv4" | "ipv6"; - prefix?: number; -} - -export type ServiceTokenDataV3 = { - _id: string; - name: string; - organization: string; - isActive: boolean; - refreshTokenLastUsed?: string; - accessTokenLastUsed?: string; - refreshTokenUsageCount: number; - accessTokenUsageCount: number; - trustedIps: ServiceTokenV3TrustedIp[]; - expiresAt?: string; - accessTokenTTL: number; - isRefreshTokenRotationEnabled: boolean; - createdAt: string; - updatedAt: string; -}; - -export type ServiceMembershipOrg = { - _id: string; - service: ServiceTokenDataV3; - organization: string; - role: "admin" | "member" | "viewer" | "custom"; - customRole?: TRole; - createdAt: string; - updatedAt: string; -} - -export type ServiceMembership = { - _id: string; - service: ServiceTokenDataV3; - organization: string; - role: "admin" | "member" | "viewer" | "custom"; - customRole?: TRole; - createdAt: string; - updatedAt: string; -} - -export type CreateServiceTokenDataV3DTO = { - name: string; - organizationId: string; - role?: string; - trustedIps: { - ipAddress: string; - }[]; - expiresIn?: number; - accessTokenTTL: number; - isRefreshTokenRotationEnabled: boolean; -} - -export type CreateServiceTokenDataV3Res = { - refreshToken: string; - serviceTokenData: ServiceTokenDataV3; -} - -export type UpdateServiceTokenDataV3DTO = { - serviceTokenDataId: string; - isActive?: boolean; - name?: string; - role?: string; - trustedIps?: { - ipAddress: string; - }[]; - expiresIn?: number; - accessTokenTTL?: number; - isRefreshTokenRotationEnabled?: boolean; -} - -export type DeleteServiceTokenDataV3DTO = { - serviceTokenDataId: string; -} \ No newline at end of file +export type DeleteServiceTokenRes = { serviceTokenData: ServiceToken }; \ No newline at end of file diff --git a/frontend/src/hooks/api/workspace/index.tsx b/frontend/src/hooks/api/workspace/index.tsx index 44fe58109..40c8c0587 100644 --- a/frontend/src/hooks/api/workspace/index.tsx +++ b/frontend/src/hooks/api/workspace/index.tsx @@ -1,9 +1,9 @@ export { - useAddServiceToWorkspace, + useAddMachineToWorkspace, useAddUserToWorkspace, useCreateWorkspace, useCreateWsEnvironment, - useDeleteServiceFromWorkspace, + useDeleteMachineFromWorkspace, useDeleteUserFromWorkspace, useDeleteWorkspace, useDeleteWsEnvironment, @@ -13,8 +13,8 @@ export { useGetWorkspaceById, useGetWorkspaceIndexStatus, useGetWorkspaceIntegrations, + useGetWorkspaceMachineMemberships, useGetWorkspaceSecrets, - useGetWorkspaceServiceMemberships, useGetWorkspaceUsers, useNameWorkspaceSecrets, useRenameWorkspace, diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index 624a7283c..8fe84c895 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -4,8 +4,8 @@ import { apiRequest } from "@app/config/request"; import { IntegrationAuth } from "../integrationAuth/types"; import { TIntegration } from "../integrations/types"; +import { MachineMembership } from "../machineIdentities/types"; import { EncryptedSecret } from "../secrets/types"; -import { ServiceMembership } from "../serviceTokens/types"; import { TWorkspaceUser } from "../users/types"; import { CreateEnvironmentDTO, @@ -31,8 +31,6 @@ export const workspaceKeys = { getAllUserWorkspace: ["workspaces"] as const, getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const, getWorkspaceUsers: (workspaceId: string) => [{ workspaceId }] as const, - getWorkspaceServiceTokenDataV3: (workspaceId: string) => - [{ workspaceId }, "workspace-service-token-data-v3"] as const, getWorkspaceServiceMemberships: (workspaceId: string) => [{ workspaceId }, "organization-service-memberships"] as const }; @@ -358,23 +356,22 @@ export const useUpdateUserWorkspaceRole = () => { }); }; -export const useAddServiceToWorkspace = () => { +export const useAddMachineToWorkspace = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ - serviceId, + machineId, workspaceId, role }: { - serviceId: string; + machineId: string; workspaceId: string; role?: string; }) => { const { data: { serviceMembership } - } = await apiRequest.post(`/api/v2/workspace/${workspaceId}/service-memberships`, { - serviceId, + } = await apiRequest.post(`/api/v2/workspace/${workspaceId}/machine-memberships/${machineId}`, { role }); @@ -386,22 +383,20 @@ export const useAddServiceToWorkspace = () => { }); }; -// TODO: update - -export const useDeleteServiceFromWorkspace = () => { +export const useDeleteMachineFromWorkspace = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ - serviceId, + machineId, workspaceId, }: { - serviceId: string; + machineId: string; workspaceId: string; }) => { const { data: { serviceMembership } - } = await apiRequest.delete(`/api/v2/workspace/${workspaceId}/service-memberships/${serviceId}`); + } = await apiRequest.delete(`/api/v2/workspace/${workspaceId}/machine-memberships/${machineId}`); return serviceMembership; }, @@ -411,18 +406,19 @@ export const useDeleteServiceFromWorkspace = () => { }); }; -export const useGetWorkspaceServiceMemberships = (workspaceId: string) => { + +export const useGetWorkspaceMachineMemberships = (workspaceId: string) => { return useQuery({ queryKey: workspaceKeys.getWorkspaceServiceMemberships(workspaceId), queryFn: async () => { const { - data: { serviceMemberships } - } = await apiRequest.get<{ serviceMemberships: ServiceMembership[] }>( - `/api/v2/workspace/${workspaceId}/service-memberships` + data: { machineMemberships } + } = await apiRequest.get<{ machineMemberships: MachineMembership[] }>( + `/api/v2/workspace/${workspaceId}/machine-memberships` ); - return serviceMemberships; + return machineMemberships; }, enabled: true }); diff --git a/frontend/src/views/Org/MembersPage/MembersPage.tsx b/frontend/src/views/Org/MembersPage/MembersPage.tsx index 907a06b50..b6338a96d 100644 --- a/frontend/src/views/Org/MembersPage/MembersPage.tsx +++ b/frontend/src/views/Org/MembersPage/MembersPage.tsx @@ -4,15 +4,14 @@ import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { withPermission } from "@app/hoc"; import { + OrgMachineIdentityTab, OrgMembersTab, - OrgRoleTabSection, - OrgServiceTokenTab -} from "./components"; + OrgRoleTabSection} from "./components"; enum TabSections { Member = "members", Roles = "roles", - ServiceTokens = "service-tokens" + MachineIdentities = "machine-identities" } export const MembersPage = withPermission( @@ -26,14 +25,14 @@ export const MembersPage = withPermission( People - Machine Identities + Machine Identities Roles - - + + diff --git a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/OrgServiceTokenTab.tsx b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/OrgMachineIdentityTab.tsx similarity index 71% rename from frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/OrgServiceTokenTab.tsx rename to frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/OrgMachineIdentityTab.tsx index a6977110e..6c3973c3b 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/OrgServiceTokenTab.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/OrgMachineIdentityTab.tsx @@ -1,8 +1,8 @@ import { motion } from "framer-motion"; -import { ServiceTokenV3Section } from "./components"; +import { MachineIdentitySection } from "./components"; -export const OrgServiceTokenTab = () => { +export const OrgMachineIdentityTab = () => { return ( { animate={{ opacity: 1, translateX: 0 }} exit={{ opacity: 0, translateX: 30 }} > - + ); } \ No newline at end of file diff --git a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/AddServiceTokenV3Modal.tsx b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/AddMachineIdentityModal.tsx similarity index 91% rename from frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/AddServiceTokenV3Modal.tsx rename to frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/AddMachineIdentityModal.tsx index 7ba34feb5..b76205f08 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/AddServiceTokenV3Modal.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/AddMachineIdentityModal.tsx @@ -28,9 +28,9 @@ import { } from "@app/context"; import { useToggle } from "@app/hooks"; import { - useCreateServiceTokenV3, + useCreateMachineIdentity, useGetRoles, - useUpdateServiceTokenV3 + useUpdateMachineIdentity } from "@app/hooks/api"; import { ServiceTokenV3TrustedIp } from "@app/hooks/api/serviceTokens/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -79,16 +79,17 @@ const schema = yup.object({ export type FormData = yup.InferType; type Props = { - popUp: UsePopUpState<["serviceTokenV3", "upgradePlan"]>; + popUp: UsePopUpState<["machineIdentity", "upgradePlan"]>; handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; - handlePopUpToggle: (popUpName: keyof UsePopUpState<["serviceTokenV3", "upgradePlan"]>, state?: boolean) => void; + handlePopUpToggle: (popUpName: keyof UsePopUpState<["machineIdentity", "upgradePlan"]>, state?: boolean) => void; }; -export const AddServiceTokenV3Modal = ({ +export const AddMachineIdentityModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => { + const { createNotification } = useNotificationContext(); const [newServiceTokenJSON, setNewServiceTokenJSON] = useState(""); const [isServiceTokenJSONCopied, setIsServiceTokenJSONCopied] = useToggle(false); @@ -101,9 +102,9 @@ export const AddServiceTokenV3Modal = ({ orgId }); - const { mutateAsync: createMutateAsync } = useCreateServiceTokenV3(); - const { mutateAsync: updateMutateAsync } = useUpdateServiceTokenV3(); - const { createNotification } = useNotificationContext(); + const { mutateAsync: createMutateAsync } = useCreateMachineIdentity(); + const { mutateAsync: updateMutateAsync } = useUpdateMachineIdentity(); + const { control, handleSubmit, @@ -136,8 +137,9 @@ export const AddServiceTokenV3Modal = ({ }; useEffect(() => { - const serviceTokenData = popUp?.serviceTokenV3?.data as { - serviceTokenDataId: string; + + const machineIdentity = popUp?.machineIdentity?.data as { + machineId: string; name: string; role: string; customRole: { @@ -148,15 +150,15 @@ export const AddServiceTokenV3Modal = ({ accessTokenTTL: number; isRefreshTokenRotationEnabled: boolean; }; - + if (!roles?.length) return; - if (serviceTokenData) { + if (machineIdentity) { reset({ - name: serviceTokenData.name, + name: machineIdentity.name, expiresIn: "", - role: serviceTokenData?.customRole?.slug ?? serviceTokenData.role, - trustedIps: serviceTokenData.trustedIps.map(({ + role: machineIdentity?.customRole?.slug ?? machineIdentity.role, + trustedIps: machineIdentity.trustedIps.map(({ ipAddress, prefix }: ServiceTokenV3TrustedIp) => { @@ -164,8 +166,8 @@ export const AddServiceTokenV3Modal = ({ ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}` }); }), - accessTokenTTL: String(serviceTokenData.accessTokenTTL), - isRefreshTokenRotationEnabled: serviceTokenData.isRefreshTokenRotationEnabled + accessTokenTTL: String(machineIdentity.accessTokenTTL), + isRefreshTokenRotationEnabled: machineIdentity.isRefreshTokenRotationEnabled }); } else { reset({ @@ -178,7 +180,7 @@ export const AddServiceTokenV3Modal = ({ }] }); } - }, [popUp?.serviceTokenV3?.data, roles]); + }, [popUp?.machineIdentity?.data, roles]); const { fields: tokenTrustedIps, append: appendTrustedIp, remove: removeTrustedIp } = useFieldArray({ control, name: "trustedIps" }); @@ -192,17 +194,17 @@ export const AddServiceTokenV3Modal = ({ }: FormData) => { try { - const serviceTokenData = popUp?.serviceTokenV3?.data as { - serviceTokenDataId: string; + const machineIdentity = popUp?.machineIdentity?.data as { + machineId: string; name: string; role: string; }; - if (serviceTokenData) { + if (machineIdentity) { // update await updateMutateAsync({ - serviceTokenDataId: serviceTokenData.serviceTokenDataId, + machineId: machineIdentity.machineId, name, role, trustedIps, @@ -211,7 +213,7 @@ export const AddServiceTokenV3Modal = ({ isRefreshTokenRotationEnabled }); - handlePopUpToggle("serviceTokenV3", false); + handlePopUpToggle("machineIdentity", false); } else { const { refreshToken } = await createMutateAsync({ @@ -228,7 +230,7 @@ export const AddServiceTokenV3Modal = ({ } createNotification({ - text: `Successfully ${popUp?.serviceTokenV3?.data ? "updated" : "created"} service account`, + text: `Successfully ${popUp?.machineIdentity?.data ? "updated" : "created"} machine identity`, type: "success" }); @@ -236,7 +238,7 @@ export const AddServiceTokenV3Modal = ({ } catch (err) { console.error(err); createNotification({ - text: `Failed to ${popUp?.serviceTokenV3?.data ? "updated" : "created"} service account`, + text: `Failed to ${popUp?.machineIdentity?.data ? "updated" : "created"} machine identity`, type: "error" }); } @@ -246,14 +248,14 @@ export const AddServiceTokenV3Modal = ({ return ( { - handlePopUpToggle("serviceTokenV3", isOpen); + handlePopUpToggle("machineIdentity", isOpen); reset(); setNewServiceTokenJSON(""); }} > - + {!hasServiceTokenJSON ? (
@@ -283,7 +285,7 @@ export const AddServiceTokenV3Modal = ({ > )} @@ -294,7 +296,7 @@ export const AddServiceTokenV3Modal = ({ defaultValue="" render={({ field: { onChange, ...field }, fieldState: { error } }) => ( ( - {popUp?.serviceTokenV3?.data ? "Update" : "Create"} + {popUp?.machineIdentity?.data ? "Update" : "Create"} diff --git a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/ServiceTokenV3Section.tsx b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/MachineIdentitySection.tsx similarity index 68% rename from frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/ServiceTokenV3Section.tsx rename to frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/MachineIdentitySection.tsx index 4bafbb09c..852e8d96a 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/ServiceTokenV3Section.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/MachineIdentitySection.tsx @@ -9,35 +9,33 @@ import { } from "@app/components/v2"; import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { withPermission } from "@app/hoc"; -import { - useDeleteServiceTokenV3 -} from "@app/hooks/api"; +import { useDeleteMachineIdentity } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; -import { AddServiceTokenV3Modal } from "./AddServiceTokenV3Modal"; -import { ServiceTokenV3Table } from "./ServiceTokenV3Table"; +import { AddMachineIdentityModal } from "./AddMachineIdentityModal"; +import { MachineIdentityTable } from "./MachineIdentityTable"; -export const ServiceTokenV3Section = withPermission( +export const MachineIdentitySection = withPermission( () => { const { createNotification } = useNotificationContext(); - const { mutateAsync: deleteMutateAsync } = useDeleteServiceTokenV3(); + const { mutateAsync: deleteMutateAsync } = useDeleteMachineIdentity(); const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - "serviceTokenV3", - "deleteServiceTokenV3", + "machineIdentity", + "deleteMachineIdentity", "upgradePlan" ] as const); - const onDeleteServiceTokenDataSubmit = async (serviceTokenDataId: string) => { + const onDeleteMachineIdentitySubmit = async (machineId: string) => { try { await deleteMutateAsync({ - serviceTokenDataId + machineId }); createNotification({ text: "Successfully deleted service token v3", type: "success" }); - handlePopUpClose("deleteServiceTokenV3"); + handlePopUpClose("deleteMachineIdentity"); } catch (err) { console.error(err); createNotification({ @@ -51,7 +49,7 @@ export const ServiceTokenV3Section = withPermission(

- Machines + Machine Identities (MIs)

} - onClick={() => handlePopUpOpen("serviceTokenV3")} + onClick={() => handlePopUpOpen("machineIdentity")} isDisabled={!isAllowed} > - Create account + Create MI )}
- - handlePopUpToggle("deleteServiceTokenV3", isOpen)} + onChange={(isOpen) => handlePopUpToggle("deleteMachineIdentity", isOpen)} deleteKey="confirm" onDeleteApproved={() => - onDeleteServiceTokenDataSubmit( - (popUp?.deleteServiceTokenV3?.data as { serviceTokenDataId: string })?.serviceTokenDataId + onDeleteMachineIdentitySubmit( + (popUp?.deleteMachineIdentity?.data as { machineId: string })?.machineId ) } /> diff --git a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/ServiceTokenV3Table.tsx b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/MachineIdentityTable.tsx similarity index 91% rename from frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/ServiceTokenV3Table.tsx rename to frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/MachineIdentityTable.tsx index 162441515..26a998197 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/ServiceTokenV3Table.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/MachineIdentityTable.tsx @@ -24,17 +24,18 @@ import { OrgPermissionSubjects, useOrganization} from "@app/context"; import { - useGetOrgServiceMemberships, + useGetMachineMembershipOrgs, useGetRoles, - useUpdateServiceTokenV3} from "@app/hooks/api"; + useUpdateMachineIdentity +} from "@app/hooks/api"; import { ServiceTokenV3TrustedIp } from "@app/hooks/api/serviceTokens/types" import { UsePopUpState } from "@app/hooks/usePopUp"; type Props = { handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["deleteServiceTokenV3", "serviceTokenV3"]>, + popUpName: keyof UsePopUpState<["deleteMachineIdentity", "machineIdentity"]>, data?: { - serviceTokenDataId?: string; + machineId?: string; name?: string; role?: string; customRole?: { @@ -48,42 +49,42 @@ type Props = { ) => void; }; -export const ServiceTokenV3Table = ({ +export const MachineIdentityTable = ({ handlePopUpOpen }: Props) => { const { createNotification } = useNotificationContext(); const { currentOrg } = useOrganization(); const orgId = currentOrg?._id || ""; - const { mutateAsync: updateMutateAsync } = useUpdateServiceTokenV3(); - const { data, isLoading } = useGetOrgServiceMemberships(currentOrg?._id || ""); + const { mutateAsync: updateMutateAsync } = useUpdateMachineIdentity(); + const { data, isLoading } = useGetMachineMembershipOrgs(currentOrg?._id || ""); const { data: roles } = useGetRoles({ orgId }); const handleChangeRole = async ({ - serviceTokenDataId, + machineId, role }: { - serviceTokenDataId: string; + machineId: string; role: string; }) => { try { await updateMutateAsync({ - serviceTokenDataId, + machineId, role }); createNotification({ - text: "Successfully updated service account role", + text: "Successfully updated machine identity role", type: "success" }); } catch (err) { console.error(err); createNotification({ - text: "Failed to update service account role", + text: "Failed to update machine identity role", type: "error" }); } @@ -138,12 +139,12 @@ export const ServiceTokenV3Table = ({ - {isLoading && } + {isLoading && } {!isLoading && data && data.length > 0 && data.map(({ - service: { + machineIdentity: { _id, name, // isActive, @@ -195,7 +196,7 @@ export const ServiceTokenV3Table = ({ dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800" onValueChange={(selectedRole) => handleChangeRole({ - serviceTokenDataId: _id, + machineId: _id, role: selectedRole }) } @@ -235,8 +236,8 @@ export const ServiceTokenV3Table = ({ {(isAllowed) => ( { - handlePopUpOpen("serviceTokenV3", { - serviceTokenDataId: _id, + handlePopUpOpen("machineIdentity", { + machineId: _id, name, role, customRole, @@ -262,8 +263,8 @@ export const ServiceTokenV3Table = ({ {(isAllowed) => ( { - handlePopUpOpen("deleteServiceTokenV3", { - serviceTokenDataId: _id, + handlePopUpOpen("deleteMachineIdentity", { + machineId: _id, name }); }} @@ -285,7 +286,7 @@ export const ServiceTokenV3Table = ({ {!isLoading && data && data?.length === 0 && ( - + )} diff --git a/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/index.tsx b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/index.tsx new file mode 100644 index 000000000..06de47d26 --- /dev/null +++ b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/MachineIdentitySection/index.tsx @@ -0,0 +1 @@ +export { MachineIdentitySection } from "./MachineIdentitySection"; \ No newline at end of file diff --git a/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/index.tsx b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/index.tsx new file mode 100644 index 000000000..06de47d26 --- /dev/null +++ b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/components/index.tsx @@ -0,0 +1 @@ +export { MachineIdentitySection } from "./MachineIdentitySection"; \ No newline at end of file diff --git a/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/index.tsx b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/index.tsx new file mode 100644 index 000000000..b66402617 --- /dev/null +++ b/frontend/src/views/Org/MembersPage/components/OrgMachineIdentityTab/index.tsx @@ -0,0 +1 @@ +export { OrgMachineIdentityTab } from "./OrgMachineIdentityTab"; \ No newline at end of file diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx index d475a4792..1dc85b74b 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx @@ -118,7 +118,7 @@ export const OrgMembersTable = ({ } } - const isLoading = isMembersLoading || IsWsMembershipLoading || isRolesLoading; + const isLoading = isMembersLoading || isRolesLoading; const isIamOwner = useMemo( () => members?.find(({ user: u }) => userId === u?._id)?.role === "owner", diff --git a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/index.tsx b/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/index.tsx deleted file mode 100644 index b6abc117c..000000000 --- a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/ServiceTokenV3Section/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ServiceTokenV3Section } from "./ServiceTokenV3Section"; \ No newline at end of file diff --git a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/index.tsx b/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/index.tsx deleted file mode 100644 index b6abc117c..000000000 --- a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/components/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ServiceTokenV3Section } from "./ServiceTokenV3Section"; \ No newline at end of file diff --git a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/index.tsx b/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/index.tsx deleted file mode 100644 index eab42b317..000000000 --- a/frontend/src/views/Org/MembersPage/components/OrgServiceTokenTab/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { OrgServiceTokenTab } from "./OrgServiceTokenTab"; \ No newline at end of file diff --git a/frontend/src/views/Org/MembersPage/components/index.tsx b/frontend/src/views/Org/MembersPage/components/index.tsx index 16f6a981b..03acfd92c 100644 --- a/frontend/src/views/Org/MembersPage/components/index.tsx +++ b/frontend/src/views/Org/MembersPage/components/index.tsx @@ -1,3 +1,3 @@ +export { OrgMachineIdentityTab } from "./OrgMachineIdentityTab"; export { OrgMembersTab } from "./OrgMembersTab"; -export { OrgRoleTabSection } from "./OrgRoleTabSection"; -export { OrgServiceTokenTab } from "./OrgServiceTokenTab"; \ No newline at end of file +export { OrgRoleTabSection } from "./OrgRoleTabSection"; \ No newline at end of file diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx b/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx index 52f35393a..e28e55306 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx @@ -167,19 +167,19 @@ export const LogsTableRow = ({

{`Name: ${event.metadata.name}`}

); - case EventType.CREATE_SERVICE_TOKEN_V3: + case EventType.CREATE_MACHINE_IDENTITY: return (

{`Name: ${event.metadata.name}`}

); - case EventType.UPDATE_SERVICE_TOKEN_V3: + case EventType.UPDATE_MACHINE_IDENTITY: return (

{`Name: ${event.metadata.name}`}

); - case EventType.DELETE_SERVICE_TOKEN_V3: + case EventType.DELETE_MACHINE_IDENTITY: return (

{`Name: ${event.metadata.name}`}

diff --git a/frontend/src/views/Project/MembersPage/MembersPage.tsx b/frontend/src/views/Project/MembersPage/MembersPage.tsx index aaa4d2f5a..4de7bc635 100644 --- a/frontend/src/views/Project/MembersPage/MembersPage.tsx +++ b/frontend/src/views/Project/MembersPage/MembersPage.tsx @@ -5,15 +5,17 @@ import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; import { withProjectPermission } from "@app/hoc"; -import { MemberListTab } from "./components/MemberListTab"; -import { ProjectRoleListTab } from "./components/ProjectRoleListTab"; -import { ServiceAccountTab } from "./components/ServiceAccountTab"; -import { ServiceTokenTab } from "./components/ServiceTokenTab"; +import { + MachineIdentityTab, + MemberListTab, + ProjectRoleListTab, + ServiceTokenTab +} from "./components"; enum TabSections { Member = "members", Roles = "roles", - ServiceAccounts = "service-accounts", + MachineIdentities = "machine-identities", ServiceTokens = "service-tokens" } @@ -27,8 +29,8 @@ export const MembersPage = withProjectPermission(

- Members - Service Accounts + People + Machine Identities Service Tokens Roles @@ -43,8 +45,8 @@ export const MembersPage = withProjectPermission( - - + + diff --git a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/ServiceAccountTab.tsx b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/MachineIdentityTab.tsx similarity index 69% rename from frontend/src/views/Project/MembersPage/components/ServiceAccountTab/ServiceAccountTab.tsx rename to frontend/src/views/Project/MembersPage/components/MachineIdentityTab/MachineIdentityTab.tsx index 53a666d69..a21d1e43e 100644 --- a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/ServiceAccountTab.tsx +++ b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/MachineIdentityTab.tsx @@ -1,19 +1,19 @@ import { motion } from "framer-motion"; import { - ServiceTokenV3Section + MachineIdentitySection } from "./components"; -export const ServiceAccountTab = () => { +export const MachineIdentityTab = () => { return ( - + ); } \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/AddServiceTokenV3Modal.tsx b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/AddMachineIdentityModal.tsx similarity index 69% rename from frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/AddServiceTokenV3Modal.tsx rename to frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/AddMachineIdentityModal.tsx index 124439b50..7bb828604 100644 --- a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/AddServiceTokenV3Modal.tsx +++ b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/AddMachineIdentityModal.tsx @@ -17,25 +17,27 @@ import { useOrganization, useWorkspace } from "@app/context"; -import { useAddServiceToWorkspace , - useGetOrgServiceMemberships, +import { + useAddMachineToWorkspace, + useGetMachineMembershipOrgs, useGetRoles, - useGetWorkspaceServiceMemberships} from "@app/hooks/api"; + useGetWorkspaceMachineMemberships +} from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; const schema = yup.object({ - serviceTokenDataId: yup.string().required("ST V3 id is required"), + machineId: yup.string().required("ST V3 id is required"), role: yup.string().required("ST V3 role is required") }).required(); export type FormData = yup.InferType; type Props = { - popUp: UsePopUpState<["serviceTokenV3"]>; - handlePopUpToggle: (popUpName: keyof UsePopUpState<["serviceTokenV3"]>, state?: boolean) => void; + popUp: UsePopUpState<["machineIdentity"]>; + handlePopUpToggle: (popUpName: keyof UsePopUpState<["machineIdentity"]>, state?: boolean) => void; }; -export const AddServiceTokenV3Modal = ({ +export const AddMachineIdentityModal = ({ popUp, handlePopUpToggle }: Props) => { @@ -47,26 +49,27 @@ export const AddServiceTokenV3Modal = ({ const orgId = currentOrg?._id || ""; const workspaceId = currentWorkspace?._id || ""; - const { data: orgServices } = useGetOrgServiceMemberships(orgId); - const { data: services } = useGetWorkspaceServiceMemberships(workspaceId); + const { data: machineMembershipOrgs } = useGetMachineMembershipOrgs(orgId); + const { data: machineMemberships } = useGetWorkspaceMachineMemberships(workspaceId); + const { data: roles } = useGetRoles({ orgId, workspaceId }); - const addServiceToWorkspace = useAddServiceToWorkspace(); + const { mutateAsync: addMachineToWorkspaceMutateAsync } = useAddMachineToWorkspace(); - const filteredOrgServices = useMemo(() => { - const wsServiceIds = new Map(); + const filteredMachineMembershipOrgs = useMemo(() => { + const wsMachineIds = new Map(); - services?.forEach((service) => { - wsServiceIds.set(service.service._id, true); + machineMemberships?.forEach((machineMembership) => { + wsMachineIds.set(machineMembership.machineIdentity._id, true); }); - return (orgServices || []).filter( - ({ service: s }) => !wsServiceIds.has(s._id) + return (machineMembershipOrgs || []).filter( + ({ machineIdentity: mi }) => !wsMachineIds.has(mi._id) ); - }, [orgServices, services]); + }, [machineMembershipOrgs, machineMemberships]); const { control, @@ -78,28 +81,27 @@ export const AddServiceTokenV3Modal = ({ }); const onFormSubmit = async ({ - serviceTokenDataId, + machineId, role }: FormData) => { try { - - await addServiceToWorkspace.mutateAsync({ + await addMachineToWorkspaceMutateAsync({ workspaceId, - serviceId: serviceTokenDataId as string, + machineId, role }); createNotification({ - text: "Successfully added service account to project", + text: "Successfully added machine identity to project", type: "success" }); reset(); - handlePopUpToggle("serviceTokenV3", false); + handlePopUpToggle("machineIdentity", false); } catch (err) { console.error(err); createNotification({ - text: "Failed to add service account to project", + text: "Failed to add machine identity to project", type: "error" }); } @@ -107,19 +109,19 @@ export const AddServiceTokenV3Modal = ({ return ( { - handlePopUpToggle("serviceTokenV3", isOpen); + handlePopUpToggle("machineIdentity", isOpen); reset(); }} > - - {filteredOrgServices.length ? ( + + {filteredMachineMembershipOrgs.length ? ( ( onChange(e)} className="w-full" > - {filteredOrgServices.map(({ service }) => ( - - {service.name} + {filteredMachineMembershipOrgs.map(({ machineIdentity }) => ( + + {machineIdentity.name} ))} @@ -175,7 +177,7 @@ export const AddServiceTokenV3Modal = ({ isLoading={isSubmitting} isDisabled={isSubmitting} > - {popUp?.serviceTokenV3?.data ? "Update" : "Create"} + {popUp?.machineIdentity?.data ? "Update" : "Create"} +
)} diff --git a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/ServiceTokenV3Section.tsx b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/MachineIdentitySection.tsx similarity index 71% rename from frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/ServiceTokenV3Section.tsx rename to frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/MachineIdentitySection.tsx index 1d1bff197..6702743ad 100644 --- a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/ServiceTokenV3Section.tsx +++ b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/MachineIdentitySection.tsx @@ -13,33 +13,33 @@ import { useWorkspace} from "@app/context"; import { withProjectPermission } from "@app/hoc"; import { - useDeleteServiceFromWorkspace + useDeleteMachineFromWorkspace } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; -import { AddServiceTokenV3Modal } from "./AddServiceTokenV3Modal"; -import { ServiceTokenV3Table } from "./ServiceTokenV3Table"; +import { AddMachineIdentityModal } from "./AddMachineIdentityModal"; +import { MachineIdentityTable } from "./MachineIdentityTable"; -export const ServiceTokenV3Section = withProjectPermission( +export const MachineIdentitySection = withProjectPermission( () => { const { createNotification } = useNotificationContext(); const { currentWorkspace } = useWorkspace(); const workspaceId = currentWorkspace?._id ?? ""; - const { mutateAsync: deleteMutateAsync } = useDeleteServiceFromWorkspace(); + const { mutateAsync: deleteMutateAsync } = useDeleteMachineFromWorkspace(); const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - "serviceTokenV3", - "deleteServiceTokenV3", + "machineIdentity", + "deleteMachineIdentity", "upgradePlan" ] as const); - const onRemoveServiceTokenDataSubmit = async (serviceTokenDataId: string) => { + const onRemoveServiceTokenDataSubmit = async (machineId: string) => { try { await deleteMutateAsync({ - serviceId: serviceTokenDataId, + machineId, workspaceId }); @@ -48,7 +48,7 @@ export const ServiceTokenV3Section = withProjectPermission( type: "success" }); - handlePopUpClose("deleteServiceTokenV3"); + handlePopUpClose("deleteMachineIdentity"); } catch (err) { console.error(err); createNotification({ @@ -62,7 +62,7 @@ export const ServiceTokenV3Section = withProjectPermission(

- Service Accounts + Machine Identities (MIs)

} - onClick={() => handlePopUpOpen("serviceTokenV3")} + onClick={() => handlePopUpOpen("machineIdentity")} isDisabled={!isAllowed} > - Add service account + Add machine identity )}
- - handlePopUpToggle("deleteServiceTokenV3", isOpen)} + onChange={(isOpen) => handlePopUpToggle("deleteMachineIdentity", isOpen)} deleteKey="confirm" onDeleteApproved={() => onRemoveServiceTokenDataSubmit( - (popUp?.deleteServiceTokenV3?.data as { serviceTokenDataId: string })?.serviceTokenDataId + (popUp?.deleteMachineIdentity?.data as { machineId: string })?.machineId ) } /> diff --git a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/ServiceTokenV3Table.tsx b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/MachineIdentityTable.tsx similarity index 87% rename from frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/ServiceTokenV3Table.tsx rename to frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/MachineIdentityTable.tsx index 0e91bae16..2096411d0 100644 --- a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/ServiceTokenV3Table.tsx +++ b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/MachineIdentityTable.tsx @@ -27,30 +27,30 @@ import { } from "@app/context"; import { useGetRoles, - useGetWorkspaceServiceMemberships} from "@app/hooks/api"; -import { ServiceTokenV3TrustedIp } from "@app/hooks/api/serviceTokens/types" + useGetWorkspaceMachineMemberships +} from "@app/hooks/api"; +import { MachineTrustedIp} from "@app/hooks/api/machineIdentities/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; type Props = { handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["deleteServiceTokenV3", "serviceTokenV3"]>, + popUpName: keyof UsePopUpState<["deleteMachineIdentity", "machineIdentity"]>, data?: { - serviceTokenDataId?: string; + machineId?: string; name?: string; role?: string; customRole?: { name: string; slug: string; }; - trustedIps?: ServiceTokenV3TrustedIp[]; + trustedIps?: MachineTrustedIp[]; accessTokenTTL?: number; isRefreshTokenRotationEnabled?: boolean; } ) => void; }; -// TODO: update roles thing here -export const ServiceTokenV3Table = ({ +export const MachineIdentityTable = ({ handlePopUpOpen }: Props) => { const { createNotification } = useNotificationContext(); @@ -59,7 +59,7 @@ export const ServiceTokenV3Table = ({ const orgId = currentOrg?._id || ""; const workspaceId = currentWorkspace?._id || ""; - const { data, isLoading } = useGetWorkspaceServiceMemberships(currentWorkspace?._id || ""); + const { data, isLoading } = useGetWorkspaceMachineMemberships(currentWorkspace?._id || ""); const { data: roles } = useGetRoles({ orgId, @@ -67,33 +67,35 @@ export const ServiceTokenV3Table = ({ }); const handleChangeRole = async ({ - serviceTokenDataId, + machineId, role }: { - serviceTokenDataId: string; + machineId: string; role: string; }) => { try { console.log("handle project-level role change vals: ", { - serviceTokenDataId, + machineId, role }); + // TODO: change role + // await updateMutateAsync({ // serviceTokenDataId, // role // }); createNotification({ - text: "Successfully updated service account role", + text: "Successfully updated machine identity role", type: "success" }); } catch (err) { console.error(err); createNotification({ - text: "Failed to update service account role", + text: "Failed to update machine identity role", type: "error" }); } @@ -118,12 +120,12 @@ export const ServiceTokenV3Table = ({ - {isLoading && } + {isLoading && } {!isLoading && data && data.length > 0 && data.map(({ - service: { + machineIdentity: { _id, name }, @@ -150,7 +152,7 @@ export const ServiceTokenV3Table = ({ dropdownContainerClassName="border border-mineshaft-600 bg-mineshaft-800" onValueChange={(selectedRole) => handleChangeRole({ - serviceTokenDataId: _id, + machineId: _id, role: selectedRole }) } @@ -175,8 +177,8 @@ export const ServiceTokenV3Table = ({ {(isAllowed) => ( { - handlePopUpOpen("deleteServiceTokenV3", { - serviceTokenDataId: _id, + handlePopUpOpen("deleteMachineIdentity", { + machineId: _id, name }); }} @@ -198,7 +200,7 @@ export const ServiceTokenV3Table = ({ {!isLoading && data && data?.length === 0 && ( - + )} diff --git a/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/index.tsx b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/index.tsx new file mode 100644 index 000000000..06de47d26 --- /dev/null +++ b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/MachineIdentitySection/index.tsx @@ -0,0 +1 @@ +export { MachineIdentitySection } from "./MachineIdentitySection"; \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/index.tsx b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/index.tsx new file mode 100644 index 000000000..06de47d26 --- /dev/null +++ b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/components/index.tsx @@ -0,0 +1 @@ +export { MachineIdentitySection } from "./MachineIdentitySection"; \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/index.tsx b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/index.tsx new file mode 100644 index 000000000..568043da2 --- /dev/null +++ b/frontend/src/views/Project/MembersPage/components/MachineIdentityTab/index.tsx @@ -0,0 +1 @@ +export { MachineIdentityTab } from "./MachineIdentityTab"; \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/index.tsx b/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/index.tsx deleted file mode 100644 index b6abc117c..000000000 --- a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/ServiceTokenV3Section/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ServiceTokenV3Section } from "./ServiceTokenV3Section"; \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/index.tsx b/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/index.tsx deleted file mode 100644 index b6abc117c..000000000 --- a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/components/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ServiceTokenV3Section } from "./ServiceTokenV3Section"; \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/index.tsx b/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/index.tsx deleted file mode 100644 index a34f3c17b..000000000 --- a/frontend/src/views/Project/MembersPage/components/ServiceAccountTab/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { ServiceAccountTab } from "./ServiceAccountTab"; \ No newline at end of file diff --git a/frontend/src/views/Project/MembersPage/components/index.tsx b/frontend/src/views/Project/MembersPage/components/index.tsx new file mode 100644 index 000000000..f6281751f --- /dev/null +++ b/frontend/src/views/Project/MembersPage/components/index.tsx @@ -0,0 +1,4 @@ +export { MachineIdentityTab } from "./MachineIdentityTab"; +export { MemberListTab } from "./MemberListTab"; +export { ProjectRoleListTab } from "./ProjectRoleListTab"; +export { ServiceTokenTab } from "./ServiceTokenTab"; \ No newline at end of file