feat: added root org identity link functionality

This commit is contained in:
=
2025-10-20 18:58:22 +05:30
parent 545ea4e27c
commit b7644c9294
26 changed files with 753 additions and 82 deletions
+3 -1
View File
@@ -585,6 +585,7 @@ export const registerRoutes = async (
});
const membershipIdentityService = membershipIdentityServiceFactory({
identityDAL,
membershipIdentityDAL,
membershipRoleDAL,
orgDAL,
@@ -1577,7 +1578,8 @@ export const registerRoutes = async (
permissionService,
projectDAL,
accessTokenQueue,
smtpService
smtpService,
orgDAL
});
const identityService = identityServiceFactory({
@@ -0,0 +1,137 @@
import { z } from "zod";
import { AccessScope, TemporaryPermissionMode } from "@app/db/schemas";
import { ApiDocsTags, PROJECT_IDENTITIES } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
const sanitizedOrgIdentityMembershipSchema = z.object({
id: z.string().uuid(),
orgId: z.string(),
identityId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export const registerOrgIdentityMembershipRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/identity-memberships/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
hide: true,
// this is hidden so not updating tags
tags: [ApiDocsTags.ProjectIdentities],
description: "Create org identity membership",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().trim()
}),
body: z.object({
roles: z
.array(
z.union([
z.object({
role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z
.literal(false)
.default(false)
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role)
}),
z.object({
role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryMode: z
.nativeEnum(TemporaryPermissionMode)
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryAccessStartTime: z
.string()
.datetime()
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role)
})
])
)
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.description)
.max(1)
}),
response: {
200: z.object({
identityMembership: sanitizedOrgIdentityMembershipSchema
})
}
},
handler: async (req) => {
const { membership } = await server.services.membershipIdentity.createMembership({
permission: req.permission,
scopeData: {
scope: AccessScope.Organization,
orgId: req.permission.orgId
},
data: {
identityId: req.params.identityId,
roles: req.body.roles
}
});
return {
identityMembership: { ...membership, identityId: req.params.identityId, orgId: req.permission.orgId }
};
}
});
server.route({
method: "DELETE",
url: "/identity-memberships/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT]),
schema: {
hide: true,
tags: [ApiDocsTags.ProjectIdentities],
description: "Delete org identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({
identityId: z.string().trim().describe(PROJECT_IDENTITIES.DELETE_IDENTITY_MEMBERSHIP.identityId)
}),
response: {
200: z.object({
identityMembership: sanitizedOrgIdentityMembershipSchema
})
}
},
handler: async (req) => {
const { membership } = await server.services.membershipIdentity.deleteMembership({
permission: req.permission,
scopeData: {
scope: AccessScope.Organization,
orgId: req.permission.orgId
},
selector: {
identityId: req.params.identityId
}
});
return {
identityMembership: { ...membership, identityId: req.params.identityId, orgId: req.permission.orgId }
};
}
});
};
+2
View File
@@ -65,6 +65,7 @@ import { registerUserEngagementRouter } from "./user-engagement-router";
import { registerUserRouter } from "./user-router";
import { registerWebhookRouter } from "./webhook-router";
import { registerWorkflowIntegrationRouter } from "./workflow-integration-router";
import { registerOrgIdentityMembershipRouter } from "./identity-org-membership-router";
export const registerV1Routes = async (server: FastifyZodProvider) => {
await server.register(registerSsoRouter, { prefix: "/sso" });
@@ -89,6 +90,7 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
);
await server.register(registerPasswordRouter, { prefix: "/password" });
await server.register(registerOrgRouter, { prefix: "/organization" });
await server.register(registerOrgIdentityMembershipRouter, { prefix: "/organization" });
await server.register(registerAdminRouter, { prefix: "/admin" });
await server.register(registerOrgAdminRouter, { prefix: "/organization-admin" });
await server.register(registerUserRouter, { prefix: "/user" });
@@ -2,6 +2,7 @@ import RE2 from "re2";
import { z } from "zod";
import {
AccessScope,
AuditLogsSchema,
GroupsSchema,
IncidentContactsSchema,
@@ -475,4 +476,68 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
return { groups };
}
});
server.route({
method: "GET",
url: "/users/available",
schema: {
response: {
200: z.object({
users: z
.object({
id: z.string().uuid(),
username: z.string(),
email: z.string().nullable().optional(),
firstName: z.string().nullable().optional(),
lastName: z.string().nullable().optional()
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { users } = await server.services.membershipUser.listAvailableUsers({
permission: req.permission,
scopeData: {
orgId: req.permission.orgId,
scope: AccessScope.Organization
},
data: {}
});
return { users };
}
});
server.route({
method: "GET",
url: "/identities/available",
schema: {
response: {
200: z.object({
identities: z
.object({
id: z.string().uuid(),
name: z.string(),
hasDeleteProtection: z.boolean()
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { identities } = await server.services.membershipIdentity.listAvailableIdentities({
permission: req.permission,
scopeData: {
orgId: req.permission.orgId,
scope: AccessScope.Organization
},
data: {}
});
return { identities };
}
});
};