Update authz logic for MI

This commit is contained in:
Tuan Dang
2023-12-05 17:46:50 +07:00
parent c91f6521c1
commit 6787c0eaaa
13 changed files with 209 additions and 115 deletions

View File

@@ -1,6 +1,7 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import {
IMachineIdentity,
Key,
MachineIdentity,
MachineMembership,
@@ -8,7 +9,7 @@ import {
ServiceTokenData,
Workspace
} from "../../models";
import { Role } from "../../ee/models";
import { IRole, Role } from "../../ee/models";
import {
pullSecrets as pull,
v2PushSecrets as push,
@@ -534,24 +535,26 @@ export const addMachineToWorkspace = async (req: Request, res: Response) => {
});
if (machineMembership) throw BadRequestError({
message: `Machine identity with id ${machineId} already exists in workspace with id ${workspaceId}`
message: `Machine identity with id ${machineId} already exists in project with id ${workspaceId}`
});
const machineIdentity = await MachineIdentity.findById(machineId);
if (!machineIdentity) throw ResourceNotFoundError();
if (!machineIdentity) throw ResourceNotFoundError({
message: `Failed to find machine identity with id ${machineId}`
});
const workspace = await Workspace.findById(workspaceId);
if (!workspace) throw ResourceNotFoundError();
if (!machineIdentity.organization.equals(workspace.organization)) throw BadRequestError({
message: "Failed to add machine identity to workspace in another organization"
message: "Failed to add machine identity to project in another organization"
});
const rolePermission = await getRolePermissions(role, workspaceId);
const hasRequiredPrivileges = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
message: "Failed to add a more privileged MI to project"
if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({
message: "Failed to add MI to project with more privileged role"
});
let customRole;
@@ -604,29 +607,33 @@ export const addMachineToWorkspace = async (req: Request, res: Response) => {
ProjectPermissionSub.MachineIdentity
);
let machineMembership = await MachineMembership.findOne({
machineIdentity: new Types.ObjectId(machineId),
workspace: new Types.ObjectId(workspaceId)
});
let machineMembership = await MachineMembership
.findOne({
machineIdentity: new Types.ObjectId(machineId),
workspace: new Types.ObjectId(workspaceId)
})
.populate<{
machineIdentity: IMachineIdentity,
customRole: IRole
}>("machineIdentity customRole");
if (!machineMembership) throw BadRequestError({
message: `Machine identity with id ${machineId} does not exist in workspace with id ${workspaceId}`
message: `Machine identity with id ${machineId} does not exist in project with id ${workspaceId}`
});
const machineIdentity = await MachineIdentity.findById(machineId);
if (!machineIdentity) throw ResourceNotFoundError();
const workspace = await Workspace.findById(workspaceId);
if (!workspace) throw ResourceNotFoundError();
if (!machineIdentity.organization.equals(workspace.organization)) throw BadRequestError({
message: "Failed to update machine identity in workspace in another organization"
const machineIdentityRolePermission = await getRolePermissions(
machineMembership?.customRole?.slug ?? machineMembership.role,
machineMembership.workspace.toString()
);
const isAsPrivilegedAsMachine = isAtLeastAsPrivilegedWorkspace(permission, machineIdentityRolePermission);
if (!isAsPrivilegedAsMachine) throw ForbiddenRequestError({
message: "Failed to update role of more privileged MI"
});
const rolePermission = await getRolePermissions(role, workspaceId);
const hasRequiredPrivileges = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
const isAsPrivilegedAsIntendedRole = isAtLeastAsPrivilegedWorkspace(permission, rolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
if (!isAsPrivilegedAsIntendedRole) throw ForbiddenRequestError({
message: "Failed to update MI to a more privileged role"
});
@@ -646,7 +653,7 @@ export const addMachineToWorkspace = async (req: Request, res: Response) => {
machineMembership = await MachineMembership.findOneAndUpdate(
{
machineIdentity: machineIdentity._id,
machineIdentity: machineMembership.machineIdentity,
workspace: new Types.ObjectId(workspaceId),
},
{
@@ -684,12 +691,30 @@ export const addMachineToWorkspace = async (req: Request, res: Response) => {
ProjectPermissionSub.MachineIdentity
);
const machineMembership = await MachineMembership.findOneAndDelete({
machineIdentity: new Types.ObjectId(machineId),
workspace: new Types.ObjectId(workspaceId)
const machineMembership = await MachineMembership
.findOne({
machineIdentity: new Types.ObjectId(machineId),
workspace: new Types.ObjectId(workspaceId)
})
.populate<{
machineIdentity: IMachineIdentity,
customRole: IRole
}>("machineIdentity customRole");
if (!machineMembership) throw ResourceNotFoundError({
message: `Machine with id ${machineId} does not exist in project with id ${workspaceId}`
});
if (!machineMembership) throw ResourceNotFoundError();
const machineIdentityRolePermission = await getRolePermissions(
machineMembership?.customRole?.slug ?? machineMembership.role,
machineMembership.workspace.toString()
);
const isAsPrivilegedAsMachine = isAtLeastAsPrivilegedWorkspace(permission, machineIdentityRolePermission);
if (!isAsPrivilegedAsMachine) throw ForbiddenRequestError({
message: "Failed to remove more privileged MI from project"
});
await MachineMembership.findByIdAndDelete(machineMembership._id);
return res.status(200).send({
machineMembership

View File

@@ -4,10 +4,10 @@ import { Request, Response } from "express";
import { Types } from "mongoose";
import {
IMachineIdentity,
IMachineIdentityClientSecretData,
IMachineIdentityClientSecret,
IMachineIdentityTrustedIp,
MachineIdentity,
MachineIdentityClientSecretData,
MachineIdentityClientSecret,
MachineMembership,
MachineMembershipOrg,
Organization,
@@ -15,6 +15,7 @@ import {
import {
ActorType,
EventType,
IRole,
Role
} from "../../models";
import { validateRequest } from "../../../helpers/validation";
@@ -25,7 +26,12 @@ import {
getUserOrgPermissions,
isAtLeastAsPrivilegedOrg
} from "../../services/RoleService";
import { BadRequestError, ForbiddenRequestError, ResourceNotFoundError, UnauthorizedRequestError } from "../../../utils/errors";
import {
BadRequestError,
ForbiddenRequestError,
ResourceNotFoundError,
UnauthorizedRequestError
} from "../../../utils/errors";
import { extractIPDetails, isValidIpOrCidr } from "../../../utils/ip";
import { EEAuditLogService, EELicenseService } from "../../services";
import { getAuthSecret, getSaltRounds } from "../../../config";
@@ -38,15 +44,15 @@ import { ForbiddenError } from "@casl/ability";
import { checkIPAgainstBlocklist } from "../../../utils/ip";
import { getUserAgentType } from "../../../utils/posthog";
const packageClientSecretData = (clientSecretData: IMachineIdentityClientSecretData) => ({
_id: clientSecretData._id,
machineIdentity: clientSecretData.machineIdentity,
isActive: clientSecretData.isActive,
description: clientSecretData.description,
clientSecretPrefix: clientSecretData.clientSecretPrefix,
clientSecretNumUses: clientSecretData.clientSecretNumUses,
clientSecretNumUsesLimit: clientSecretData.clientSecretNumUsesLimit,
clientSecretTTL: clientSecretData.clientSecretTTL
const packageClientSecretData = (machineIdentityClientSecret: IMachineIdentityClientSecret) => ({
_id: machineIdentityClientSecret._id,
machineIdentity: machineIdentityClientSecret.machineIdentity,
isActive: machineIdentityClientSecret.isActive,
description: machineIdentityClientSecret.description,
clientSecretPrefix: machineIdentityClientSecret.clientSecretPrefix,
clientSecretNumUses: machineIdentityClientSecret.clientSecretNumUses,
clientSecretNumUsesLimit: machineIdentityClientSecret.clientSecretNumUsesLimit,
clientSecretTTL: machineIdentityClientSecret.clientSecretTTL
});
/**
@@ -63,7 +69,10 @@ export const getMIClientSecrets = async (req: Request, res: Response) => {
const machineMembershipOrg = await MachineMembershipOrg.findOne({
machineIdentity: new Types.ObjectId(machineId)
}).populate<{ machineIdentity: IMachineIdentity }>("machineIdentity");
}).populate<{
machineIdentity: IMachineIdentity,
customRole: IRole
}>("machineIdentity customRole");
if (!machineMembershipOrg) throw ResourceNotFoundError();
@@ -74,14 +83,17 @@ export const getMIClientSecrets = async (req: Request, res: Response) => {
OrgPermissionSubjects.MachineIdentity
);
const rolePermission = await getOrgRolePermissions(machineMembershipOrg.role, machineMembershipOrg.organization.toString());
const rolePermission = await getOrgRolePermissions(
machineMembershipOrg?.customRole?.slug ?? machineMembershipOrg.role,
machineMembershipOrg.organization.toString()
);
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
message: "Failed to get client secrets for more privileged MI"
});
const clientSecretData = await MachineIdentityClientSecretData
const clientSecretData = await MachineIdentityClientSecret
.find({
machineIdentity: machineMembershipOrg.machineIdentity,
isActive: true
@@ -127,7 +139,10 @@ export const createMIClientSecret = async (req: Request, res: Response) => {
const machineMembershipOrg = await MachineMembershipOrg.findOne({
machineIdentity: new Types.ObjectId(machineId)
}).populate<{ machineIdentity: IMachineIdentity }>("machineIdentity");
}).populate<{
machineIdentity: IMachineIdentity,
customRole: IRole
}>("machineIdentity customRole");
if (!machineMembershipOrg) throw ResourceNotFoundError();
@@ -138,7 +153,10 @@ export const createMIClientSecret = async (req: Request, res: Response) => {
OrgPermissionSubjects.MachineIdentity
);
const rolePermission = await getOrgRolePermissions(machineMembershipOrg.role, machineMembershipOrg.organization.toString());
const rolePermission = await getOrgRolePermissions(
machineMembershipOrg?.customRole?.slug ?? machineMembershipOrg.role,
machineMembershipOrg.organization.toString()
);
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
@@ -148,7 +166,7 @@ export const createMIClientSecret = async (req: Request, res: Response) => {
const clientSecret = crypto.randomBytes(32).toString("hex");
const clientSecretHash = await bcrypt.hash(clientSecret, await getSaltRounds());
const machineIdentityClientSecretData = await new MachineIdentityClientSecretData({
const machineIdentityClientSecret = await new MachineIdentityClientSecret({
machineIdentity: machineMembershipOrg.machineIdentity,
isActive: true,
description,
@@ -167,7 +185,7 @@ export const createMIClientSecret = async (req: Request, res: Response) => {
metadata: {
machineId: machineMembershipOrg.machineIdentity._id.toString(),
clientId: machineMembershipOrg.machineIdentity.clientId,
clientSecretId: machineIdentityClientSecretData._id.toString()
clientSecretId: machineIdentityClientSecret._id.toString()
}
},
{
@@ -177,7 +195,7 @@ export const createMIClientSecret = async (req: Request, res: Response) => {
return res.status(200).send({
clientSecret,
clientSecretData: packageClientSecretData(machineIdentityClientSecretData)
clientSecretData: packageClientSecretData(machineIdentityClientSecret)
});
}
@@ -194,11 +212,18 @@ export const deleteMIClientSecret = async (req: Request, res: Response) => {
}
} = await validateRequest(reqValidator.DeleteClientSecretV3, req);
const machineMembershipOrg = await MachineMembershipOrg.findOne({
machineIdentity: new Types.ObjectId(machineId)
}).populate<{ machineIdentity: IMachineIdentity }>("machineIdentity");
const machineMembershipOrg = await MachineMembershipOrg
.findOne({
machineIdentity: new Types.ObjectId(machineId)
})
.populate<{
machineIdentity: IMachineIdentity,
customRole: IRole
}>("machineIdentity customRole");
if (!machineMembershipOrg) throw ResourceNotFoundError();
if (!machineMembershipOrg) throw ResourceNotFoundError({
message: `Failed to find machine identity with id ${machineId}`
});
const { permission } = await getUserOrgPermissions(req.user._id, machineMembershipOrg.organization.toString());
@@ -207,19 +232,22 @@ export const deleteMIClientSecret = async (req: Request, res: Response) => {
OrgPermissionSubjects.MachineIdentity
);
const rolePermission = await getOrgRolePermissions(machineMembershipOrg.role, machineMembershipOrg.organization.toString());
const rolePermission = await getOrgRolePermissions(
machineMembershipOrg?.customRole?.slug ?? machineMembershipOrg.role,
machineMembershipOrg.organization.toString()
);
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
message: "Failed to delete client secrets for more privileged MI"
});
const clientSecretData = await MachineIdentityClientSecretData.findOneAndDelete({
const machineIdentityClientSecret = await MachineIdentityClientSecret.findOneAndDelete({
_id: clientSecretId,
machineIdentity: machineId
});
if (!clientSecretData) throw ResourceNotFoundError();
if (!machineIdentityClientSecret) throw ResourceNotFoundError();
await EEAuditLogService.createAuditLog(
req.authData,
@@ -237,7 +265,7 @@ export const deleteMIClientSecret = async (req: Request, res: Response) => {
);
return res.status(200).send({
clientSecretData: packageClientSecretData(clientSecretData)
clientSecretData: packageClientSecretData(machineIdentityClientSecret)
})
}
@@ -267,12 +295,12 @@ export const loginMI = async (req: Request, res: Response) => {
trustedIps: machineIdentity.clientSecretTrustedIps
});
const clientSecretData = await MachineIdentityClientSecretData.find({
const clientSecretData = await MachineIdentityClientSecret.find({
machineIdentity: machineIdentity._id,
isActive: true
});
let validatedClientSecretDatum: IMachineIdentityClientSecretData | undefined;
let validatedClientSecretDatum: IMachineIdentityClientSecret | undefined;
for (const clientSecretDatum of clientSecretData) {
const isSecretValid = await bcrypt.compare(
@@ -298,13 +326,10 @@ export const loginMI = async (req: Request, res: Response) => {
const expiresAt = new Date(new Date().getTime() + clientSecretTTL * 1000);
if (expiresAt < new Date()) {
await MachineIdentityClientSecretData.findByIdAndUpdate(
await MachineIdentityClientSecret.findByIdAndUpdate(
validatedClientSecretDatum._id,
{
isActive: false
},
{
new: true
}
);
@@ -315,7 +340,7 @@ export const loginMI = async (req: Request, res: Response) => {
if (clientSecretNumUses > 0 && clientSecretNumUses === clientSecretNumUsesLimit) {
// number of times client secret can be used for
// a login operation reached
await MachineIdentityClientSecretData.findByIdAndUpdate(
await MachineIdentityClientSecret.findByIdAndUpdate(
validatedClientSecretDatum._id,
{
isActive: false
@@ -329,7 +354,7 @@ export const loginMI = async (req: Request, res: Response) => {
}
// increment usage count by 1
await MachineIdentityClientSecretData.findByIdAndUpdate(
await MachineIdentityClientSecret.findByIdAndUpdate(
validatedClientSecretDatum._id,
{
$inc: { clientSecretNumUses: 1 }
@@ -342,7 +367,7 @@ export const loginMI = async (req: Request, res: Response) => {
// token version
const accessToken = createToken({
payload: {
machineId: machineIdentity._id.toString(), // consider changing to clientId and making it more extensible
machineId: machineIdentity._id.toString(),
clientSecretDataId: validatedClientSecretDatum._id.toString(),
authTokenType: AuthTokenType.MACHINE_ACCESS_TOKEN,
tokenVersion: validatedClientSecretDatum.accessTokenVersion
@@ -525,22 +550,36 @@ export const updateMachineIdentity = async (req: Request, res: Response) => {
}
} = await validateRequest(reqValidator.UpdateMachineIdentityV3, req);
let machineIdentity = await MachineIdentity.findById(machineId);
if (!machineIdentity) throw ResourceNotFoundError({
message: `Machine identity with id ${machineId} not found`
const machineMembershipOrg = await MachineMembershipOrg
.findOne({
machineIdentity: new Types.ObjectId(machineId)
})
.populate<{
machineIdentity: IMachineIdentity,
customRole: IRole
}>("machineIdentity customRole");
if (!machineMembershipOrg) throw ResourceNotFoundError({
message: `Failed to find machine identity with id ${machineId}`
});
// TODO: validate existing role (if it is currently admin then cant demote it)
const { permission } = await getUserOrgPermissions(req.user._id, machineIdentity.organization.toString());
const { permission } = await getUserOrgPermissions(req.user._id, machineMembershipOrg.organization.toString());
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Edit,
OrgPermissionSubjects.MachineIdentity
);
const machineIdentityRolePermission = await getOrgRolePermissions(
machineMembershipOrg?.customRole?.slug ?? machineMembershipOrg.role,
machineMembershipOrg.organization.toString()
);
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, machineIdentityRolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
message: "Failed to update more privileged MI"
});
if (role) {
const rolePermission = await getOrgRolePermissions(role, machineIdentity.organization.toString());
const rolePermission = await getOrgRolePermissions(role, machineMembershipOrg.organization.toString());
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, rolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
@@ -555,14 +594,14 @@ export const updateMachineIdentity = async (req: Request, res: Response) => {
customRole = await Role.findOne({
slug: role,
isOrgRole: true,
organization: machineIdentity.organization
organization: machineMembershipOrg.organization
});
if (!customRole) throw BadRequestError({ message: "Role not found" });
}
}
const plan = await EELicenseService.getPlan(machineIdentity.organization);
const plan = await EELicenseService.getPlan(machineMembershipOrg.organization);
// validate client secret trusted ips
let reformattedClientSecretTrustedIps;
@@ -600,7 +639,7 @@ export const updateMachineIdentity = async (req: Request, res: Response) => {
});
}
machineIdentity = await MachineIdentity.findByIdAndUpdate(
const machineIdentity = await MachineIdentity.findByIdAndUpdate(
machineId,
{
name,
@@ -669,38 +708,51 @@ export const deleteMachineIdentity = async (req: Request, res: Response) => {
params: { machineId }
} = await validateRequest(reqValidator.DeleteMachineIdentityV3, req);
let machineIdentity = await MachineIdentity.findById(machineId);
if (!machineIdentity) throw ResourceNotFoundError({
message: `Machine identity with id ${machineId} not found`
});
const { permission } = await getUserOrgPermissions(req.user._id, machineIdentity.organization.toString());
const machineMembershipOrg = await MachineMembershipOrg
.findOne({
machineIdentity: new Types.ObjectId(machineId)
})
.populate<{
machineIdentity: IMachineIdentity,
customRole: IRole
}>("machineIdentity customRole");
if (!machineMembershipOrg) throw ResourceNotFoundError({
message: `Failed to find machine identity with id ${machineId}`
});
const { permission } = await getUserOrgPermissions(req.user._id, machineMembershipOrg.organization.toString());
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Delete,
OrgPermissionSubjects.MachineIdentity
);
const machineIdentityRolePermission = await getOrgRolePermissions(
machineMembershipOrg?.customRole?.slug ?? machineMembershipOrg.role,
machineMembershipOrg.organization.toString()
);
const hasRequiredPrivileges = isAtLeastAsPrivilegedOrg(permission, machineIdentityRolePermission);
if (!hasRequiredPrivileges) throw ForbiddenRequestError({
message: "Failed to delete more privileged MI"
});
machineIdentity = await MachineIdentity.findByIdAndDelete(machineId);
if (!machineIdentity) throw BadRequestError({
message: "Failed to delete service token"
const machineIdentity = await MachineIdentity.findByIdAndDelete(machineMembershipOrg.machineIdentity);
if (!machineIdentity) throw ResourceNotFoundError({
message: `Machine identity with id ${machineId} not found`
});
const machineMembershipOrg = await MachineMembershipOrg.findOneAndDelete({
machineIdentity: machineIdentity._id,
});
await MachineMembershipOrg.findByIdAndDelete(machineMembershipOrg._id);
if (!machineMembershipOrg) throw BadRequestError({
message: "Failed to delete service token"
message: `Failed to delete machine identity with id ${machineId}`
});
await MachineMembership.deleteMany({
machineIdentity: machineIdentity._id,
machineIdentity: machineMembershipOrg.machineIdentity
});
await MachineIdentityClientSecretData.deleteMany({
machineIdentity: machineIdentity._id
await MachineIdentityClientSecret.deleteMany({
machineIdentity: machineMembershipOrg.machineIdentity
});
await EEAuditLogService.createAuditLog(

View File

@@ -28,7 +28,7 @@ router.delete(
machineIdentityController.deleteMIClientSecret
);
// consider moving to /auth/app/login
// consider moving to /auth/machine/login
router.post(
"/login",
machineIdentityController.loginMI

View File

@@ -66,7 +66,7 @@ class EELicenseService {
secretVersioning: true,
pitRecovery: false,
ipAllowlisting: false,
rbac: false,
rbac: true,
customRateLimits: false,
customAlerts: false,
auditLogs: false,

View File

@@ -206,7 +206,7 @@ const main = async () => {
app.use("/api/v1/sso", eeSSORouter);
app.use("/api/v1/cloud-products", eeCloudProductsRouter);
app.use("/api/v3/api-key", v3apiKeyDataRouter);
app.use("/api/v3/machines", v3MachineIdentityRouter);
app.use("/api/v3/machines", v3MachineIdentityRouter); // TODO: consider moving to v1
app.use("/api/v1/secret-rotation-providers", v1SecretRotationProviderRouter);
app.use("/api/v1/secret-rotations", v1SecretRotation);

View File

@@ -21,7 +21,7 @@ export * from "./userAction";
export * from "./workspace";
export * from "./serviceTokenData"; // TODO: deprecate
export * from "./machineIdentity";
export * from "./machineIdentityClientSecretData";
export * from "./machineIdentityClientSecret";
export * from "./machineMembershipOrg";
export * from "./machineMembership";
export * from "./apiKeyData"; // TODO: deprecate

View File

@@ -1,6 +1,6 @@
import { Document, Schema, Types, model } from "mongoose";
export interface IMachineIdentityClientSecretData extends Document {
export interface IMachineIdentityClientSecret extends Document {
_id: Types.ObjectId;
machineIdentity: Types.ObjectId;
isActive: boolean;
@@ -16,7 +16,7 @@ export interface IMachineIdentityClientSecretData extends Document {
createdAt: Date;
}
const machineIdentityClientSecretDataSchema = new Schema(
const machineIdentityClientSecretSchema = new Schema(
{
machineIdentity: {
type: Schema.Types.ObjectId,
@@ -74,8 +74,8 @@ const machineIdentityClientSecretDataSchema = new Schema(
}
);
machineIdentityClientSecretDataSchema.index(
machineIdentityClientSecretSchema.index(
{ machineIdentity: 1, isActive: 1 }
)
export const MachineIdentityClientSecretData = model<IMachineIdentityClientSecretData>("MachineIdentityClientSecretData", machineIdentityClientSecretDataSchema);
export const MachineIdentityClientSecret = model<IMachineIdentityClientSecret>("MachineIdentityClientSecret", machineIdentityClientSecretSchema);

View File

@@ -1,6 +1,6 @@
import jwt from "jsonwebtoken";
import { Types } from "mongoose";
import { MachineIdentity, MachineIdentityClientSecretData } from "../../../models";
import { MachineIdentity, MachineIdentityClientSecret } from "../../../models";
import { getAuthSecret } from "../../../config";
import { AuthTokenType } from "../../../variables";
import { UnauthorizedRequestError } from "../../errors";
@@ -18,14 +18,14 @@ export const validateMachineIdentity = async ({
if (decodedToken.authTokenType !== AuthTokenType.MACHINE_ACCESS_TOKEN) throw UnauthorizedRequestError();
const machineIdentityClientSecretData = await MachineIdentityClientSecretData.findOne({
const machineIdentityClientSecret = await MachineIdentityClientSecret.findOne({
_id: new Types.ObjectId(decodedToken.clientSecretDataId),
isActive: true
});
if (!machineIdentityClientSecretData) throw UnauthorizedRequestError();
if (!machineIdentityClientSecret) throw UnauthorizedRequestError();
if (decodedToken.tokenVersion !== machineIdentityClientSecretData.accessTokenVersion) {
if (decodedToken.tokenVersion !== machineIdentityClientSecret.accessTokenVersion) {
// TODO: raise alarm
throw UnauthorizedRequestError({
message: "Failed to authenticate",
@@ -33,7 +33,7 @@ export const validateMachineIdentity = async ({
}
const machineIdentity = await MachineIdentity.findByIdAndUpdate(
machineIdentityClientSecretData.machineIdentity,
machineIdentityClientSecret.machineIdentity,
{
accessTokenLastUsed: new Date(),
$inc: { accessTokenUsageCount: 1 }

View File

@@ -132,6 +132,13 @@ In the following steps, we explore how to create and use MIs for your applicatio
- You are attempting to access a `/raw` secrets endpoint that requires your project to disable E2EE.
- The client secret/access token is being used from an untrusted IP.
</Accordion>
<Accordion title="Why can I not create, read, update, or delete a machine identity?">
There are a few reasons for why this might happen:
- You have insufficient organization permissions to create, read, update, delete machine identities.
- The MI you are trying to read, update, or delete is more privileged than yourself.
- The role you are trying to create a MI for or update a MI to is more privileged than yours.
</Accordion>
<Accordion title="Can you provide examples for using glob patterns?">
1. `/**`: This pattern matches all folders at any depth in the directory structure. For example, it would match folders like `/folder1/`, `/folder1/subfolder/`, and so on.

View File

@@ -244,6 +244,7 @@ export const AddMachineIdentityModal = ({
reset();
} catch (err) {
console.error(err);
const error = err as any;
const text = error?.response?.data?.message
?? `Failed to ${popUp?.machineIdentity?.data ? "updated" : "created"} machine identity`;

View File

@@ -81,8 +81,11 @@ export const MachineIdentityTable = ({
});
} catch (err) {
console.error(err);
const error = err as any;
const text = error?.response?.data?.message ?? "Failed to update machine identity role"
createNotification({
text: "Failed to update machine identity role",
text,
type: "error"
});
}

View File

@@ -35,7 +35,7 @@ export const MachineIdentitySection = withProjectPermission(
"upgradePlan"
] as const);
const onRemoveServiceTokenDataSubmit = async (machineId: string) => {
const onRemoveMachineIdentitySubmit = async (machineId: string) => {
try {
await deleteMutateAsync({
@@ -44,17 +44,20 @@ export const MachineIdentitySection = withProjectPermission(
});
createNotification({
text: "Successfully removed service account from project",
text: "Successfully removed machine identity from project",
type: "success"
});
handlePopUpClose("deleteMachineIdentity");
} catch (err) {
console.error(err);
createNotification({
text: "Failed to delete service account from project",
type: "error"
});
console.error(err);
const error = err as any;
const text = error?.response?.data?.message ?? "Failed to remove machine identity from project"
createNotification({
text,
type: "error"
});
}
}
@@ -96,7 +99,7 @@ export const MachineIdentitySection = withProjectPermission(
onChange={(isOpen) => handlePopUpToggle("deleteMachineIdentity", isOpen)}
deleteKey="confirm"
onDeleteApproved={() =>
onRemoveServiceTokenDataSubmit(
onRemoveMachineIdentitySubmit(
(popUp?.deleteMachineIdentity?.data as { machineId: string })?.machineId
)
}

View File

@@ -89,8 +89,11 @@ export const MachineIdentityTable = ({
});
} catch (err) {
console.error(err);
const error = err as any;
const text = error?.response?.data?.message ?? "Failed to update machine identity role"
createNotification({
text: "Failed to update machine identity role",
text,
type: "error"
});
}