Revert "Revert "Permission phase 2""

This reverts commit 8b9244b079.
This commit is contained in:
=
2024-10-25 00:59:38 +05:30
parent 45b9de63f0
commit 93218d5a3f
100 changed files with 3390 additions and 1615 deletions
@@ -4,6 +4,7 @@ import ms from "ms";
import { z } from "zod";
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
import { UnauthorizedError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -79,7 +80,9 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: false,
permissions: JSON.stringify(packRules(permission))
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(permission)))
});
return { privilege };
}
@@ -159,7 +162,9 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: true,
permissions: JSON.stringify(packRules(permission))
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(permission)))
});
return { privilege };
}
@@ -244,7 +249,11 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
projectSlug: req.body.projectSlug,
data: {
...updatedInfo,
permissions: permission ? JSON.stringify(packRules(permission)) : undefined
permissions: permission
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore-error this is valid ts
JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(permission)))
: undefined
}
});
return { privilege };
@@ -3,7 +3,10 @@ import slugify from "@sindresorhus/slugify";
import { z } from "zod";
import { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
import { ProjectPermissionSchema } from "@app/ee/services/permission/project-permission";
import {
backfillPermissionV1SchemaToV2Schema,
ProjectPermissionV1Schema
} from "@app/ee/services/permission/project-permission";
import { PROJECT_ROLE } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -43,7 +46,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
.describe(PROJECT_ROLE.CREATE.slug),
name: z.string().min(1).trim().describe(PROJECT_ROLE.CREATE.name),
description: z.string().trim().optional().describe(PROJECT_ROLE.CREATE.description),
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.CREATE.permissions)
permissions: ProjectPermissionV1Schema.array().describe(PROJECT_ROLE.CREATE.permissions)
}),
response: {
200: z.object({
@@ -61,7 +64,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
projectSlug: req.params.projectSlug,
data: {
...req.body,
permissions: JSON.stringify(packRules(req.body.permissions))
permissions: JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions)))
}
});
return { role };
@@ -103,7 +106,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
}),
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
description: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.description),
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.UPDATE.permissions).optional()
permissions: ProjectPermissionV1Schema.array().describe(PROJECT_ROLE.UPDATE.permissions).optional()
}),
response: {
200: z.object({
@@ -122,7 +125,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
roleId: req.params.roleId,
data: {
...req.body,
permissions: req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined
permissions: req.body.permissions
? JSON.stringify(packRules(backfillPermissionV1SchemaToV2Schema(req.body.permissions)))
: undefined
}
});
return { role };
@@ -1,13 +1,16 @@
import { packRules } from "@casl/ability/extra";
import slugify from "@sindresorhus/slugify";
import ms from "ms";
import { z } from "zod";
import { ProjectUserAdditionalPrivilegeSchema } from "@app/db/schemas";
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ProjectSpecificPrivilegePermissionSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
@@ -31,7 +34,9 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
})
.optional()
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions)
permissions: ProjectSpecificPrivilegePermissionSchema.describe(
PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions
)
}),
response: {
200: z.object({
@@ -49,7 +54,17 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: false,
permissions: JSON.stringify(req.body.permissions)
permissions: JSON.stringify(
packRules(
backfillPermissionV1SchemaToV2Schema(
req.body.permissions.actions.map((action) => ({
action,
subject: req.body.permissions.subject,
conditions: req.body.permissions.conditions
}))
)
)
)
});
return { privilege };
}
@@ -75,7 +90,9 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
})
.optional()
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions),
permissions: ProjectSpecificPrivilegePermissionSchema.describe(
PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions
),
temporaryMode: z
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
@@ -104,7 +121,17 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : `privilege-${slugify(alphaNumericNanoId(12))}`,
isTemporary: true,
permissions: JSON.stringify(req.body.permissions)
permissions: JSON.stringify(
packRules(
backfillPermissionV1SchemaToV2Schema(
req.body.permissions.actions.map((action) => ({
action,
subject: req.body.permissions.subject,
conditions: req.body.permissions.conditions
}))
)
)
)
});
return { privilege };
}
@@ -131,7 +158,9 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
message: "Slug must be a valid slug"
})
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.slug),
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
permissions: ProjectSpecificPrivilegePermissionSchema.describe(
PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.permissions
).optional(),
isTemporary: z.boolean().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
temporaryMode: z
.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode)
@@ -160,7 +189,19 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
...req.body,
permissions: req.body.permissions ? JSON.stringify(req.body.permissions) : undefined,
permissions: req.body.permissions
? JSON.stringify(
packRules(
backfillPermissionV1SchemaToV2Schema(
req.body.permissions.actions.map((action) => ({
action,
subject: req.body.permissions!.subject,
conditions: req.body.permissions!.conditions
}))
)
)
)
: undefined,
privilegeId: req.params.privilegeId
});
return { privilege };
+11
View File
@@ -0,0 +1,11 @@
import { registerProjectRoleRouter } from "./project-role-router";
export const registerV2EERoutes = async (server: FastifyZodProvider) => {
// org role starts with organization
await server.register(
async (projectRouter) => {
await projectRouter.register(registerProjectRoleRouter);
},
{ prefix: "/workspace" }
);
};
@@ -0,0 +1,272 @@
import { packRules } from "@casl/ability/extra";
import slugify from "@sindresorhus/slugify";
import { z } from "zod";
import { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
import { PROJECT_ROLE } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/:projectSlug/roles",
config: {
rateLimit: writeLimit
},
schema: {
description: "Create a project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.CREATE.projectSlug)
}),
body: z.object({
slug: z
.string()
.toLowerCase()
.trim()
.min(1)
.refine(
(val) => !Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid"
})
.describe(PROJECT_ROLE.CREATE.slug),
name: z.string().min(1).trim().describe(PROJECT_ROLE.CREATE.name),
description: z.string().trim().optional().describe(PROJECT_ROLE.CREATE.description),
permissions: ProjectPermissionV2Schema.array().describe(PROJECT_ROLE.CREATE.permissions)
}),
response: {
200: z.object({
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const role = await server.services.projectRole.createRole({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
data: {
...req.body,
permissions: JSON.stringify(packRules(req.body.permissions))
}
});
return { role };
}
});
server.route({
method: "PATCH",
url: "/:projectSlug/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
description: "Update a project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.UPDATE.projectSlug),
roleId: z.string().trim().describe(PROJECT_ROLE.UPDATE.roleId)
}),
body: z.object({
slug: z
.string()
.toLowerCase()
.trim()
.optional()
.describe(PROJECT_ROLE.UPDATE.slug)
.refine(
(val) =>
typeof val === "undefined" ||
!Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((val) => typeof val === "undefined" || slugify(val) === val, {
message: "Slug must be a valid"
}),
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
description: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.description),
permissions: ProjectPermissionV2Schema.array().describe(PROJECT_ROLE.UPDATE.permissions).optional()
}),
response: {
200: z.object({
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const role = await server.services.projectRole.updateRole({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId,
data: {
...req.body,
permissions: req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined
}
});
return { role };
}
});
server.route({
method: "DELETE",
url: "/:projectSlug/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
description: "Delete a project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.DELETE.projectSlug),
roleId: z.string().trim().describe(PROJECT_ROLE.DELETE.roleId)
}),
response: {
200: z.object({
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const role = await server.services.projectRole.deleteRole({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId
});
return { role };
}
});
server.route({
method: "GET",
url: "/:projectSlug/roles",
config: {
rateLimit: readLimit
},
schema: {
description: "List project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.LIST.projectSlug)
}),
response: {
200: z.object({
roles: ProjectRolesSchema.omit({ permissions: true }).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const roles = await server.services.projectRole.listRoles({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug
});
return { roles };
}
});
server.route({
method: "GET",
url: "/:projectSlug/roles/slug/:roleSlug",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug),
roleSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug)
}),
response: {
200: z.object({
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const role = await server.services.projectRole.getRoleBySlug({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleSlug: req.params.roleSlug
});
return { role };
}
});
server.route({
method: "GET",
url: "/:projectId/permissions",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
response: {
200: z.object({
data: z.object({
membership: ProjectMembershipsSchema.extend({
roles: z
.object({
role: z.string()
})
.array()
}),
permissions: z.any().array()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { permissions, membership } = await server.services.projectRole.getUserPermission(
req.permission.id,
req.params.projectId,
req.permission.authMethod,
req.permission.orgId
);
return { data: { permissions, membership } };
}
});
};