feat(server): completed routes for user additional privilege

This commit is contained in:
Akhil Mohan
2024-03-15 15:54:32 +05:30
parent a908471e66
commit a5039494cd
7 changed files with 238 additions and 2 deletions

View File

@@ -8,6 +8,7 @@ import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secr
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
@@ -121,6 +122,7 @@ declare module "fastify" {
telemetry: TTelemetryServiceFactory;
dynamicSecret: TDynamicSecretServiceFactory;
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer

View File

@@ -15,6 +15,7 @@ import { registerSecretScanningRouter } from "./secret-scanning-router";
import { registerSecretVersionRouter } from "./secret-version-router";
import { registerSnapshotRouter } from "./snapshot-router";
import { registerTrustedIpRouter } from "./trusted-ip-router";
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
export const registerV1EERoutes = async (server: FastifyZodProvider) => {
// org role starts with organization
@@ -51,4 +52,10 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerSecretScanningRouter, { prefix: "/secret-scanning" });
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
await server.register(registerSecretVersionRouter, { prefix: "/secret" });
await server.register(
async (privilegeRouter) => {
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
},
{ prefix: "/additional-privilege" }
);
};

View File

@@ -0,0 +1,142 @@
import ms from "ms";
import { z } from "zod";
import { ProjectUserAdditionalPrivilegeSchema } from "@app/db/schemas";
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/",
method: "POST",
schema: {
body: z.union([
z.object({
projectMembershipId: z.string(),
slug: z.string().max(60).trim(),
name: z.string().trim(),
description: z.string().trim().optional(),
permissions: z.any().array(),
isTemporary: z.literal(false).default(false)
}),
z.object({
projectMembershipId: z.string(),
slug: z.string().max(60).trim(),
name: z.string().trim(),
description: z.string().trim().optional(),
permissions: z.any().array(),
isTemporary: z.literal(true),
temporaryMode: z.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode),
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
temporaryAccessStartTime: z.string().datetime()
})
]),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.create({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.body,
permissions: JSON.stringify(req.body.permissions)
});
return { privilege };
}
});
server.route({
url: "/:privilegeId",
method: "PATCH",
schema: {
params: z.object({
privilegeId: z.string()
}),
body: z
.object({
slug: z.string().max(60).trim(),
name: z.string().trim(),
description: z.string().trim().optional(),
permissions: z.any().array(),
isTemporary: z.boolean(),
temporaryMode: z.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode),
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
temporaryAccessStartTime: z.string().datetime()
})
.partial(),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.updateById({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.body,
permissions: req.body.permissions ? JSON.stringify(req.body.permissions) : undefined,
privilegeId: req.params.privilegeId
});
return { privilege };
}
});
server.route({
url: "/:privilegeId",
method: "DELETE",
schema: {
params: z.object({
privilegeId: z.string()
}),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.deleteById({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
privilegeId: req.params.privilegeId
});
return { privilege };
}
});
server.route({
url: "/:privilegeId",
method: "GET",
schema: {
params: z.object({
privilegeId: z.string()
}),
response: {
200: z.object({
privilege: ProjectUserAdditionalPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const privilege = await server.services.projectUserAdditionalPrivilege.getPrivilegeDetailsById({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
privilegeId: req.params.privilegeId
});
return { privilege };
}
});
};

View File

@@ -17,6 +17,8 @@ import { licenseDALFactory } from "@app/ee/services/license/license-dal";
import { licenseServiceFactory } from "@app/ee/services/license/license-service";
import { permissionDALFactory } from "@app/ee/services/permission/permission-dal";
import { permissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { projectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { projectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
import { samlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal";
import { samlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
import { scimDALFactory } from "@app/ee/services/scim/scim-dal";
@@ -149,6 +151,7 @@ export const registerRoutes = async (
const projectDAL = projectDALFactory(db);
const projectMembershipDAL = projectMembershipDALFactory(db);
const projectUserAdditionalPrivilegeDAL = projectUserAdditionalPrivilegeDALFactory(db);
const projectUserMembershipRoleDAL = projectUserMembershipRoleDALFactory(db);
const projectRoleDAL = projectRoleDALFactory(db);
const projectEnvDAL = projectEnvDALFactory(db);
@@ -345,6 +348,11 @@ export const registerRoutes = async (
projectRoleDAL,
licenseService
});
const projectUserAdditionalPrivilegeService = projectUserAdditionalPrivilegeServiceFactory({
permissionService,
projectMembershipDAL,
projectUserAdditionalPrivilegeDAL
});
const projectKeyService = projectKeyServiceFactory({
permissionService,
projectKeyDAL,
@@ -638,7 +646,8 @@ export const registerRoutes = async (
trustedIp: trustedIpService,
scim: scimService,
secretBlindIndex: secretBlindIndexService,
telemetry: telemetryService
telemetry: telemetryService,
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService
});
server.decorate<FastifyZodProvider["store"]>("store", {

View File

@@ -40,6 +40,20 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
lastName: true,
id: true
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
additionalPrivileges: z.array(
z.object({
id: z.string(),
name: z.string(),
slug: z.string(),
description: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional(),
createdAt: z.date()
})
),
roles: z.array(
z.object({
id: z.string(),

View File

@@ -73,6 +73,20 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
lastName: true,
id: true
}).merge(UserEncryptionKeysSchema.pick({ publicKey: true })),
additionalPrivileges: z.array(
z.object({
id: z.string(),
name: z.string(),
slug: z.string(),
description: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional(),
createdAt: z.date()
})
),
roles: z.array(
z.object({
id: z.string(),

View File

@@ -29,6 +29,11 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
`${TableName.ProjectUserMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.leftJoin(
TableName.ProjectUserAdditionalPrivilege,
`${TableName.ProjectMembership}.id`,
`${TableName.ProjectUserAdditionalPrivilege}.projectMembershipId`
)
.select(
db.ref("id").withSchema(TableName.ProjectMembership),
db.ref("isGhost").withSchema(TableName.Users),
@@ -47,7 +52,23 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole),
db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole)
db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole),
db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"),
db.ref("name").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApName"),
db.ref("description").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApDescription"),
db.ref("slug").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApSlug"),
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"),
db.ref("createdAt").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApCreatedAt"),
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessStartTime"),
db
.ref("temporaryAccessEndTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessEndTime")
)
.where({ isGhost: false });
@@ -87,6 +108,33 @@ export const projectMembershipDALFactory = (db: TDbClient) => {
temporaryAccessStartTime,
isTemporary
})
},
{
label: "additionalPrivileges" as const,
key: "userApId",
mapper: ({
userApId,
userApDescription,
userApName,
userApSlug,
userApIsTemporary,
userApTemporaryMode,
userApTemporaryRange,
userApTemporaryAccessEndTime,
userApTemporaryAccessStartTime,
userApCreatedAt
}) => ({
id: userApId,
name: userApName,
description: userApDescription,
slug: userApSlug,
temporaryRange: userApTemporaryRange,
temporaryMode: userApTemporaryMode,
temporaryAccessEndTime: userApTemporaryAccessEndTime,
temporaryAccessStartTime: userApTemporaryAccessStartTime,
isTemporary: userApIsTemporary,
createdAt: userApCreatedAt
})
}
]
});