feat: greepy review

This commit is contained in:
=
2025-10-20 22:05:02 +05:30
parent 2fa762e56d
commit eec2b30666
25 changed files with 65 additions and 66 deletions
@@ -14,8 +14,14 @@ export async function up(knex: Knex): Promise<void> {
t.foreign("rootOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); t.foreign("rootOrgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.dropUnique(["slug"]); t.dropUnique(["slug"]);
t.unique(["rootOrgId", "parentOrgId", "slug"]);
}); });
// had to switch to raw for null not distinct
await knex.raw(`
ALTER TABLE "organization"
ADD CONSTRAINT "organization_root_parent_slug_unique"
UNIQUE ("rootOrgId", "parentOrgId", "slug") NULLS NOT DISTINCT;
`);
} }
const hasIdentityOrgCol = await knex.schema.hasColumn(TableName.Identity, "orgId"); const hasIdentityOrgCol = await knex.schema.hasColumn(TableName.Identity, "orgId");
+2 -1
View File
@@ -24,7 +24,8 @@ export async function seed(knex: Knex): Promise<void> {
// @ts-ignore // @ts-ignore
id: seedData1.machineIdentity.id, id: seedData1.machineIdentity.id,
name: seedData1.machineIdentity.name, name: seedData1.machineIdentity.name,
authMethod: IdentityAuthMethod.UNIVERSAL_AUTH authMethod: IdentityAuthMethod.UNIVERSAL_AUTH,
orgId: seedData1.organization.id
} }
]); ]);
const identityUa = await knex(TableName.IdentityUniversalAuth) const identityUa = await knex(TableName.IdentityUniversalAuth)
+9 -23
View File
@@ -4,11 +4,11 @@ import { OrganizationsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, SUB_ORGANIZATIONS } from "@app/lib/api-docs"; import { ApiDocsTags, SUB_ORGANIZATIONS } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { GenericResourceNameSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { GenericResourceNameSchema } from "@app/server/lib/schemas";
const sanitiziedSubOrganizationSchema = OrganizationsSchema.pick({ const sanitizedSubOrganizationSchema = OrganizationsSchema.pick({
id: true, id: true,
name: true, name: true,
slug: true, slug: true,
@@ -26,7 +26,7 @@ export const registerSubOrgRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
hide: false, hide: false,
tags: [ApiDocsTags.SubOrganizations], tags: [ApiDocsTags.SubOrganizations],
description: "Create a child organization", description: "Create a sub organization",
security: [ security: [
{ {
bearerAuth: [] bearerAuth: []
@@ -37,7 +37,7 @@ export const registerSubOrgRouter = async (server: FastifyZodProvider) => {
}), }),
response: { response: {
200: z.object({ 200: z.object({
organization: sanitiziedSubOrganizationSchema organization: sanitizedSubOrganizationSchema
}) })
} }
}, },
@@ -45,21 +45,14 @@ export const registerSubOrgRouter = async (server: FastifyZodProvider) => {
handler: async (req) => { handler: async (req) => {
const { organization } = await server.services.subOrganization.createSubOrg({ const { organization } = await server.services.subOrganization.createSubOrg({
name: req.body.name, name: req.body.name,
permissionActor: { permissionActor: req.permission
id: req.permission.id,
type: req.permission.type,
authMethod: req.permission.authMethod,
orgId: req.permission.orgId,
parentOrgId: req.permission.parentOrgId,
rootOrgId: req.permission.rootOrgId
}
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: req.permission.orgId, orgId: req.permission.orgId,
event: { event: {
type: EventType.CREATE_CHILD_ORGANIZATION, type: EventType.CREATE_SUB_ORGANIZATION,
metadata: { metadata: {
name: req.body.name, name: req.body.name,
organizationId: organization.id organizationId: organization.id
@@ -80,7 +73,7 @@ export const registerSubOrgRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
hide: false, hide: false,
tags: [ApiDocsTags.SubOrganizations], tags: [ApiDocsTags.SubOrganizations],
description: "List child organizations", description: "List of sub organizations",
security: [ security: [
{ {
bearerAuth: [] bearerAuth: []
@@ -97,21 +90,14 @@ export const registerSubOrgRouter = async (server: FastifyZodProvider) => {
}), }),
response: { response: {
200: z.object({ 200: z.object({
organizations: sanitiziedSubOrganizationSchema.array() organizations: sanitizedSubOrganizationSchema.array()
}) })
} }
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const { organizations } = await server.services.subOrganization.listSubOrgs({ const { organizations } = await server.services.subOrganization.listSubOrgs({
permissionActor: { permissionActor: req.permission,
id: req.permission.id,
type: req.permission.type,
authMethod: req.permission.authMethod,
orgId: req.permission.orgId,
parentOrgId: req.permission.orgId,
rootOrgId: req.permission.rootOrgId
},
data: { data: {
limit: req.query.limit, limit: req.query.limit,
offset: req.query.offset, offset: req.query.offset,
@@ -173,7 +173,7 @@ export enum EventType {
UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth", UPDATE_TOKEN_IDENTITY_TOKEN_AUTH = "update-token-identity-token-auth",
GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth", GET_TOKENS_IDENTITY_TOKEN_AUTH = "get-tokens-identity-token-auth",
CREATE_CHILD_ORGANIZATION = "create-child-organization", CREATE_SUB_ORGANIZATION = "create-child-organization",
ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth", ADD_IDENTITY_TOKEN_AUTH = "add-identity-token-auth",
UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth", UPDATE_IDENTITY_TOKEN_AUTH = "update-identity-token-auth",
@@ -609,8 +609,8 @@ interface GetSecretsEvent {
}; };
} }
interface CreateChildOrganizationEvent { interface CreateSubOrganizationEvent {
type: EventType.CREATE_CHILD_ORGANIZATION; type: EventType.CREATE_SUB_ORGANIZATION;
metadata: { metadata: {
name: string; name: string;
organizationId: string; organizationId: string;
@@ -3873,7 +3873,7 @@ interface PamResourceDeleteEvent {
} }
export type Event = export type Event =
| CreateChildOrganizationEvent | CreateSubOrganizationEvent
| GetSecretsEvent | GetSecretsEvent
| GetSecretEvent | GetSecretEvent
| CreateSecretEvent | CreateSecretEvent
@@ -212,9 +212,9 @@ export const permissionServiceFactory = ({
const rootOrgId = permissionData?.[0]?.rootOrgId; const rootOrgId = permissionData?.[0]?.rootOrgId;
const isChild = Boolean(rootOrgId); const isChild = Boolean(rootOrgId);
if (scope === OrganizationActionScope.ParentOrganization && isChild) { if (scope === OrganizationActionScope.ParentOrganization && isChild) {
throw new BadRequestError({ message: `Child organization cannot do this operation` }); throw new ForbiddenRequestError({ message: `Child organization cannot do this operation` });
} else if (scope === OrganizationActionScope.ChildOrganization && !isChild) { } else if (scope === OrganizationActionScope.ChildOrganization && !isChild) {
throw new BadRequestError({ message: `Parent organization cannot do this operation` }); throw new ForbiddenRequestError({ message: `Parent organization cannot do this operation` });
} }
const permissionFromRoles = permissionData.flatMap((membership) => { const permissionFromRoles = permissionData.flatMap((membership) => {
+4 -4
View File
@@ -719,12 +719,12 @@ export const ORGANIZATIONS = {
export const SUB_ORGANIZATIONS = { export const SUB_ORGANIZATIONS = {
CREATE: { CREATE: {
name: "The name of the child organization to create." name: "The name of the sub organization to create."
}, },
LIST: { LIST: {
limit: "The number of child organizations to return.", limit: "The number of sub organizations to return.",
offset: "The offset to start from. If you enter 10, it will start from the 10th child organization.", offset: "The offset to start from. If you enter 10, it will start from the 10th sub organization.",
isAccessible: "Filter to only return child organizations that the actor has access to." isAccessible: "Filter to only return sub organizations that the actor has access to."
} }
} as const; } as const;
@@ -8,10 +8,10 @@ import { TScimTokenJwtPayload } from "@app/ee/services/scim/scim-types";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto"; import { crypto } from "@app/lib/crypto";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { GenericResourceNameSchema } from "@app/server/lib/schemas";
import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type"; import { ActorType, AuthMethod, AuthMode, AuthModeJwtTokenPayload, AuthTokenType } from "@app/services/auth/auth-type";
import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-token/identity-access-token-types";
import { getServerCfg } from "@app/services/super-admin/super-admin-service"; import { getServerCfg } from "@app/services/super-admin/super-admin-service";
import { GenericResourceNameSchema } from "@app/server/lib/schemas";
export type TAuthMode = export type TAuthMode =
| { | {
@@ -243,7 +243,7 @@ export const injectIdentity = fp(
requestContext.set("orgId", orgId); requestContext.set("orgId", orgId);
if (subOrganizationSelector) if (subOrganizationSelector)
throw new BadRequestError({ message: `Service token doesn't support sub organization selector` }); throw new BadRequestError({ message: `SCIM token doesn't support sub organization selector` });
req.auth = { req.auth = {
authMode: AuthMode.SCIM_TOKEN, authMode: AuthMode.SCIM_TOKEN,
@@ -216,7 +216,7 @@ export const identityAccessTokenServiceFactory = ({
if (subOrganizationSelector) { if (subOrganizationSelector) {
const subOrganization = await orgDAL.findOne({ rootOrgId, slug: subOrganizationSelector }); const subOrganization = await orgDAL.findOne({ rootOrgId, slug: subOrganizationSelector });
if (!subOrganizationSelector) if (!subOrganization)
throw new BadRequestError({ message: `Sub organization ${subOrganizationSelector} not found` }); throw new BadRequestError({ message: `Sub organization ${subOrganizationSelector} not found` });
const identityOrgMembership = await membershipIdentityDAL.findOne({ const identityOrgMembership = await membershipIdentityDAL.findOne({
@@ -24,9 +24,9 @@ import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { ActorType, AuthTokenType } from "../auth/auth-type"; import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
import { TOrgDALFactory } from "../org/org-dal"; import { TOrgDALFactory } from "../org/org-dal";
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
@@ -23,9 +23,9 @@ import {
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type"; import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
import { TOrgDALFactory } from "../org/org-dal"; import { TOrgDALFactory } from "../org/org-dal";
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
@@ -20,9 +20,9 @@ import {
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type"; import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
import { TOrgDALFactory } from "../org/org-dal"; import { TOrgDALFactory } from "../org/org-dal";
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
@@ -20,9 +20,9 @@ import {
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip"; import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
import { ActorType, AuthTokenType } from "../auth/auth-type"; import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
import { TOrgDALFactory } from "../org/org-dal"; import { TOrgDALFactory } from "../org/org-dal";
import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns"; import { validateIdentityUpdateForSuperAdminPrivileges } from "../super-admin/super-admin-fns";
@@ -23,6 +23,7 @@ import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr, TIp } from
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { ActorType, AuthTokenType } from "../auth/auth-type"; import { ActorType, AuthTokenType } from "../auth/auth-type";
import { TIdentityDALFactory } from "../identity/identity-dal";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types"; import { TIdentityAccessTokenJwtPayload } from "../identity-access-token/identity-access-token-types";
import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal"; import { TMembershipIdentityDALFactory } from "../membership-identity/membership-identity-dal";
@@ -41,7 +42,6 @@ import {
TRevokeUaDTO, TRevokeUaDTO,
TUpdateUaDTO TUpdateUaDTO
} from "./identity-ua-types"; } from "./identity-ua-types";
import { TIdentityDALFactory } from "../identity/identity-dal";
type TIdentityUaServiceFactoryDep = { type TIdentityUaServiceFactoryDep = {
identityDAL: Pick<TIdentityDALFactory, "findById">; identityDAL: Pick<TIdentityDALFactory, "findById">;
@@ -314,10 +314,6 @@ export const identityUaServiceFactory = ({
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" }); throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
} }
if (identityMembershipOrg.identity.identityOrgId !== actorOrgId) {
throw new ForbiddenRequestError({ message: "Sub organization not authorized to access this identity" });
}
if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) { if (accessTokenMaxTTL > 0 && accessTokenTTL > accessTokenMaxTTL) {
throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" }); throw new BadRequestError({ message: "Access token TTL cannot be greater than max TTL" });
} }
@@ -356,7 +356,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
} }
}; };
// this right nwo only support sub organization // this right now only support sub organization
const listAvailableIdentities = async (orgId: string, rootOrgId: string) => { const listAvailableIdentities = async (orgId: string, rootOrgId: string) => {
try { try {
const usersConnectedToOrg = db const usersConnectedToOrg = db
@@ -381,7 +381,7 @@ export const membershipIdentityDALFactory = (db: TDbClient) => {
return docs; return docs;
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "ListAvailableUsers" }); throw new DatabaseError({ error, name: "ListAvailableIdentities" });
} }
}; };
@@ -57,7 +57,7 @@ export const newOrgMembershipIdentityFactory = ({
const identityDetails = await identityDAL.findById(dto.data.identityId); const identityDetails = await identityDAL.findById(dto.data.identityId);
if (identityDetails.orgId !== dto.permission.rootOrgId) { if (identityDetails.orgId !== dto.permission.rootOrgId) {
throw new BadRequestError({ message: "Only identites from parent organization can be invited" }); throw new BadRequestError({ message: "Only identities from parent organization can be invited" });
} }
const permissionRoles = await permissionService.getOrgPermissionByRoles( const permissionRoles = await permissionService.getOrgPermissionByRoles(
@@ -143,11 +143,11 @@ export const newOrgMembershipIdentityFactory = ({
scope: OrganizationActionScope.ChildOrganization scope: OrganizationActionScope.ChildOrganization
}); });
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionIdentityActions.Delete, OrgPermissionSubjects.Identity);
const identityDetails = await identityDAL.findById(dto.selector.identityId); const identityDetails = await identityDAL.findById(dto.selector.identityId);
if (identityDetails.orgId !== dto.permission.rootOrgId) { if (identityDetails.orgId !== dto.permission.rootOrgId) {
throw new BadRequestError({ message: "Only identites from parent organization can do this operation" }); throw new BadRequestError({ message: "Only identities from parent organization can do this operation" });
} }
if (identityDetails.orgId === dto.permission.orgId) { if (identityDetails.orgId === dto.permission.orgId) {
@@ -291,7 +291,7 @@ export const membershipUserDALFactory = (db: TDbClient) => {
} }
}; };
// this right nwo only support sub organization // this right now only support sub organization
const listAvailableUsers = async (orgId: string, rootOrgId: string) => { const listAvailableUsers = async (orgId: string, rootOrgId: string) => {
try { try {
const usersConnectedToOrg = db const usersConnectedToOrg = db
@@ -92,10 +92,15 @@ export const newOrgMembershipUserFactory = ({
}, },
scopeOrgId: org.rootOrgId scopeOrgId: org.rootOrgId
}); });
if (rootOrgMembership.length !== newMembers.length) if (rootOrgMembership.length !== newMembers.length) {
const emails = newMembers
.filter((user) => !rootOrgMembership.find((i) => i.actorUserId === user.id))
.map((el) => el.email)
.join(",");
throw new BadRequestError({ throw new BadRequestError({
message: "User doesn't have membership in root organization" message: `Users with email ${emails} doesn't have membership in root organization`
}); });
}
} }
}; };
@@ -2,6 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { identitiesKeys } from "../identities";
import { import {
TCreateOrgIdentityMembershipDTO, TCreateOrgIdentityMembershipDTO,
TDeleteOrgIdentityMembershipDTO, TDeleteOrgIdentityMembershipDTO,
@@ -19,8 +20,7 @@ export const useCreateOrgIdentityMembership = () => {
return data.identityMembership; return data.identityMembership;
}, },
onSuccess: () => { onSuccess: () => {
// Invalidate relevant queries if needed queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentities({ search: {} }) });
queryClient.invalidateQueries({ queryKey: ["organization"] });
} }
}); });
}; };
@@ -35,8 +35,7 @@ export const useDeleteOrgIdentityMembership = () => {
return data.identityMembership; return data.identityMembership;
}, },
onSuccess: () => { onSuccess: () => {
// Invalidate relevant queries if needed queryClient.invalidateQueries({ queryKey: identitiesKeys.searchIdentities({ search: {} }) });
queryClient.invalidateQueries({ queryKey: ["organization"] });
} }
}); });
}; };
@@ -344,7 +344,7 @@ export const Navbar = () => {
size="xs" size="xs"
className="flex w-full items-center justify-start p-0 font-normal" className="flex w-full items-center justify-start p-0 font-normal"
leftIcon={ leftIcon={
currentOrg?.id === org.id && ( currentOrg?.parentOrgId === org.id && (
<FontAwesomeIcon <FontAwesomeIcon
icon={faCheck} icon={faCheck}
className="mr-3 text-primary" className="mr-3 text-primary"
@@ -654,7 +654,7 @@ export const Navbar = () => {
subTitle="Define a new sub-organization under your current organization." subTitle="Define a new sub-organization under your current organization."
> >
<div className="mb-2"> <div className="mb-2">
<NewSubOrganizationForm onClose={() => setShowSubOrgForm(true)} /> <NewSubOrganizationForm onClose={() => setShowSubOrgForm(false)} />
</div> </div>
</ModalContent> </ModalContent>
</Modal> </Modal>
@@ -3,8 +3,8 @@ import { motion } from "framer-motion";
import { CreateOrgModal } from "@app/components/organization/CreateOrgModal"; import { CreateOrgModal } from "@app/components/organization/CreateOrgModal";
import { Tab, TabList, Tabs } from "@app/components/v2"; import { Tab, TabList, Tabs } from "@app/components/v2";
import { usePopUp } from "@app/hooks";
import { useOrganization } from "@app/context"; import { useOrganization } from "@app/context";
import { usePopUp } from "@app/hooks";
type Props = { type Props = {
isHidden?: boolean; isHidden?: boolean;
@@ -243,7 +243,7 @@ export const IdentitySection = withPermission(
> >
<ModalContent <ModalContent
title="Assign Existing Identity" title="Assign Existing Identity"
subTitle="Assign an existing identity from your organization or namespace to this project. The identity will continue to be managed at its original scope." subTitle="Assign an existing identity from your root organization to the sub organization. The identity will continue to be managed at its original scope."
> >
<IdentityLinkForm onClose={() => handlePopUpClose("linkIdentity")} /> <IdentityLinkForm onClose={() => handlePopUpClose("linkIdentity")} />
</ModalContent> </ModalContent>
@@ -370,7 +370,7 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
<Td> <Td>
<p className="truncate"> <p className="truncate">
<FontAwesomeIcon size="sm" className="mr-1.5" icon={faBuilding} /> <FontAwesomeIcon size="sm" className="mr-1.5" icon={faBuilding} />
{currentOrg.id === orgId ? "Organization" : "Root Organization"} {currentOrg.id === orgId ? "Sub Organization" : "Root Organization"}
</p> </p>
</Td> </Td>
)} )}
@@ -1,4 +1,5 @@
import { createFileRoute } from "@tanstack/react-router"; import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
import { AppConnectionsPage } from "./AppConnectionsPage"; import { AppConnectionsPage } from "./AppConnectionsPage";
@@ -6,6 +7,11 @@ export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/organization/app-connections/" "/_authenticate/_inject-org-details/_org-layout/organization/app-connections/"
)({ )({
component: AppConnectionsPage, component: AppConnectionsPage,
validateSearch: z.object({
error: z.string().optional(),
success: z.string().optional(),
connectionId: z.string().optional()
}),
context: () => ({ context: () => ({
breadcrumbs: [ breadcrumbs: [
{ {
@@ -145,7 +145,7 @@ export const IdentityDetailsSection = ({ identityId, handlePopUpOpen, isOrgIdent
</div> </div>
{isSubOrganization && ( {isSubOrganization && (
<div className="mb-4"> <div className="mb-4">
<p className="text-sm font-medium text-mineshaft-300">Manage By</p> <p className="text-sm font-medium text-mineshaft-300">Managed By</p>
<p className="text-sm text-mineshaft-300"> <p className="text-sm text-mineshaft-300">
{isOrgIdentity ? "Organization" : "Root Organization"} {isOrgIdentity ? "Organization" : "Root Organization"}
</p> </p>
@@ -3,6 +3,7 @@ import { Controller, useForm } from "react-hook-form";
import { faCheck, faCopy, faRedo } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faCopy, faRedo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useSearch } from "@tanstack/react-router";
import { z } from "zod"; import { z } from "zod";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
@@ -22,7 +23,6 @@ import {
import { useTimedReset } from "@app/hooks"; import { useTimedReset } from "@app/hooks";
import { useCreatePublicSharedSecret, useCreateSharedSecret } from "@app/hooks/api"; import { useCreatePublicSharedSecret, useCreateSharedSecret } from "@app/hooks/api";
import { SecretSharingAccessType } from "@app/hooks/api/secretSharing"; import { SecretSharingAccessType } from "@app/hooks/api/secretSharing";
import { useSearch } from "@tanstack/react-router";
// values in ms // values in ms
const expiresInOptions = [ const expiresInOptions = [