mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 01:26:43 +00:00
feat(server): completed identity additional privilege
This commit is contained in:
Vendored
+2
@@ -5,6 +5,7 @@ import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-se
|
|||||||
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
||||||
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||||
|
import { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
||||||
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
@@ -123,6 +124,7 @@ declare module "fastify" {
|
|||||||
dynamicSecret: TDynamicSecretServiceFactory;
|
dynamicSecret: TDynamicSecretServiceFactory;
|
||||||
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
|
||||||
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
|
||||||
|
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
|||||||
Vendored
+8
@@ -38,6 +38,9 @@ import {
|
|||||||
TIdentityOrgMemberships,
|
TIdentityOrgMemberships,
|
||||||
TIdentityOrgMembershipsInsert,
|
TIdentityOrgMembershipsInsert,
|
||||||
TIdentityOrgMembershipsUpdate,
|
TIdentityOrgMembershipsUpdate,
|
||||||
|
TIdentityProjectAdditionalPrivilege,
|
||||||
|
TIdentityProjectAdditionalPrivilegeInsert,
|
||||||
|
TIdentityProjectAdditionalPrivilegeUpdate,
|
||||||
TIdentityProjectMembershipRole,
|
TIdentityProjectMembershipRole,
|
||||||
TIdentityProjectMembershipRoleInsert,
|
TIdentityProjectMembershipRoleInsert,
|
||||||
TIdentityProjectMembershipRoleUpdate,
|
TIdentityProjectMembershipRoleUpdate,
|
||||||
@@ -302,6 +305,11 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityProjectMembershipRoleInsert,
|
TIdentityProjectMembershipRoleInsert,
|
||||||
TIdentityProjectMembershipRoleUpdate
|
TIdentityProjectMembershipRoleUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.IdentityProjectAdditionalPrivilege]: Knex.CompositeTableType<
|
||||||
|
TIdentityProjectAdditionalPrivilege,
|
||||||
|
TIdentityProjectAdditionalPrivilegeInsert,
|
||||||
|
TIdentityProjectAdditionalPrivilegeUpdate
|
||||||
|
>;
|
||||||
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
||||||
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
||||||
TSecretApprovalPolicies,
|
TSecretApprovalPolicies,
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.IdentityProjectAdditionalPrivilege))) {
|
||||||
|
await knex.schema.createTable(TableName.IdentityProjectAdditionalPrivilege, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("name").notNullable();
|
||||||
|
t.string("slug", 60).notNullable();
|
||||||
|
t.string("description");
|
||||||
|
t.uuid("projectMembershipId").notNullable();
|
||||||
|
t.foreign("projectMembershipId")
|
||||||
|
.references("id")
|
||||||
|
.inTable(TableName.IdentityProjectMembership)
|
||||||
|
.onDelete("CASCADE");
|
||||||
|
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||||
|
t.string("temporaryMode");
|
||||||
|
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||||
|
t.datetime("temporaryAccessStartTime");
|
||||||
|
t.datetime("temporaryAccessEndTime");
|
||||||
|
t.jsonb("permissions").notNullable();
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.IdentityProjectAdditionalPrivilege);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.IdentityProjectAdditionalPrivilege);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.IdentityProjectAdditionalPrivilege);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const IdentityProjectAdditionalPrivilegeSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
description: z.string().nullable().optional(),
|
||||||
|
projectMembershipId: z.string().uuid(),
|
||||||
|
isTemporary: z.boolean().default(false),
|
||||||
|
temporaryMode: z.string().nullable().optional(),
|
||||||
|
temporaryRange: z.string().nullable().optional(),
|
||||||
|
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||||
|
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||||
|
permissions: z.unknown(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TIdentityProjectAdditionalPrivilege = z.infer<typeof IdentityProjectAdditionalPrivilegeSchema>;
|
||||||
|
export type TIdentityProjectAdditionalPrivilegeInsert = Omit<
|
||||||
|
z.input<typeof IdentityProjectAdditionalPrivilegeSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TIdentityProjectAdditionalPrivilegeUpdate = Partial<
|
||||||
|
Omit<z.input<typeof IdentityProjectAdditionalPrivilegeSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
||||||
@@ -10,6 +10,7 @@ export * from "./git-app-org";
|
|||||||
export * from "./identities";
|
export * from "./identities";
|
||||||
export * from "./identity-access-tokens";
|
export * from "./identity-access-tokens";
|
||||||
export * from "./identity-org-memberships";
|
export * from "./identity-org-memberships";
|
||||||
|
export * from "./identity-project-additional-privilege";
|
||||||
export * from "./identity-project-membership-role";
|
export * from "./identity-project-membership-role";
|
||||||
export * from "./identity-project-memberships";
|
export * from "./identity-project-memberships";
|
||||||
export * from "./identity-ua-client-secrets";
|
export * from "./identity-ua-client-secrets";
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export enum TableName {
|
|||||||
IdentityOrgMembership = "identity_org_memberships",
|
IdentityOrgMembership = "identity_org_memberships",
|
||||||
IdentityProjectMembership = "identity_project_memberships",
|
IdentityProjectMembership = "identity_project_memberships",
|
||||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||||
|
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
||||||
ScimToken = "scim_tokens",
|
ScimToken = "scim_tokens",
|
||||||
SecretApprovalPolicy = "secret_approval_policies",
|
SecretApprovalPolicy = "secret_approval_policies",
|
||||||
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
||||||
|
|||||||
@@ -0,0 +1,171 @@
|
|||||||
|
import ms from "ms";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { IdentityProjectAdditionalPrivilegeSchema } from "@app/db/schemas";
|
||||||
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z.union([
|
||||||
|
z.object({
|
||||||
|
identityId: z.string(),
|
||||||
|
projectId: 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({
|
||||||
|
identityId: z.string(),
|
||||||
|
projectId: 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(IdentityProjectAdditionalPrivilegeTemporaryMode),
|
||||||
|
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
|
||||||
|
temporaryAccessStartTime: z.string().datetime()
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
privilege: IdentityProjectAdditionalPrivilegeSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const privilege = await server.services.identityProjectAdditionalPrivilege.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(IdentityProjectAdditionalPrivilegeTemporaryMode),
|
||||||
|
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: IdentityProjectAdditionalPrivilegeSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const privilege = await server.services.identityProjectAdditionalPrivilege.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: IdentityProjectAdditionalPrivilegeSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const privilege = await server.services.identityProjectAdditionalPrivilege.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: IdentityProjectAdditionalPrivilegeSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const privilege = await server.services.identityProjectAdditionalPrivilege.getPrivilegeDetailsById({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
privilegeId: req.params.privilegeId
|
||||||
|
});
|
||||||
|
return { privilege };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/permissions",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
identityId: z.string(),
|
||||||
|
projectId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const privileges = await server.services.identityProjectAdditionalPrivilege.listIdentityProjectPrivileges({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
projectId: req.body.projectId,
|
||||||
|
identityId: req.body.identityId
|
||||||
|
});
|
||||||
|
return { privileges };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||||
|
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
||||||
import { registerLdapRouter } from "./ldap-router";
|
import { registerLdapRouter } from "./ldap-router";
|
||||||
import { registerLicenseRouter } from "./license-router";
|
import { registerLicenseRouter } from "./license-router";
|
||||||
import { registerOrgRoleRouter } from "./org-role-router";
|
import { registerOrgRoleRouter } from "./org-role-router";
|
||||||
@@ -55,6 +56,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(
|
await server.register(
|
||||||
async (privilegeRouter) => {
|
async (privilegeRouter) => {
|
||||||
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
|
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
|
||||||
|
await privilegeRouter.register(registerIdentityProjectAdditionalPrivilegeRouter, { prefix: "/identity" });
|
||||||
},
|
},
|
||||||
{ prefix: "/additional-privilege" }
|
{ prefix: "/additional-privilege" }
|
||||||
);
|
);
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TIdentityProjectAdditionalPrivilegeDALFactory = ReturnType<
|
||||||
|
typeof identityProjectAdditionalPrivilegeDALFactory
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const identityProjectAdditionalPrivilegeDALFactory = (db: TDbClient) => {
|
||||||
|
const orm = ormify(db, TableName.IdentityProjectAdditionalPrivilege);
|
||||||
|
return orm;
|
||||||
|
};
|
||||||
+259
@@ -0,0 +1,259 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import ms from "ms";
|
||||||
|
|
||||||
|
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
||||||
|
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
|
|
||||||
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
|
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
||||||
|
import {
|
||||||
|
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
||||||
|
TCreateIdentityPrivilegeDTO,
|
||||||
|
TDeleteIdentityPrivilegeDTO,
|
||||||
|
TGetIdentityPrivilegeDetailsDTO,
|
||||||
|
TListIdentityPrivilegesDTO,
|
||||||
|
TUpdateIdentityPrivilegeDTO
|
||||||
|
} from "./identity-project-additional-privilege-types";
|
||||||
|
|
||||||
|
type TIdentityProjectAdditionalPrivilegeServiceFactoryDep = {
|
||||||
|
identityProjectAdditionalPrivilegeDAL: TIdentityProjectAdditionalPrivilegeDALFactory;
|
||||||
|
identityProjectDAL: Pick<TIdentityProjectDALFactory, "findOne" | "findById">;
|
||||||
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
|
||||||
|
typeof identityProjectAdditionalPrivilegeServiceFactory
|
||||||
|
>;
|
||||||
|
|
||||||
|
export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
||||||
|
identityProjectAdditionalPrivilegeDAL,
|
||||||
|
identityProjectDAL,
|
||||||
|
permissionService
|
||||||
|
}: TIdentityProjectAdditionalPrivilegeServiceFactoryDep) => {
|
||||||
|
const create = async ({
|
||||||
|
name,
|
||||||
|
slug,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
projectId,
|
||||||
|
identityId,
|
||||||
|
permissions: customPermission,
|
||||||
|
actorOrgId,
|
||||||
|
description,
|
||||||
|
...dto
|
||||||
|
}: TCreateIdentityPrivilegeDTO) => {
|
||||||
|
const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId });
|
||||||
|
if (!identityProjectMembership)
|
||||||
|
throw new BadRequestError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||||
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
identityId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||||
|
if (!hasRequiredPriviledges)
|
||||||
|
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
||||||
|
|
||||||
|
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||||
|
slug,
|
||||||
|
projectMembershipId: identityProjectMembership.id
|
||||||
|
});
|
||||||
|
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
||||||
|
|
||||||
|
if (!dto.isTemporary) {
|
||||||
|
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
||||||
|
projectMembershipId: identityProjectMembership.id,
|
||||||
|
slug,
|
||||||
|
permissions: customPermission,
|
||||||
|
name,
|
||||||
|
description
|
||||||
|
});
|
||||||
|
return additionalPrivilege;
|
||||||
|
}
|
||||||
|
|
||||||
|
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
|
||||||
|
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
||||||
|
projectMembershipId: identityProjectMembership.id,
|
||||||
|
slug,
|
||||||
|
permissions: customPermission,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
isTemporary: true,
|
||||||
|
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative,
|
||||||
|
temporaryRange: dto.temporaryRange,
|
||||||
|
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
|
||||||
|
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||||
|
});
|
||||||
|
return additionalPrivilege;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateById = async ({ privilegeId, actorOrgId, actor, actorId, ...dto }: TUpdateIdentityPrivilegeDTO) => {
|
||||||
|
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId);
|
||||||
|
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
|
||||||
|
const identityProjectMembership = await identityProjectDAL.findById(identityPrivilege.projectMembershipId);
|
||||||
|
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||||
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
identityProjectMembership.identityId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||||
|
if (!hasRequiredPriviledges)
|
||||||
|
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
||||||
|
|
||||||
|
if (dto?.slug) {
|
||||||
|
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||||
|
slug: dto.slug,
|
||||||
|
projectMembershipId: identityProjectMembership.id
|
||||||
|
});
|
||||||
|
if (existingSlug && existingSlug.id !== identityPrivilege.id)
|
||||||
|
throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isTemporary = typeof dto?.isTemporary !== "undefined" ? dto.isTemporary : identityPrivilege.isTemporary;
|
||||||
|
if (isTemporary) {
|
||||||
|
const temporaryAccessStartTime = dto?.temporaryAccessStartTime || identityPrivilege?.temporaryAccessStartTime;
|
||||||
|
const temporaryRange = dto?.temporaryRange || identityPrivilege?.temporaryRange;
|
||||||
|
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
||||||
|
...dto,
|
||||||
|
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
|
||||||
|
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
|
||||||
|
});
|
||||||
|
return additionalPrivilege;
|
||||||
|
}
|
||||||
|
|
||||||
|
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
|
||||||
|
...dto,
|
||||||
|
isTemporary: false,
|
||||||
|
temporaryAccessStartTime: null,
|
||||||
|
temporaryAccessEndTime: null,
|
||||||
|
temporaryRange: null,
|
||||||
|
temporaryMode: null
|
||||||
|
});
|
||||||
|
return additionalPrivilege;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteById = async ({ actorId, actor, actorOrgId, privilegeId }: TDeleteIdentityPrivilegeDTO) => {
|
||||||
|
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId);
|
||||||
|
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
|
||||||
|
const identityProjectMembership = await identityProjectDAL.findById(identityPrivilege.projectMembershipId);
|
||||||
|
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||||
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
identityProjectMembership.identityId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||||
|
if (!hasRequiredPriviledges)
|
||||||
|
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
||||||
|
|
||||||
|
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
||||||
|
return deletedPrivilege;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPrivilegeDetailsById = async ({
|
||||||
|
privilegeId,
|
||||||
|
actorOrgId,
|
||||||
|
actor,
|
||||||
|
actorId
|
||||||
|
}: TGetIdentityPrivilegeDetailsDTO) => {
|
||||||
|
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId);
|
||||||
|
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
|
||||||
|
const identityProjectMembership = await identityProjectDAL.findById(identityPrivilege.projectMembershipId);
|
||||||
|
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||||
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
identityProjectMembership.identityId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||||
|
if (!hasRequiredPriviledges)
|
||||||
|
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
||||||
|
|
||||||
|
return identityPrivilege;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listIdentityProjectPrivileges = async ({
|
||||||
|
projectId,
|
||||||
|
identityId,
|
||||||
|
actorOrgId,
|
||||||
|
actor,
|
||||||
|
actorId
|
||||||
|
}: TListIdentityPrivilegesDTO) => {
|
||||||
|
const identityProjectMembership = await identityProjectDAL.findOne({ projectId, identityId });
|
||||||
|
if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` });
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity);
|
||||||
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission(
|
||||||
|
ActorType.IDENTITY,
|
||||||
|
identityProjectMembership.identityId,
|
||||||
|
identityProjectMembership.projectId,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
||||||
|
if (!hasRequiredPriviledges)
|
||||||
|
throw new ForbiddenRequestError({ message: "Failed to delete more privileged identity" });
|
||||||
|
|
||||||
|
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
|
||||||
|
projectMembershipId: identityProjectMembership.id
|
||||||
|
});
|
||||||
|
if (!identityPrivileges) throw new BadRequestError({ message: "Identity additional privilege not found" });
|
||||||
|
return identityPrivileges;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
create,
|
||||||
|
updateById,
|
||||||
|
deleteById,
|
||||||
|
getPrivilegeDetailsById,
|
||||||
|
listIdentityProjectPrivileges
|
||||||
|
};
|
||||||
|
};
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
|
||||||
|
export enum IdentityProjectAdditionalPrivilegeTemporaryMode {
|
||||||
|
Relative = "relative"
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TCreateIdentityPrivilegeDTO = (
|
||||||
|
| {
|
||||||
|
permissions: unknown;
|
||||||
|
identityId: string;
|
||||||
|
projectId: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
description?: string;
|
||||||
|
isTemporary: false;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
permissions: unknown;
|
||||||
|
identityId: string;
|
||||||
|
projectId: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
description?: string;
|
||||||
|
isTemporary: true;
|
||||||
|
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
||||||
|
temporaryRange: string;
|
||||||
|
temporaryAccessStartTime: string;
|
||||||
|
}
|
||||||
|
) &
|
||||||
|
Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TUpdateIdentityPrivilegeDTO = { privilegeId: string } & Omit<TProjectPermission, "projectId"> &
|
||||||
|
Partial<{
|
||||||
|
permissions: unknown;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
description?: string;
|
||||||
|
isTemporary: boolean;
|
||||||
|
temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative;
|
||||||
|
temporaryRange: string;
|
||||||
|
temporaryAccessStartTime: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type TDeleteIdentityPrivilegeDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
|
||||||
|
|
||||||
|
export type TGetIdentityPrivilegeDetailsDTO = Omit<TProjectPermission, "projectId"> & { privilegeId: string };
|
||||||
|
|
||||||
|
export type TListIdentityPrivilegesDTO = Omit<TProjectPermission, "projectId"> & {
|
||||||
|
identityId: string;
|
||||||
|
projectId: string;
|
||||||
|
};
|
||||||
@@ -11,6 +11,8 @@ import { buildDynamicSecretProviders } from "@app/ee/services/dynamic-secret/pro
|
|||||||
import { dynamicSecretLeaseDALFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-dal";
|
import { dynamicSecretLeaseDALFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-dal";
|
||||||
import { dynamicSecretLeaseQueueServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue";
|
import { dynamicSecretLeaseQueueServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-queue";
|
||||||
import { dynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
import { dynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||||
|
import { identityProjectAdditionalPrivilegeDALFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-dal";
|
||||||
|
import { identityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
||||||
import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal";
|
import { ldapConfigDALFactory } from "@app/ee/services/ldap-config/ldap-config-dal";
|
||||||
import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
import { ldapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
||||||
import { licenseDALFactory } from "@app/ee/services/license/license-dal";
|
import { licenseDALFactory } from "@app/ee/services/license/license-dal";
|
||||||
@@ -177,6 +179,7 @@ export const registerRoutes = async (
|
|||||||
const identityOrgMembershipDAL = identityOrgDALFactory(db);
|
const identityOrgMembershipDAL = identityOrgDALFactory(db);
|
||||||
const identityProjectDAL = identityProjectDALFactory(db);
|
const identityProjectDAL = identityProjectDALFactory(db);
|
||||||
const identityProjectMembershipRoleDAL = identityProjectMembershipRoleDALFactory(db);
|
const identityProjectMembershipRoleDAL = identityProjectMembershipRoleDALFactory(db);
|
||||||
|
const identityProjectAdditionalPrivilegeDAL = identityProjectAdditionalPrivilegeDALFactory(db);
|
||||||
|
|
||||||
const identityUaDAL = identityUaDALFactory(db);
|
const identityUaDAL = identityUaDALFactory(db);
|
||||||
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
const identityUaClientSecretDAL = identityUaClientSecretDALFactory(db);
|
||||||
@@ -556,6 +559,11 @@ export const registerRoutes = async (
|
|||||||
identityProjectMembershipRoleDAL,
|
identityProjectMembershipRoleDAL,
|
||||||
projectRoleDAL
|
projectRoleDAL
|
||||||
});
|
});
|
||||||
|
const identityProjectAdditionalPrivilegeService = identityProjectAdditionalPrivilegeServiceFactory({
|
||||||
|
identityProjectAdditionalPrivilegeDAL,
|
||||||
|
permissionService,
|
||||||
|
identityProjectDAL
|
||||||
|
});
|
||||||
const identityUaService = identityUaServiceFactory({
|
const identityUaService = identityUaServiceFactory({
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
@@ -647,7 +655,8 @@ export const registerRoutes = async (
|
|||||||
scim: scimService,
|
scim: scimService,
|
||||||
secretBlindIndex: secretBlindIndexService,
|
secretBlindIndex: secretBlindIndexService,
|
||||||
telemetry: telemetryService,
|
telemetry: telemetryService,
|
||||||
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService
|
projectUserAdditionalPrivilege: projectUserAdditionalPrivilegeService,
|
||||||
|
identityProjectAdditionalPrivilege: identityProjectAdditionalPrivilegeService
|
||||||
});
|
});
|
||||||
|
|
||||||
server.decorate<FastifyZodProvider["store"]>("store", {
|
server.decorate<FastifyZodProvider["store"]>("store", {
|
||||||
|
|||||||
@@ -156,6 +156,20 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
|
|||||||
identityId: z.string(),
|
identityId: z.string(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
|
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(
|
roles: z.array(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
`${TableName.IdentityProjectMembershipRole}.customRoleId`,
|
||||||
`${TableName.ProjectRoles}.id`
|
`${TableName.ProjectRoles}.id`
|
||||||
)
|
)
|
||||||
|
.leftJoin(
|
||||||
|
TableName.IdentityProjectAdditionalPrivilege,
|
||||||
|
`${TableName.IdentityProjectMembership}.id`,
|
||||||
|
`${TableName.IdentityProjectAdditionalPrivilege}.projectMembershipId`
|
||||||
|
)
|
||||||
.select(
|
.select(
|
||||||
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
db.ref("id").withSchema(TableName.IdentityProjectMembership),
|
||||||
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
db.ref("createdAt").withSchema(TableName.IdentityProjectMembership),
|
||||||
@@ -42,7 +47,29 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("isTemporary").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("temporaryRange").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
db.ref("temporaryAccessStartTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole)
|
db.ref("temporaryAccessEndTime").withSchema(TableName.IdentityProjectMembershipRole),
|
||||||
|
db.ref("id").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApId"),
|
||||||
|
db.ref("name").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApName"),
|
||||||
|
db.ref("description").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApDescription"),
|
||||||
|
db.ref("slug").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApSlug"),
|
||||||
|
db
|
||||||
|
.ref("temporaryMode")
|
||||||
|
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||||
|
.as("identityApTemporaryMode"),
|
||||||
|
db.ref("isTemporary").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApIsTemporary"),
|
||||||
|
db.ref("createdAt").withSchema(TableName.IdentityProjectAdditionalPrivilege).as("identityApCreatedAt"),
|
||||||
|
db
|
||||||
|
.ref("temporaryRange")
|
||||||
|
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||||
|
.as("identityApTemporaryRange"),
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessStartTime")
|
||||||
|
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||||
|
.as("identityApTemporaryAccessStartTime"),
|
||||||
|
db
|
||||||
|
.ref("temporaryAccessEndTime")
|
||||||
|
.withSchema(TableName.IdentityProjectAdditionalPrivilege)
|
||||||
|
.as("identityApTemporaryAccessEndTime")
|
||||||
);
|
);
|
||||||
|
|
||||||
const members = sqlNestRelationships({
|
const members = sqlNestRelationships({
|
||||||
@@ -86,6 +113,33 @@ export const identityProjectDALFactory = (db: TDbClient) => {
|
|||||||
temporaryAccessStartTime,
|
temporaryAccessStartTime,
|
||||||
isTemporary
|
isTemporary
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "additionalPrivileges" as const,
|
||||||
|
key: "identityApId",
|
||||||
|
mapper: ({
|
||||||
|
identityApId,
|
||||||
|
identityApDescription,
|
||||||
|
identityApName,
|
||||||
|
identityApSlug,
|
||||||
|
identityApIsTemporary,
|
||||||
|
identityApTemporaryMode,
|
||||||
|
identityApTemporaryRange,
|
||||||
|
identityApCreatedAt,
|
||||||
|
identityApTemporaryAccessEndTime,
|
||||||
|
identityApTemporaryAccessStartTime
|
||||||
|
}) => ({
|
||||||
|
id: identityApId,
|
||||||
|
name: identityApName,
|
||||||
|
description: identityApDescription,
|
||||||
|
slug: identityApSlug,
|
||||||
|
temporaryRange: identityApTemporaryRange,
|
||||||
|
temporaryMode: identityApTemporaryMode,
|
||||||
|
temporaryAccessEndTime: identityApTemporaryAccessEndTime,
|
||||||
|
temporaryAccessStartTime: identityApTemporaryAccessStartTime,
|
||||||
|
isTemporary: identityApIsTemporary,
|
||||||
|
createdAt: identityApCreatedAt
|
||||||
|
})
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user