mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 06:28:11 +00:00
Merge pull request #3688 from Infisical/daniel/super-admin-view-orgs
feat(instance-management): organizations overview and control
This commit is contained in:
@@ -741,12 +741,14 @@ export const registerRoutes = async (
|
|||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
identityTokenAuthDAL,
|
identityTokenAuthDAL,
|
||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
|
orgMembershipDAL,
|
||||||
identityOrgMembershipDAL,
|
identityOrgMembershipDAL,
|
||||||
authService: loginService,
|
authService: loginService,
|
||||||
serverCfgDAL: superAdminDAL,
|
serverCfgDAL: superAdminDAL,
|
||||||
kmsRootConfigDAL,
|
kmsRootConfigDAL,
|
||||||
orgService,
|
orgService,
|
||||||
keyStore,
|
keyStore,
|
||||||
|
orgDAL,
|
||||||
licenseService,
|
licenseService,
|
||||||
kmsService,
|
kmsService,
|
||||||
microsoftTeamsService,
|
microsoftTeamsService,
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import DOMPurify from "isomorphic-dompurify";
|
import DOMPurify from "isomorphic-dompurify";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentitiesSchema, OrganizationsSchema, SuperAdminSchema, UsersSchema } from "@app/db/schemas";
|
import {
|
||||||
|
IdentitiesSchema,
|
||||||
|
OrganizationsSchema,
|
||||||
|
OrgMembershipsSchema,
|
||||||
|
SuperAdminSchema,
|
||||||
|
UsersSchema
|
||||||
|
} from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -161,6 +167,129 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/organization-management/organizations",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
searchTerm: z.string().default(""),
|
||||||
|
offset: z.coerce.number().default(0),
|
||||||
|
limit: z.coerce.number().max(100).default(20)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
organizations: OrganizationsSchema.extend({
|
||||||
|
members: z
|
||||||
|
.object({
|
||||||
|
user: z.object({
|
||||||
|
id: z.string(),
|
||||||
|
email: z.string().nullish(),
|
||||||
|
username: z.string(),
|
||||||
|
firstName: z.string().nullish(),
|
||||||
|
lastName: z.string().nullish()
|
||||||
|
}),
|
||||||
|
membershipId: z.string(),
|
||||||
|
role: z.string(),
|
||||||
|
roleId: z.string().nullish()
|
||||||
|
})
|
||||||
|
.array(),
|
||||||
|
projects: z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
createdAt: z.date()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
}).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: (req, res, done) => {
|
||||||
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
|
verifySuperAdmin(req, res, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const organizations = await server.services.superAdmin.getOrganizations({
|
||||||
|
...req.query
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
organizations
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/organization-management/organizations/:organizationId/memberships/:membershipId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
organizationId: z.string(),
|
||||||
|
membershipId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
organizationMembership: OrgMembershipsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: (req, res, done) => {
|
||||||
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
|
verifySuperAdmin(req, res, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const organizationMembership = await server.services.superAdmin.deleteOrganizationMembership(
|
||||||
|
req.params.organizationId,
|
||||||
|
req.params.membershipId,
|
||||||
|
req.permission.id,
|
||||||
|
req.permission.type
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
organizationMembership
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/organization-management/organizations/:organizationId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
organizationId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
organization: OrganizationsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: (req, res, done) => {
|
||||||
|
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||||
|
verifySuperAdmin(req, res, done);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const organization = await server.services.superAdmin.deleteOrganization(req.params.organizationId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
organization
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/identity-management/identities",
|
url: "/identity-management/identities",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import { TDbClient } from "@app/db";
|
import { TDbClient } from "@app/db";
|
||||||
import {
|
import {
|
||||||
|
OrganizationsSchema,
|
||||||
OrgMembershipRole,
|
OrgMembershipRole,
|
||||||
TableName,
|
TableName,
|
||||||
TOrganizations,
|
TOrganizations,
|
||||||
@@ -12,7 +13,15 @@ import {
|
|||||||
TUserEncryptionKeys
|
TUserEncryptionKeys
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt, withTransaction } from "@app/lib/knex";
|
import {
|
||||||
|
buildFindFilter,
|
||||||
|
ormify,
|
||||||
|
selectAllTableCols,
|
||||||
|
sqlNestRelationships,
|
||||||
|
TFindFilter,
|
||||||
|
TFindOpt,
|
||||||
|
withTransaction
|
||||||
|
} from "@app/lib/knex";
|
||||||
import { generateKnexQueryFromScim } from "@app/lib/knex/scim";
|
import { generateKnexQueryFromScim } from "@app/lib/knex/scim";
|
||||||
|
|
||||||
import { OrgAuthMethod } from "./org-types";
|
import { OrgAuthMethod } from "./org-types";
|
||||||
@@ -22,6 +31,110 @@ export type TOrgDALFactory = ReturnType<typeof orgDALFactory>;
|
|||||||
export const orgDALFactory = (db: TDbClient) => {
|
export const orgDALFactory = (db: TDbClient) => {
|
||||||
const orgOrm = ormify(db, TableName.Organization);
|
const orgOrm = ormify(db, TableName.Organization);
|
||||||
|
|
||||||
|
const findOrganizationsByFilter = async ({
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
searchTerm,
|
||||||
|
sortBy
|
||||||
|
}: {
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
searchTerm: string;
|
||||||
|
sortBy?: keyof TOrganizations;
|
||||||
|
}) => {
|
||||||
|
try {
|
||||||
|
const query = db.replicaNode()(TableName.Organization);
|
||||||
|
|
||||||
|
// Build the subquery for limited organization IDs
|
||||||
|
const orgSubquery = db.replicaNode().select("id").from(TableName.Organization);
|
||||||
|
|
||||||
|
if (searchTerm) {
|
||||||
|
void orgSubquery.where((qb) => {
|
||||||
|
void qb.whereILike(`${TableName.Organization}.name`, `%${searchTerm}%`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortBy) {
|
||||||
|
void orgSubquery.orderBy(sortBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void orgSubquery.limit(limit).offset(offset);
|
||||||
|
|
||||||
|
// Main query with joins, limited to the subquery results
|
||||||
|
const docs = await query
|
||||||
|
.whereIn(`${TableName.Organization}.id`, orgSubquery)
|
||||||
|
.leftJoin(TableName.Project, `${TableName.Organization}.id`, `${TableName.Project}.orgId`)
|
||||||
|
.leftJoin(TableName.OrgMembership, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`)
|
||||||
|
.leftJoin(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
|
||||||
|
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
|
||||||
|
.where((qb) => {
|
||||||
|
void qb.where(`${TableName.Users}.isGhost`, false).orWhereNull(`${TableName.Users}.id`);
|
||||||
|
})
|
||||||
|
.select(selectAllTableCols(TableName.Organization))
|
||||||
|
.select(db.ref("name").withSchema(TableName.Project).as("projectName"))
|
||||||
|
.select(db.ref("id").withSchema(TableName.Project).as("projectId"))
|
||||||
|
.select(db.ref("slug").withSchema(TableName.Project).as("projectSlug"))
|
||||||
|
.select(db.ref("createdAt").withSchema(TableName.Project).as("projectCreatedAt"))
|
||||||
|
.select(db.ref("email").withSchema(TableName.Users).as("userEmail"))
|
||||||
|
.select(db.ref("username").withSchema(TableName.Users).as("username"))
|
||||||
|
.select(db.ref("firstName").withSchema(TableName.Users).as("firstName"))
|
||||||
|
.select(db.ref("lastName").withSchema(TableName.Users).as("lastName"))
|
||||||
|
.select(db.ref("id").withSchema(TableName.Users).as("userId"))
|
||||||
|
.select(db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"))
|
||||||
|
.select(db.ref("role").withSchema(TableName.OrgMembership).as("orgMembershipRole"))
|
||||||
|
.select(db.ref("roleId").withSchema(TableName.OrgMembership).as("orgMembershipRoleId"))
|
||||||
|
.select(db.ref("name").withSchema(TableName.OrgRoles).as("orgMembershipRoleName"));
|
||||||
|
|
||||||
|
const formattedDocs = sqlNestRelationships({
|
||||||
|
data: docs,
|
||||||
|
key: "id",
|
||||||
|
parentMapper: (data) => OrganizationsSchema.parse(data),
|
||||||
|
childrenMapper: [
|
||||||
|
{
|
||||||
|
key: "projectId",
|
||||||
|
label: "projects" as const,
|
||||||
|
mapper: ({ projectId, projectName, projectSlug, projectCreatedAt }) => ({
|
||||||
|
id: projectId,
|
||||||
|
name: projectName,
|
||||||
|
slug: projectSlug,
|
||||||
|
createdAt: projectCreatedAt
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "userId",
|
||||||
|
label: "members" as const,
|
||||||
|
mapper: ({
|
||||||
|
userId,
|
||||||
|
userEmail,
|
||||||
|
username,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
orgMembershipId,
|
||||||
|
orgMembershipRole,
|
||||||
|
orgMembershipRoleName,
|
||||||
|
orgMembershipRoleId
|
||||||
|
}) => ({
|
||||||
|
user: {
|
||||||
|
id: userId,
|
||||||
|
email: userEmail,
|
||||||
|
username,
|
||||||
|
firstName,
|
||||||
|
lastName
|
||||||
|
},
|
||||||
|
membershipId: orgMembershipId,
|
||||||
|
role: orgMembershipRoleName || orgMembershipRole, // custom role name or pre-defined role name
|
||||||
|
roleId: orgMembershipRoleId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return formattedDocs;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Find organizations by filter" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const findOrgById = async (orgId: string) => {
|
const findOrgById = async (orgId: string) => {
|
||||||
try {
|
try {
|
||||||
const org = (await db
|
const org = (await db
|
||||||
@@ -507,6 +620,7 @@ export const orgDALFactory = (db: TDbClient) => {
|
|||||||
findOrgById,
|
findOrgById,
|
||||||
findOrgBySlug,
|
findOrgBySlug,
|
||||||
findAllOrgsByUserId,
|
findAllOrgsByUserId,
|
||||||
|
findOrganizationsByFilter,
|
||||||
ghostUserExists,
|
ghostUserExists,
|
||||||
findOrgMembersByUsername,
|
findOrgMembersByUsername,
|
||||||
findOrgMembersByRole,
|
findOrgMembersByRole,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
|||||||
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
import { TIdentityDALFactory } from "@app/services/identity/identity-dal";
|
||||||
|
|
||||||
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
import { TAuthLoginFactory } from "../auth/auth-login-service";
|
||||||
import { AuthMethod, AuthTokenType } from "../auth/auth-type";
|
import { ActorType, AuthMethod, AuthTokenType } from "../auth/auth-type";
|
||||||
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
|
import { TIdentityOrgDALFactory } from "../identity/identity-org-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";
|
||||||
@@ -21,7 +21,9 @@ import { TKmsRootConfigDALFactory } from "../kms/kms-root-config-dal";
|
|||||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||||
import { RootKeyEncryptionStrategy } from "../kms/kms-types";
|
import { RootKeyEncryptionStrategy } from "../kms/kms-types";
|
||||||
import { TMicrosoftTeamsServiceFactory } from "../microsoft-teams/microsoft-teams-service";
|
import { TMicrosoftTeamsServiceFactory } from "../microsoft-teams/microsoft-teams-service";
|
||||||
|
import { TOrgDALFactory } from "../org/org-dal";
|
||||||
import { TOrgServiceFactory } from "../org/org-service";
|
import { TOrgServiceFactory } from "../org/org-service";
|
||||||
|
import { TOrgMembershipDALFactory } from "../org-membership/org-membership-dal";
|
||||||
import { TUserDALFactory } from "../user/user-dal";
|
import { TUserDALFactory } from "../user/user-dal";
|
||||||
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
import { TUserAliasDALFactory } from "../user-alias/user-alias-dal";
|
||||||
import { UserAliasType } from "../user-alias/user-alias-types";
|
import { UserAliasType } from "../user-alias/user-alias-types";
|
||||||
@@ -33,7 +35,8 @@ import {
|
|||||||
TAdminBootstrapInstanceDTO,
|
TAdminBootstrapInstanceDTO,
|
||||||
TAdminGetIdentitiesDTO,
|
TAdminGetIdentitiesDTO,
|
||||||
TAdminGetUsersDTO,
|
TAdminGetUsersDTO,
|
||||||
TAdminSignUpDTO
|
TAdminSignUpDTO,
|
||||||
|
TGetOrganizationsDTO
|
||||||
} from "./super-admin-types";
|
} from "./super-admin-types";
|
||||||
|
|
||||||
type TSuperAdminServiceFactoryDep = {
|
type TSuperAdminServiceFactoryDep = {
|
||||||
@@ -41,6 +44,8 @@ type TSuperAdminServiceFactoryDep = {
|
|||||||
identityTokenAuthDAL: TIdentityTokenAuthDALFactory;
|
identityTokenAuthDAL: TIdentityTokenAuthDALFactory;
|
||||||
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
|
||||||
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||||
|
orgDAL: TOrgDALFactory;
|
||||||
|
orgMembershipDAL: TOrgMembershipDALFactory;
|
||||||
serverCfgDAL: TSuperAdminDALFactory;
|
serverCfgDAL: TSuperAdminDALFactory;
|
||||||
userDAL: TUserDALFactory;
|
userDAL: TUserDALFactory;
|
||||||
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
userAliasDAL: Pick<TUserAliasDALFactory, "findOne">;
|
||||||
@@ -73,6 +78,8 @@ export const superAdminServiceFactory = ({
|
|||||||
serverCfgDAL,
|
serverCfgDAL,
|
||||||
userDAL,
|
userDAL,
|
||||||
identityDAL,
|
identityDAL,
|
||||||
|
orgDAL,
|
||||||
|
orgMembershipDAL,
|
||||||
userAliasDAL,
|
userAliasDAL,
|
||||||
authService,
|
authService,
|
||||||
orgService,
|
orgService,
|
||||||
@@ -521,6 +528,47 @@ export const superAdminServiceFactory = ({
|
|||||||
return updatedUser;
|
return updatedUser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getOrganizations = async ({ offset, limit, searchTerm }: TGetOrganizationsDTO) => {
|
||||||
|
const organizations = await orgDAL.findOrganizationsByFilter({
|
||||||
|
offset,
|
||||||
|
searchTerm,
|
||||||
|
sortBy: "name",
|
||||||
|
limit
|
||||||
|
});
|
||||||
|
return organizations;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteOrganization = async (organizationId: string) => {
|
||||||
|
const organization = await orgDAL.deleteById(organizationId);
|
||||||
|
return organization;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteOrganizationMembership = async (
|
||||||
|
organizationId: string,
|
||||||
|
membershipId: string,
|
||||||
|
actorId: string,
|
||||||
|
actorType: ActorType
|
||||||
|
) => {
|
||||||
|
if (actorType === ActorType.USER) {
|
||||||
|
const orgMembership = await orgMembershipDAL.findById(membershipId);
|
||||||
|
if (!orgMembership) {
|
||||||
|
throw new NotFoundError({ name: "Organization Membership", message: "Organization membership not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orgMembership.userId === actorId) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "You cannot remove yourself from the organization from the instance management panel."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [organizationMembership] = await orgMembershipDAL.delete({
|
||||||
|
orgId: organizationId,
|
||||||
|
id: membershipId
|
||||||
|
});
|
||||||
|
return organizationMembership;
|
||||||
|
};
|
||||||
|
|
||||||
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||||
const identities = await identityDAL.getIdentitiesByFilter({
|
const identities = await identityDAL.getIdentitiesByFilter({
|
||||||
limit,
|
limit,
|
||||||
@@ -663,6 +711,9 @@ export const superAdminServiceFactory = ({
|
|||||||
deleteIdentitySuperAdminAccess,
|
deleteIdentitySuperAdminAccess,
|
||||||
deleteUserSuperAdminAccess,
|
deleteUserSuperAdminAccess,
|
||||||
invalidateCache,
|
invalidateCache,
|
||||||
checkIfInvalidatingCache
|
checkIfInvalidatingCache,
|
||||||
|
getOrganizations,
|
||||||
|
deleteOrganization,
|
||||||
|
deleteOrganizationMembership
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ export type TAdminGetIdentitiesDTO = {
|
|||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TGetOrganizationsDTO = {
|
||||||
|
offset: number;
|
||||||
|
limit: number;
|
||||||
|
searchTerm: string;
|
||||||
|
};
|
||||||
|
|
||||||
export enum LoginMethod {
|
export enum LoginMethod {
|
||||||
EMAIL = "email",
|
EMAIL = "email",
|
||||||
GOOGLE = "google",
|
GOOGLE = "google",
|
||||||
|
|||||||
@@ -1,16 +1,2 @@
|
|||||||
export {
|
export * from "./mutation";
|
||||||
useAdminDeleteUser,
|
export * from "./queries";
|
||||||
useAdminGrantServerAdminAccess,
|
|
||||||
useAdminRemoveIdentitySuperAdminAccess,
|
|
||||||
useCreateAdminUser,
|
|
||||||
useInvalidateCache,
|
|
||||||
useRemoveUserServerAdminAccess,
|
|
||||||
useUpdateServerConfig,
|
|
||||||
useUpdateServerEncryptionStrategy
|
|
||||||
} from "./mutation";
|
|
||||||
export {
|
|
||||||
useAdminGetUsers,
|
|
||||||
useGetAdminIntegrationsConfig,
|
|
||||||
useGetServerConfig,
|
|
||||||
useGetServerRootKmsEncryptionDetails
|
|
||||||
} from "./queries";
|
|
||||||
|
|||||||
@@ -63,6 +63,41 @@ export const useAdminDeleteUser = () => {
|
|||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: [adminStandaloneKeys.getUsers]
|
queryKey: [adminStandaloneKeys.getUsers]
|
||||||
});
|
});
|
||||||
|
queryClient.invalidateQueries({ queryKey: adminStandaloneKeys.getOrganizations });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminDeleteOrganizationMembership = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation<object, object, { organizationId: string; membershipId: string }>({
|
||||||
|
mutationFn: async ({ organizationId, membershipId }) => {
|
||||||
|
await apiRequest.delete(
|
||||||
|
`/api/v1/admin/organization-management/organizations/${organizationId}/memberships/${membershipId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [adminStandaloneKeys.getOrganizations]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAdminDeleteOrganization = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (organizationId: string) => {
|
||||||
|
await apiRequest.delete(
|
||||||
|
`/api/v1/admin/organization-management/organizations/${organizationId}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [adminStandaloneKeys.getOrganizations]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import { Identity } from "@app/hooks/api/identities/types";
|
|||||||
import { User } from "../types";
|
import { User } from "../types";
|
||||||
import {
|
import {
|
||||||
AdminGetIdentitiesFilters,
|
AdminGetIdentitiesFilters,
|
||||||
|
AdminGetOrganizationsFilters,
|
||||||
AdminGetUsersFilters,
|
AdminGetUsersFilters,
|
||||||
AdminIntegrationsConfig,
|
AdminIntegrationsConfig,
|
||||||
|
OrganizationWithProjects,
|
||||||
TGetInvalidatingCacheStatus,
|
TGetInvalidatingCacheStatus,
|
||||||
TGetServerRootKmsEncryptionDetails,
|
TGetServerRootKmsEncryptionDetails,
|
||||||
TServerConfig
|
TServerConfig
|
||||||
@@ -15,12 +17,15 @@ import {
|
|||||||
|
|
||||||
export const adminStandaloneKeys = {
|
export const adminStandaloneKeys = {
|
||||||
getUsers: "get-users",
|
getUsers: "get-users",
|
||||||
|
getOrganizations: "get-organizations",
|
||||||
getIdentities: "get-identities"
|
getIdentities: "get-identities"
|
||||||
};
|
};
|
||||||
|
|
||||||
export const adminQueryKeys = {
|
export const adminQueryKeys = {
|
||||||
serverConfig: () => ["server-config"] as const,
|
serverConfig: () => ["server-config"] as const,
|
||||||
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const,
|
getUsers: (filters: AdminGetUsersFilters) => [adminStandaloneKeys.getUsers, { filters }] as const,
|
||||||
|
getOrganizations: (filters: AdminGetOrganizationsFilters) =>
|
||||||
|
[adminStandaloneKeys.getOrganizations, { filters }] as const,
|
||||||
getIdentities: (filters: AdminGetIdentitiesFilters) =>
|
getIdentities: (filters: AdminGetIdentitiesFilters) =>
|
||||||
[adminStandaloneKeys.getIdentities, { filters }] as const,
|
[adminStandaloneKeys.getIdentities, { filters }] as const,
|
||||||
getAdminSlackConfig: () => ["admin-slack-config"] as const,
|
getAdminSlackConfig: () => ["admin-slack-config"] as const,
|
||||||
@@ -34,6 +39,28 @@ export const fetchServerConfig = async () => {
|
|||||||
return data.config;
|
return data.config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useAdminGetOrganizations = (filters: AdminGetOrganizationsFilters) => {
|
||||||
|
return useInfiniteQuery({
|
||||||
|
initialPageParam: 0,
|
||||||
|
queryKey: adminQueryKeys.getOrganizations(filters),
|
||||||
|
queryFn: async ({ pageParam }) => {
|
||||||
|
const { data } = await apiRequest.get<{ organizations: OrganizationWithProjects[] }>(
|
||||||
|
"/api/v1/admin/organization-management/organizations",
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
...filters,
|
||||||
|
offset: pageParam
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return data.organizations;
|
||||||
|
},
|
||||||
|
getNextPageParam: (lastPage, pages) =>
|
||||||
|
lastPage.length !== 0 ? pages.length * filters.limit : undefined
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetServerConfig = ({
|
export const useGetServerConfig = ({
|
||||||
options = {}
|
options = {}
|
||||||
}: {
|
}: {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Organization } from "../types";
|
||||||
|
|
||||||
export enum LoginMethod {
|
export enum LoginMethod {
|
||||||
EMAIL = "email",
|
EMAIL = "email",
|
||||||
GOOGLE = "google",
|
GOOGLE = "google",
|
||||||
@@ -8,6 +10,27 @@ export enum LoginMethod {
|
|||||||
OIDC = "oidc"
|
OIDC = "oidc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OrganizationWithProjects = Organization & {
|
||||||
|
members: {
|
||||||
|
user: {
|
||||||
|
id: string;
|
||||||
|
email: string | null;
|
||||||
|
username: string;
|
||||||
|
firstName: string | null;
|
||||||
|
lastName: string | null;
|
||||||
|
};
|
||||||
|
membershipId: string;
|
||||||
|
role: string;
|
||||||
|
roleId: string | null;
|
||||||
|
}[];
|
||||||
|
projects: {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
createdAt: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
export type TServerConfig = {
|
export type TServerConfig = {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
allowSignUp: boolean;
|
allowSignUp: boolean;
|
||||||
@@ -51,6 +74,11 @@ export type TCreateAdminUserDTO = {
|
|||||||
salt: string;
|
salt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AdminGetOrganizationsFilters = {
|
||||||
|
limit: number;
|
||||||
|
searchTerm: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type AdminGetUsersFilters = {
|
export type AdminGetUsersFilters = {
|
||||||
limit: number;
|
limit: number;
|
||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
|
|||||||
@@ -1,27 +1,23 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { faArrowLeft, faInfo, faMobile, faQuestion } from "@fortawesome/free-solid-svg-icons";
|
import { faMobile } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { Link, Outlet } from "@tanstack/react-router";
|
import { Outlet, useRouterState } from "@tanstack/react-router";
|
||||||
|
|
||||||
import { WishForm } from "@app/components/features/WishForm";
|
|
||||||
import { Banner } from "@app/components/page-frames/Banner";
|
import { Banner } from "@app/components/page-frames/Banner";
|
||||||
import {
|
import { BreadcrumbContainer, TBreadcrumbFormat } from "@app/components/v2";
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuTrigger
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { envConfig } from "@app/config/env";
|
|
||||||
import { useServerConfig } from "@app/context";
|
import { useServerConfig } from "@app/context";
|
||||||
import { ProjectType } from "@app/hooks/api/workspace/types";
|
|
||||||
|
|
||||||
import { InsecureConnectionBanner } from "../OrganizationLayout/components/InsecureConnectionBanner";
|
import { InsecureConnectionBanner } from "../OrganizationLayout/components/InsecureConnectionBanner";
|
||||||
import { INFISICAL_SUPPORT_OPTIONS } from "../OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar";
|
import { AdminSidebar } from "./Sidebar";
|
||||||
|
|
||||||
export const AdminLayout = () => {
|
export const AdminLayout = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { config } = useServerConfig();
|
const { config } = useServerConfig();
|
||||||
|
|
||||||
|
const matches = useRouterState({ select: (s) => s.matches.at(-1)?.context });
|
||||||
|
|
||||||
|
const breadcrumbs = matches && "breadcrumbs" in matches ? matches.breadcrumbs : undefined;
|
||||||
|
|
||||||
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
|
const containerHeight = config.pageFrameContent ? "h-[94vh]" : "h-screen";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -30,54 +26,11 @@ export const AdminLayout = () => {
|
|||||||
<div className={`dark hidden ${containerHeight} w-full flex-col overflow-x-hidden md:flex`}>
|
<div className={`dark hidden ${containerHeight} w-full flex-col overflow-x-hidden md:flex`}>
|
||||||
{!window.isSecureContext && <InsecureConnectionBanner />}
|
{!window.isSecureContext && <InsecureConnectionBanner />}
|
||||||
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
|
||||||
<aside className="dark w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60">
|
<AdminSidebar />
|
||||||
<nav className="items-between flex h-full flex-col justify-between overflow-y-auto dark:[color-scheme:dark]">
|
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 px-4 pb-4 dark:[color-scheme:dark]">
|
||||||
<div className="flex-grow">
|
{breadcrumbs ? (
|
||||||
<Link to={`/organization/${ProjectType.SecretManager}/overview` as const}>
|
<BreadcrumbContainer breadcrumbs={breadcrumbs as TBreadcrumbFormat[]} />
|
||||||
<div className="my-6 flex cursor-default items-center justify-center pr-2 text-sm text-mineshaft-300 hover:text-mineshaft-100">
|
) : null}
|
||||||
<FontAwesomeIcon icon={faArrowLeft} className="pr-3" />
|
|
||||||
Back to organization
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="relative mt-10 flex w-full cursor-default flex-col items-center px-3 text-sm text-mineshaft-400">
|
|
||||||
{(window.location.origin.includes("https://app.infisical.com") ||
|
|
||||||
window.location.origin.includes("https://gamma.infisical.com")) && <WishForm />}
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<div className="mb-2 w-full pl-5 duration-200 hover:text-mineshaft-200">
|
|
||||||
<FontAwesomeIcon icon={faQuestion} className="mr-3 px-[0.1rem]" />
|
|
||||||
Help & Support
|
|
||||||
</div>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="start" className="p-1">
|
|
||||||
{INFISICAL_SUPPORT_OPTIONS.map(([icon, text, url]) => (
|
|
||||||
<DropdownMenuItem key={url as string}>
|
|
||||||
<a
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
href={String(url)}
|
|
||||||
className="flex w-full items-center rounded-md font-normal text-mineshaft-300 duration-200"
|
|
||||||
>
|
|
||||||
<div className="relative flex w-full cursor-pointer select-none items-center justify-start rounded-md">
|
|
||||||
{icon}
|
|
||||||
<div className="text-sm">{text}</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</DropdownMenuItem>
|
|
||||||
))}
|
|
||||||
{envConfig.PLATFORM_VERSION && (
|
|
||||||
<div className="mb-2 mt-2 w-full cursor-default pl-5 text-sm duration-200 hover:text-mineshaft-200">
|
|
||||||
<FontAwesomeIcon icon={faInfo} className="mr-4 px-[0.1rem]" />
|
|
||||||
Version: {envConfig.PLATFORM_VERSION}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</aside>
|
|
||||||
<main className="flex-1 overflow-y-auto overflow-x-hidden bg-bunker-800 dark:[color-scheme:dark]">
|
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import { faArrowLeft, faInfo, faQuestion } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { Link, useMatchRoute } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
Menu,
|
||||||
|
MenuGroup,
|
||||||
|
MenuItem
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { envConfig } from "@app/config/env";
|
||||||
|
import { ProjectType } from "@app/hooks/api/workspace/types";
|
||||||
|
|
||||||
|
import { INFISICAL_SUPPORT_OPTIONS } from "../OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar";
|
||||||
|
|
||||||
|
const generalTabs = [
|
||||||
|
{
|
||||||
|
label: "General",
|
||||||
|
icon: "settings-cog",
|
||||||
|
link: "/admin/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Encryption",
|
||||||
|
icon: "lock-closed",
|
||||||
|
link: "/admin/encryption"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Authentication",
|
||||||
|
icon: "check",
|
||||||
|
link: "/admin/authentication"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Integrations",
|
||||||
|
icon: "sliding-carousel",
|
||||||
|
link: "/admin/integrations"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Caching",
|
||||||
|
icon: "note",
|
||||||
|
link: "/admin/caching"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const resourceTabs = [
|
||||||
|
{
|
||||||
|
label: "Organizations",
|
||||||
|
icon: "groups",
|
||||||
|
link: "/admin/resources/organizations"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "User Identities",
|
||||||
|
icon: "user",
|
||||||
|
link: "/admin/resources/user-identities"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Machine Identities",
|
||||||
|
icon: "key-user",
|
||||||
|
link: "/admin/resources/machine-identities"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const AdminSidebar = () => {
|
||||||
|
const matchRoute = useMatchRoute();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<aside className="dark w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60">
|
||||||
|
<nav className="items-between flex h-full flex-col justify-between overflow-y-auto dark:[color-scheme:dark]">
|
||||||
|
<div className="flex-grow">
|
||||||
|
<Link to={`/organization/${ProjectType.SecretManager}/overview` as const}>
|
||||||
|
<div className="my-6 flex cursor-default items-center justify-center pr-2 text-sm text-mineshaft-300 hover:text-mineshaft-100">
|
||||||
|
<FontAwesomeIcon icon={faArrowLeft} className="pr-3" />
|
||||||
|
Back to organization
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
<Menu>
|
||||||
|
<MenuGroup title="General">
|
||||||
|
{generalTabs.map((tab) => {
|
||||||
|
const isActive = matchRoute({ to: tab.link, fuzzy: false });
|
||||||
|
return (
|
||||||
|
<Link key={tab.link} to={tab.link}>
|
||||||
|
<MenuItem isSelected={Boolean(isActive)} icon={tab.icon}>
|
||||||
|
{tab.label}
|
||||||
|
</MenuItem>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MenuGroup>
|
||||||
|
<MenuGroup title="Resources">
|
||||||
|
{resourceTabs.map((tab) => {
|
||||||
|
const isActive = matchRoute({ to: tab.link, fuzzy: false });
|
||||||
|
return (
|
||||||
|
<Link key={tab.link} to={tab.link}>
|
||||||
|
<MenuItem isSelected={Boolean(isActive)} icon={tab.icon}>
|
||||||
|
{tab.label}
|
||||||
|
</MenuItem>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MenuGroup>
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
<div className="relative mb-4 mt-10 flex w-full cursor-default flex-col items-center px-3 text-sm text-mineshaft-400">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<div className="mb-2 w-full pl-5 duration-200 hover:text-mineshaft-200">
|
||||||
|
<FontAwesomeIcon icon={faQuestion} className="mr-3 px-[0.1rem]" />
|
||||||
|
Help & Support
|
||||||
|
</div>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="p-1">
|
||||||
|
{INFISICAL_SUPPORT_OPTIONS.map(([icon, text, url]) => (
|
||||||
|
<DropdownMenuItem key={url as string}>
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={String(url)}
|
||||||
|
className="flex w-full items-center rounded-md font-normal text-mineshaft-300 duration-200"
|
||||||
|
>
|
||||||
|
<div className="relative flex w-full cursor-pointer select-none items-center justify-start rounded-md">
|
||||||
|
{icon}
|
||||||
|
<div className="text-sm">{text}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
{envConfig.PLATFORM_VERSION && (
|
||||||
|
<div className="mb-2 mt-2 w-full cursor-default pl-5 text-sm duration-200 hover:text-mineshaft-200">
|
||||||
|
<FontAwesomeIcon icon={faInfo} className="mr-4 px-[0.1rem]" />
|
||||||
|
Version: {envConfig.PLATFORM_VERSION}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</aside>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { AuthenticationPageForm } from "./components";
|
||||||
|
|
||||||
|
export const AuthenticationPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader
|
||||||
|
title="Authentication"
|
||||||
|
description="Manage authentication settings for your Infisical instance."
|
||||||
|
/>
|
||||||
|
<AuthenticationPageForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+1
-1
@@ -20,7 +20,7 @@ const formSchema = z.object({
|
|||||||
|
|
||||||
type TAuthForm = z.infer<typeof formSchema>;
|
type TAuthForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
export const AuthPanel = () => {
|
export const AuthenticationPageForm = () => {
|
||||||
const { config } = useServerConfig();
|
const { config } = useServerConfig();
|
||||||
const { enabledLoginMethods } = config;
|
const { enabledLoginMethods } = config;
|
||||||
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
|
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { AuthenticationPageForm } from "./AuthenticationPageForm";
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { AuthenticationPage } from "./AuthenticationPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/authentication"
|
||||||
|
)({
|
||||||
|
component: AuthenticationPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Authentication",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin/authentication"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { CachingPageForm } from "./components";
|
||||||
|
|
||||||
|
export const CachingPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader title="Caching" description="Manage caching for your Infisical instance." />
|
||||||
|
<CachingPageForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+1
-1
@@ -10,7 +10,7 @@ import { useInvalidateCache } from "@app/hooks/api";
|
|||||||
import { useGetInvalidatingCacheStatus } from "@app/hooks/api/admin/queries";
|
import { useGetInvalidatingCacheStatus } from "@app/hooks/api/admin/queries";
|
||||||
import { CacheType } from "@app/hooks/api/admin/types";
|
import { CacheType } from "@app/hooks/api/admin/types";
|
||||||
|
|
||||||
export const CachingPanel = () => {
|
export const CachingPageForm = () => {
|
||||||
const { mutateAsync: invalidateCache } = useInvalidateCache();
|
const { mutateAsync: invalidateCache } = useInvalidateCache();
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { CachingPageForm } from "./CachingPageForm";
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { CachingPage } from "./CachingPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/caching"
|
||||||
|
)({
|
||||||
|
component: CachingPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Caching",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin/caching"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { EncryptionPageForm } from "./components";
|
||||||
|
|
||||||
|
export const EncryptionPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader
|
||||||
|
title="Encryption"
|
||||||
|
description="Manage encryption settings for your Infisical instance."
|
||||||
|
/>
|
||||||
|
<EncryptionPageForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+6
-8
@@ -8,11 +8,11 @@ import { createNotification } from "@app/components/notifications";
|
|||||||
import { Button, FormControl, Select, SelectItem } from "@app/components/v2";
|
import { Button, FormControl, Select, SelectItem } from "@app/components/v2";
|
||||||
import { useSubscription } from "@app/context";
|
import { useSubscription } from "@app/context";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useUpdateServerEncryptionStrategy } from "@app/hooks/api";
|
|
||||||
import {
|
import {
|
||||||
RootKeyEncryptionStrategy,
|
useGetServerRootKmsEncryptionDetails,
|
||||||
TGetServerRootKmsEncryptionDetails
|
useUpdateServerEncryptionStrategy
|
||||||
} from "@app/hooks/api/admin/types";
|
} from "@app/hooks/api";
|
||||||
|
import { RootKeyEncryptionStrategy } from "@app/hooks/api/admin/types";
|
||||||
|
|
||||||
const formSchema = z.object({
|
const formSchema = z.object({
|
||||||
encryptionStrategy: z.nativeEnum(RootKeyEncryptionStrategy)
|
encryptionStrategy: z.nativeEnum(RootKeyEncryptionStrategy)
|
||||||
@@ -25,11 +25,9 @@ const strategies: Record<RootKeyEncryptionStrategy, string> = {
|
|||||||
|
|
||||||
type TForm = z.infer<typeof formSchema>;
|
type TForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
type Props = {
|
export const EncryptionPageForm = () => {
|
||||||
rootKmsDetails?: TGetServerRootKmsEncryptionDetails;
|
const { data: rootKmsDetails } = useGetServerRootKmsEncryptionDetails();
|
||||||
};
|
|
||||||
|
|
||||||
export const EncryptionPanel = ({ rootKmsDetails }: Props) => {
|
|
||||||
const { mutateAsync: updateEncryptionStrategy } = useUpdateServerEncryptionStrategy();
|
const { mutateAsync: updateEncryptionStrategy } = useUpdateServerEncryptionStrategy();
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { EncryptionPageForm } from "./EncryptionPageForm";
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { EncryptionPage } from "./EncryptionPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/encryption"
|
||||||
|
)({
|
||||||
|
component: EncryptionPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Encryption",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin/encryption"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { GeneralPageForm } from "./components";
|
||||||
|
|
||||||
|
export const GeneralPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader
|
||||||
|
title="General"
|
||||||
|
description="Manage general settings for your Infisical instance."
|
||||||
|
/>
|
||||||
|
<GeneralPageForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,309 @@
|
|||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { faAt } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectClear,
|
||||||
|
SelectItem,
|
||||||
|
Switch,
|
||||||
|
TextArea
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useServerConfig } from "@app/context";
|
||||||
|
import { useGetOrganizations, useUpdateServerConfig } from "@app/hooks/api";
|
||||||
|
|
||||||
|
enum SignUpModes {
|
||||||
|
Disabled = "disabled",
|
||||||
|
Anyone = "anyone"
|
||||||
|
}
|
||||||
|
|
||||||
|
const formSchema = z.object({
|
||||||
|
signUpMode: z.nativeEnum(SignUpModes),
|
||||||
|
allowedSignUpDomain: z.string().optional().nullable(),
|
||||||
|
trustSamlEmails: z.boolean(),
|
||||||
|
trustLdapEmails: z.boolean(),
|
||||||
|
trustOidcEmails: z.boolean(),
|
||||||
|
defaultAuthOrgId: z.string(),
|
||||||
|
authConsentContent: z.string().optional().default(""),
|
||||||
|
pageFrameContent: z.string().optional().default("")
|
||||||
|
});
|
||||||
|
|
||||||
|
type TDashboardForm = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
export const GeneralPageForm = () => {
|
||||||
|
const data = useServerConfig();
|
||||||
|
const { config } = data;
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit,
|
||||||
|
watch,
|
||||||
|
formState: { isSubmitting, isDirty }
|
||||||
|
} = useForm<TDashboardForm>({
|
||||||
|
resolver: zodResolver(formSchema),
|
||||||
|
values: {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
signUpMode: config.allowSignUp ? SignUpModes.Anyone : SignUpModes.Disabled,
|
||||||
|
allowedSignUpDomain: config.allowedSignUpDomain,
|
||||||
|
trustSamlEmails: config.trustSamlEmails,
|
||||||
|
trustLdapEmails: config.trustLdapEmails,
|
||||||
|
trustOidcEmails: config.trustOidcEmails,
|
||||||
|
defaultAuthOrgId: config.defaultAuthOrgId ?? "",
|
||||||
|
authConsentContent: config.authConsentContent ?? "",
|
||||||
|
pageFrameContent: config.pageFrameContent ?? ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const signUpMode = watch("signUpMode");
|
||||||
|
const defaultAuthOrgId = watch("defaultAuthOrgId");
|
||||||
|
|
||||||
|
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
|
||||||
|
|
||||||
|
const organizations = useGetOrganizations();
|
||||||
|
|
||||||
|
const onFormSubmit = async (formData: TDashboardForm) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
allowedSignUpDomain,
|
||||||
|
trustSamlEmails,
|
||||||
|
trustLdapEmails,
|
||||||
|
trustOidcEmails,
|
||||||
|
authConsentContent,
|
||||||
|
pageFrameContent
|
||||||
|
} = formData;
|
||||||
|
|
||||||
|
await updateServerConfig({
|
||||||
|
defaultAuthOrgId: defaultAuthOrgId || null,
|
||||||
|
allowSignUp: signUpMode !== SignUpModes.Disabled,
|
||||||
|
allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null,
|
||||||
|
trustSamlEmails,
|
||||||
|
trustLdapEmails,
|
||||||
|
trustOidcEmails,
|
||||||
|
authConsentContent,
|
||||||
|
pageFrameContent
|
||||||
|
});
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully changed sign up setting.",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
createNotification({
|
||||||
|
type: "error",
|
||||||
|
text: "Failed to update sign up setting."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
className="space-y-8 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
||||||
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col justify-start">
|
||||||
|
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Allow user signups</div>
|
||||||
|
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
||||||
|
Select if you want users to be able to signup freely into your Infisical instance.
|
||||||
|
</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="signUpMode"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl className="max-w-sm" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Select
|
||||||
|
className="w-full bg-mineshaft-700"
|
||||||
|
dropdownContainerClassName="bg-mineshaft-800"
|
||||||
|
defaultValue={field.value}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
{...field}
|
||||||
|
>
|
||||||
|
<SelectItem value={SignUpModes.Disabled}>Disabled</SelectItem>
|
||||||
|
<SelectItem value={SignUpModes.Anyone}>Anyone</SelectItem>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{signUpMode === "anyone" && (
|
||||||
|
<div className="flex flex-col justify-start">
|
||||||
|
<div className="mb-4 text-xl font-semibold text-mineshaft-100">
|
||||||
|
Restrict signup by email domain(s)
|
||||||
|
</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
defaultValue=""
|
||||||
|
name="allowedSignUpDomain"
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
label="Leave blank to allow any email domains"
|
||||||
|
className="w-72"
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
value={field.value || ""}
|
||||||
|
placeholder="gmail.com, aws.com, redhat.com"
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faAt} />}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-start">
|
||||||
|
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Default organization</div>
|
||||||
|
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
||||||
|
Select the default organization you want to set for SAML/LDAP/OIDC/Github logins. When
|
||||||
|
selected, user logins will be automatically scoped to the selected organization.
|
||||||
|
</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="defaultAuthOrgId"
|
||||||
|
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||||
|
<FormControl className="max-w-sm" errorText={error?.message} isError={Boolean(error)}>
|
||||||
|
<Select
|
||||||
|
placeholder="Allow all organizations"
|
||||||
|
className="w-full bg-mineshaft-700"
|
||||||
|
dropdownContainerClassName="bg-mineshaft-800"
|
||||||
|
defaultValue={field.value ?? " "}
|
||||||
|
onValueChange={(e) => onChange(e)}
|
||||||
|
{...field}
|
||||||
|
>
|
||||||
|
<SelectClear
|
||||||
|
selectValue={defaultAuthOrgId}
|
||||||
|
onClear={() => {
|
||||||
|
onChange("");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Allow all organizations
|
||||||
|
</SelectClear>
|
||||||
|
{organizations.data?.map((org) => (
|
||||||
|
<SelectItem key={org.id} value={org.id}>
|
||||||
|
{org.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-start">
|
||||||
|
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Trust emails</div>
|
||||||
|
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
||||||
|
Select if you want Infisical to trust external emails from SAML/LDAP/OIDC identity
|
||||||
|
providers. If set to false, then Infisical will prompt SAML/LDAP/OIDC provisioned users to
|
||||||
|
verify their email upon their first login.
|
||||||
|
</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="trustSamlEmails"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="trust-saml-emails"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-full">Trust SAML emails</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="trustLdapEmails"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="trust-ldap-emails"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-full">Trust LDAP emails</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="trustOidcEmails"
|
||||||
|
render={({ field, fieldState: { error } }) => {
|
||||||
|
return (
|
||||||
|
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
||||||
|
<Switch
|
||||||
|
id="trust-oidc-emails"
|
||||||
|
onCheckedChange={(value) => field.onChange(value)}
|
||||||
|
isChecked={field.value}
|
||||||
|
>
|
||||||
|
<p className="w-full">Trust OIDC emails</p>
|
||||||
|
</Switch>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col justify-start">
|
||||||
|
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Notices</div>
|
||||||
|
<div className="mb-4 max-w-lg text-sm text-mineshaft-400">
|
||||||
|
Configure system-wide notification banners and security messages. These settings control
|
||||||
|
the text displayed to users during authentication and throughout their session
|
||||||
|
</div>
|
||||||
|
<Controller
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Auth Consent Content"
|
||||||
|
tooltipText="Formatting supported: HTML, Markdown, Plain text"
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
placeholder="**Auth Consent Message**"
|
||||||
|
{...field}
|
||||||
|
rows={3}
|
||||||
|
className="thin-scrollbar h-48 max-w-lg !resize-none bg-mineshaft-800"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
control={control}
|
||||||
|
name="authConsentContent"
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
render={({ field, fieldState: { error } }) => (
|
||||||
|
<FormControl
|
||||||
|
isError={Boolean(error)}
|
||||||
|
errorText={error?.message}
|
||||||
|
label="Page Frame Content"
|
||||||
|
tooltipText="Formatting supported: HTML, Markdown, Plain text"
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
placeholder='<div style="background-color: red">TOP SECRET</div>'
|
||||||
|
{...field}
|
||||||
|
rows={3}
|
||||||
|
className="thin-scrollbar h-48 max-w-lg !resize-none bg-mineshaft-800"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
control={control}
|
||||||
|
name="pageFrameContent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button type="submit" isLoading={isSubmitting} isDisabled={isSubmitting || !isDirty}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { GeneralPageForm } from "./GeneralPageForm";
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { GeneralPage } from "./GeneralPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_authenticate/_inject-org-details/admin/_admin-layout/")({
|
||||||
|
component: GeneralPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "General",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { IntegrationsPageForm } from "./components";
|
||||||
|
|
||||||
|
export const IntegrationsPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader
|
||||||
|
title="Integrations"
|
||||||
|
description="Manage integrations for your Infisical instance."
|
||||||
|
/>
|
||||||
|
<IntegrationsPageForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+1
-1
@@ -3,7 +3,7 @@ import { useGetAdminIntegrationsConfig } from "@app/hooks/api";
|
|||||||
import { MicrosoftTeamsIntegrationForm } from "./MicrosoftTeamsIntegrationForm";
|
import { MicrosoftTeamsIntegrationForm } from "./MicrosoftTeamsIntegrationForm";
|
||||||
import { SlackIntegrationForm } from "./SlackIntegrationForm";
|
import { SlackIntegrationForm } from "./SlackIntegrationForm";
|
||||||
|
|
||||||
export const IntegrationPanel = () => {
|
export const IntegrationsPageForm = () => {
|
||||||
const { data: adminIntegrationsConfig } = useGetAdminIntegrationsConfig();
|
const { data: adminIntegrationsConfig } = useGetAdminIntegrationsConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { IntegrationsPageForm } from "./IntegrationsPageForm";
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { IntegrationsPage } from "./IntegrationsPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/integrations"
|
||||||
|
)({
|
||||||
|
component: IntegrationsPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Integrations",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin/integrations"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { MachineIdentitiesTable } from "./components";
|
||||||
|
|
||||||
|
export const MachineIdentitiesResourcesPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader
|
||||||
|
title="Machine Identities"
|
||||||
|
description="Manage all machine identities within your Infisical instance."
|
||||||
|
/>
|
||||||
|
<MachineIdentitiesTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+2
-5
@@ -120,7 +120,7 @@ const IdentityPanelTable = ({
|
|||||||
<Button
|
<Button
|
||||||
className="mt-4 py-3 text-sm"
|
className="mt-4 py-3 text-sm"
|
||||||
isFullWidth
|
isFullWidth
|
||||||
variant="star"
|
variant="outline_bg"
|
||||||
isLoading={isFetchingNextPage}
|
isLoading={isFetchingNextPage}
|
||||||
isDisabled={isFetchingNextPage || !hasNextPage}
|
isDisabled={isFetchingNextPage || !hasNextPage}
|
||||||
onClick={() => fetchNextPage()}
|
onClick={() => fetchNextPage()}
|
||||||
@@ -133,7 +133,7 @@ const IdentityPanelTable = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IdentityPanel = () => {
|
export const MachineIdentitiesTable = () => {
|
||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
"removeServerAdmin"
|
"removeServerAdmin"
|
||||||
] as const);
|
] as const);
|
||||||
@@ -161,9 +161,6 @@ export const IdentityPanel = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<div className="mb-4">
|
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">Identities</p>
|
|
||||||
</div>
|
|
||||||
<IdentityPanelTable handlePopUpOpen={handlePopUpOpen} />
|
<IdentityPanelTable handlePopUpOpen={handlePopUpOpen} />
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.removeServerAdmin.isOpen}
|
isOpen={popUp.removeServerAdmin.isOpen}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { MachineIdentitiesTable } from "./MachineIdentitiesTable";
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { MachineIdentitiesResourcesPage } from "./MachineIdentitiesResourcesPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/machine-identities"
|
||||||
|
)({
|
||||||
|
component: MachineIdentitiesResourcesPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Machine Identities",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin/resources/machine-identities"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { OrganizationsTable } from "./components";
|
||||||
|
|
||||||
|
export const OrganizationResourcesPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader
|
||||||
|
title="Organizations"
|
||||||
|
description="Manage all organizations within your Infisical instance."
|
||||||
|
/>
|
||||||
|
<OrganizationsTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,396 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
faBuilding,
|
||||||
|
faCircleQuestion,
|
||||||
|
faEllipsis,
|
||||||
|
faMagnifyingGlass
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { createNotification } from "@app/components/notifications";
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
DeleteActionModal,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
EmptyState,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
Table,
|
||||||
|
TableContainer,
|
||||||
|
TableSkeleton,
|
||||||
|
TBody,
|
||||||
|
Td,
|
||||||
|
Th,
|
||||||
|
THead,
|
||||||
|
Tooltip,
|
||||||
|
Tr
|
||||||
|
} from "@app/components/v2";
|
||||||
|
import { useDebounce, usePopUp } from "@app/hooks";
|
||||||
|
import {
|
||||||
|
useAdminDeleteOrganization,
|
||||||
|
useAdminDeleteOrganizationMembership,
|
||||||
|
useAdminDeleteUser,
|
||||||
|
useAdminGetOrganizations
|
||||||
|
} from "@app/hooks/api";
|
||||||
|
import { OrganizationWithProjects } from "@app/hooks/api/admin/types";
|
||||||
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
|
const ViewMembersModalContent = ({
|
||||||
|
popUp,
|
||||||
|
handlePopUpOpen
|
||||||
|
}: {
|
||||||
|
popUp: UsePopUpState<["viewMembers"]>;
|
||||||
|
handlePopUpOpen: (
|
||||||
|
popUpName: keyof UsePopUpState<["deleteOrganizationMembership", "deleteUser"]>,
|
||||||
|
data?: {
|
||||||
|
username?: string;
|
||||||
|
membershipId?: string;
|
||||||
|
userId?: string;
|
||||||
|
orgName?: string;
|
||||||
|
orgId?: string;
|
||||||
|
organization?: OrganizationWithProjects;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
}) => {
|
||||||
|
const organization = popUp.viewMembers?.data?.organization as OrganizationWithProjects;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-2">
|
||||||
|
{organization?.members?.map((member) => (
|
||||||
|
<div className="flex items-center justify-between gap-2 rounded-md bg-mineshaft-700 px-4 py-2">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p className="text-xs text-mineshaft-100">
|
||||||
|
<div>
|
||||||
|
{member.user.firstName ? (
|
||||||
|
<div>
|
||||||
|
{member.user.firstName} {member.user.lastName}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-mineshaft-400">Not set</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 opacity-80">
|
||||||
|
<div>{member.user.username || member.user.email}</div>
|
||||||
|
<Badge variant="primary">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className="capitalize">{member.role.replace("-", " ")}</span>
|
||||||
|
{Boolean(member.roleId) && (
|
||||||
|
<Tooltip content="This member has a custom role assigned.">
|
||||||
|
<FontAwesomeIcon icon={faCircleQuestion} className="text-xs" />
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faEllipsis}
|
||||||
|
className="cursor-pointer text-sm text-mineshaft-400 transition-all hover:text-primary-500"
|
||||||
|
/>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="p-1">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() =>
|
||||||
|
handlePopUpOpen("deleteOrganizationMembership", {
|
||||||
|
membershipId: member.membershipId,
|
||||||
|
orgId: organization.id,
|
||||||
|
username: member.user.username,
|
||||||
|
orgName: organization.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Remove From Organization
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handlePopUpOpen("deleteUser", { userId: member.user.id })}
|
||||||
|
>
|
||||||
|
Delete User
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ViewMembersModal = ({
|
||||||
|
isOpen,
|
||||||
|
onOpenChange,
|
||||||
|
popUp,
|
||||||
|
handlePopUpOpen
|
||||||
|
}: {
|
||||||
|
isOpen: boolean;
|
||||||
|
onOpenChange: (isOpen: boolean) => void;
|
||||||
|
popUp: UsePopUpState<["viewMembers", "deleteOrganizationMembership", "deleteUser"]>;
|
||||||
|
handlePopUpOpen: (
|
||||||
|
popUpName: keyof UsePopUpState<["deleteOrganizationMembership", "deleteUser"]>
|
||||||
|
) => void;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
|
<ModalContent
|
||||||
|
onOpenAutoFocus={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}}
|
||||||
|
title="Organization Members"
|
||||||
|
subTitle="View the members of the organization."
|
||||||
|
>
|
||||||
|
<ViewMembersModalContent popUp={popUp} handlePopUpOpen={handlePopUpOpen} />
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const OrganizationsPanelTable = ({
|
||||||
|
popUp,
|
||||||
|
handlePopUpOpen,
|
||||||
|
handlePopUpToggle
|
||||||
|
}: {
|
||||||
|
popUp: UsePopUpState<
|
||||||
|
["deleteOrganization", "viewMembers", "deleteOrganizationMembership", "deleteUser"]
|
||||||
|
>;
|
||||||
|
handlePopUpOpen: (
|
||||||
|
popUpName: keyof UsePopUpState<
|
||||||
|
["deleteOrganization", "viewMembers", "deleteOrganizationMembership", "deleteUser"]
|
||||||
|
>,
|
||||||
|
data?: {
|
||||||
|
orgName?: string;
|
||||||
|
orgId?: string;
|
||||||
|
message?: string;
|
||||||
|
organization?: OrganizationWithProjects;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
handlePopUpToggle: (
|
||||||
|
popUpName: keyof UsePopUpState<["deleteOrganization", "viewMembers"]>,
|
||||||
|
isOpen?: boolean
|
||||||
|
) => void;
|
||||||
|
}) => {
|
||||||
|
const [searchOrganizationsFilter, setSearchOrganizationsFilter] = useState("");
|
||||||
|
const [debouncedSearchTerm] = useDebounce(searchOrganizationsFilter, 500);
|
||||||
|
|
||||||
|
const { data, isPending, isFetchingNextPage, hasNextPage, fetchNextPage } =
|
||||||
|
useAdminGetOrganizations({
|
||||||
|
limit: 20,
|
||||||
|
searchTerm: debouncedSearchTerm
|
||||||
|
});
|
||||||
|
|
||||||
|
const isEmpty = !isPending && !data?.pages?.[0].length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Input
|
||||||
|
value={searchOrganizationsFilter}
|
||||||
|
onChange={(e) => setSearchOrganizationsFilter(e.target.value)}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||||
|
placeholder="Search organizations..."
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<TableContainer>
|
||||||
|
<Table>
|
||||||
|
<THead>
|
||||||
|
<Tr>
|
||||||
|
<Th className="w-5/12">Name</Th>
|
||||||
|
<Th className="w-5/12">Members</Th>
|
||||||
|
<Th className="w-5/12">Projects</Th>
|
||||||
|
<Th className="w-5" />
|
||||||
|
</Tr>
|
||||||
|
</THead>
|
||||||
|
<TBody>
|
||||||
|
{isPending && <TableSkeleton columns={4} innerKey="organizations" />}
|
||||||
|
{!isPending &&
|
||||||
|
data?.pages?.map((orgs) =>
|
||||||
|
orgs.map((org) => {
|
||||||
|
return (
|
||||||
|
<Tr key={`org-${org.id}`} className="w-full">
|
||||||
|
<Td className="w-5/12">
|
||||||
|
{org.name ? (
|
||||||
|
org.name
|
||||||
|
) : (
|
||||||
|
<span className="text-mineshaft-400">Not set</span>
|
||||||
|
)}
|
||||||
|
</Td>
|
||||||
|
<Td className="w-5/12">
|
||||||
|
{org.members.length} {org.members.length === 1 ? "member" : "members"}
|
||||||
|
<Button
|
||||||
|
variant="outline_bg"
|
||||||
|
size="xs"
|
||||||
|
className="ml-2"
|
||||||
|
onClick={() => handlePopUpOpen("viewMembers", { organization: org })}
|
||||||
|
>
|
||||||
|
View Members
|
||||||
|
</Button>
|
||||||
|
</Td>
|
||||||
|
<Td className="w-5/12">
|
||||||
|
{org.projects.length} {org.projects.length === 1 ? "project" : "projects"}
|
||||||
|
</Td>
|
||||||
|
<Td>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||||
|
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||||
|
<FontAwesomeIcon size="sm" icon={faEllipsis} />
|
||||||
|
</div>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="p-1">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handlePopUpOpen("deleteOrganization", {
|
||||||
|
orgId: org.id,
|
||||||
|
orgName: org.name
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Delete Organization
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
</Td>
|
||||||
|
</Tr>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</TBody>
|
||||||
|
</Table>
|
||||||
|
{!isPending && isEmpty && <EmptyState title="No organizations found" icon={faBuilding} />}
|
||||||
|
</TableContainer>
|
||||||
|
{!isEmpty && (
|
||||||
|
<Button
|
||||||
|
className="mt-4 py-3 text-sm"
|
||||||
|
isFullWidth
|
||||||
|
variant="outline_bg"
|
||||||
|
isLoading={isFetchingNextPage}
|
||||||
|
isDisabled={isFetchingNextPage || !hasNextPage}
|
||||||
|
onClick={() => fetchNextPage()}
|
||||||
|
>
|
||||||
|
{hasNextPage ? "Load More" : "End of list"}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ViewMembersModal
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
isOpen={popUp.viewMembers.isOpen}
|
||||||
|
onOpenChange={(isOpen) => handlePopUpToggle("viewMembers", isOpen)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OrganizationsTable = () => {
|
||||||
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
|
"deleteOrganization",
|
||||||
|
"deleteOrganizationMembership",
|
||||||
|
"deleteUser",
|
||||||
|
"viewMembers"
|
||||||
|
] as const);
|
||||||
|
|
||||||
|
const { mutateAsync: deleteOrganization } = useAdminDeleteOrganization();
|
||||||
|
const { mutateAsync: deleteOrganizationMembership } = useAdminDeleteOrganizationMembership();
|
||||||
|
const { mutateAsync: deleteUser } = useAdminDeleteUser();
|
||||||
|
|
||||||
|
const handleDeleteOrganization = async () => {
|
||||||
|
const { orgId } = popUp?.deleteOrganization?.data as { orgId: string };
|
||||||
|
|
||||||
|
await deleteOrganization(orgId);
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Successfully deleted organization"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpClose("deleteOrganization");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteOrganizationMembership = async () => {
|
||||||
|
const { orgId, membershipId } = popUp?.deleteOrganizationMembership?.data as {
|
||||||
|
orgId: string;
|
||||||
|
membershipId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!orgId || !membershipId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteOrganizationMembership({ organizationId: orgId, membershipId });
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Successfully removed user from organization"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpClose("viewMembers");
|
||||||
|
handlePopUpClose("deleteOrganizationMembership");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteUser = async () => {
|
||||||
|
const { userId } = popUp?.deleteUser?.data as { userId: string };
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteUser(userId);
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Successfully deleted user"
|
||||||
|
});
|
||||||
|
|
||||||
|
handlePopUpClose("viewMembers");
|
||||||
|
handlePopUpClose("deleteUser");
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
|
<OrganizationsPanelTable
|
||||||
|
popUp={popUp}
|
||||||
|
handlePopUpOpen={handlePopUpOpen}
|
||||||
|
handlePopUpToggle={handlePopUpToggle}
|
||||||
|
/>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteOrganization.isOpen}
|
||||||
|
deleteKey="delete"
|
||||||
|
title={`Are you sure you want to delete organization ${
|
||||||
|
(popUp?.deleteOrganization?.data as { orgName: string })?.orgName || ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteOrganization", isOpen)}
|
||||||
|
onDeleteApproved={handleDeleteOrganization}
|
||||||
|
/>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteOrganizationMembership.isOpen}
|
||||||
|
deleteKey="delete"
|
||||||
|
title={`Are you sure you want to remove ${
|
||||||
|
(popUp?.deleteOrganizationMembership?.data as { username: string })?.username || ""
|
||||||
|
} from organization ${
|
||||||
|
(popUp?.deleteOrganizationMembership?.data as { orgName: string })?.orgName || ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteOrganizationMembership", isOpen)}
|
||||||
|
onDeleteApproved={handleDeleteOrganizationMembership}
|
||||||
|
/>
|
||||||
|
<DeleteActionModal
|
||||||
|
isOpen={popUp.deleteUser.isOpen}
|
||||||
|
deleteKey="delete"
|
||||||
|
title={`Are you sure you want to delete user ${
|
||||||
|
(popUp?.deleteUser?.data as { username: string })?.username || ""
|
||||||
|
}?`}
|
||||||
|
onChange={(isOpen) => handlePopUpToggle("deleteUser", isOpen)}
|
||||||
|
onDeleteApproved={handleDeleteUser}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { OrganizationsTable } from "./OrganizationsTable";
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { OrganizationResourcesPage } from "./OrganizationResourcesPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/organizations"
|
||||||
|
)({
|
||||||
|
component: OrganizationResourcesPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Organizations",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin/resources/organizations"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,424 +0,0 @@
|
|||||||
import { Helmet } from "react-helmet";
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { faAt } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import { createNotification } from "@app/components/notifications";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
ContentLoader,
|
|
||||||
FormControl,
|
|
||||||
Input,
|
|
||||||
Select,
|
|
||||||
SelectClear,
|
|
||||||
SelectItem,
|
|
||||||
Switch,
|
|
||||||
Tab,
|
|
||||||
TabList,
|
|
||||||
TabPanel,
|
|
||||||
Tabs,
|
|
||||||
TextArea
|
|
||||||
} from "@app/components/v2";
|
|
||||||
import { useServerConfig, useUser } from "@app/context";
|
|
||||||
import {
|
|
||||||
useGetOrganizations,
|
|
||||||
useGetServerRootKmsEncryptionDetails,
|
|
||||||
useUpdateServerConfig
|
|
||||||
} from "@app/hooks/api";
|
|
||||||
import { IdentityPanel } from "@app/pages/admin/OverviewPage/components/IdentityPanel";
|
|
||||||
|
|
||||||
import { AuthPanel } from "./components/AuthPanel";
|
|
||||||
import { CachingPanel } from "./components/CachingPanel";
|
|
||||||
import { EncryptionPanel } from "./components/EncryptionPanel";
|
|
||||||
import { IntegrationPanel } from "./components/IntegrationPanel";
|
|
||||||
import { UserPanel } from "./components/UserPanel";
|
|
||||||
|
|
||||||
enum TabSections {
|
|
||||||
Settings = "settings",
|
|
||||||
Encryption = "encryption",
|
|
||||||
Auth = "auth",
|
|
||||||
Integrations = "integrations",
|
|
||||||
Users = "users",
|
|
||||||
Identities = "identities",
|
|
||||||
Kmip = "kmip",
|
|
||||||
Caching = "caching"
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SignUpModes {
|
|
||||||
Disabled = "disabled",
|
|
||||||
Anyone = "anyone"
|
|
||||||
}
|
|
||||||
|
|
||||||
const formSchema = z.object({
|
|
||||||
signUpMode: z.nativeEnum(SignUpModes),
|
|
||||||
allowedSignUpDomain: z.string().optional().nullable(),
|
|
||||||
trustSamlEmails: z.boolean(),
|
|
||||||
trustLdapEmails: z.boolean(),
|
|
||||||
trustOidcEmails: z.boolean(),
|
|
||||||
defaultAuthOrgId: z.string(),
|
|
||||||
authConsentContent: z.string().optional().default(""),
|
|
||||||
pageFrameContent: z.string().optional().default("")
|
|
||||||
});
|
|
||||||
|
|
||||||
type TDashboardForm = z.infer<typeof formSchema>;
|
|
||||||
|
|
||||||
export const OverviewPage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const data = useServerConfig();
|
|
||||||
const { data: serverRootKmsDetails } = useGetServerRootKmsEncryptionDetails();
|
|
||||||
const { config } = data;
|
|
||||||
|
|
||||||
const {
|
|
||||||
control,
|
|
||||||
handleSubmit,
|
|
||||||
watch,
|
|
||||||
formState: { isSubmitting, isDirty }
|
|
||||||
} = useForm<TDashboardForm>({
|
|
||||||
resolver: zodResolver(formSchema),
|
|
||||||
values: {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
signUpMode: config.allowSignUp ? SignUpModes.Anyone : SignUpModes.Disabled,
|
|
||||||
allowedSignUpDomain: config.allowedSignUpDomain,
|
|
||||||
trustSamlEmails: config.trustSamlEmails,
|
|
||||||
trustLdapEmails: config.trustLdapEmails,
|
|
||||||
trustOidcEmails: config.trustOidcEmails,
|
|
||||||
defaultAuthOrgId: config.defaultAuthOrgId ?? "",
|
|
||||||
authConsentContent: config.authConsentContent ?? "",
|
|
||||||
pageFrameContent: config.pageFrameContent ?? ""
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const signUpMode = watch("signUpMode");
|
|
||||||
const defaultAuthOrgId = watch("defaultAuthOrgId");
|
|
||||||
|
|
||||||
const { user } = useUser();
|
|
||||||
const { mutateAsync: updateServerConfig } = useUpdateServerConfig();
|
|
||||||
|
|
||||||
const organizations = useGetOrganizations();
|
|
||||||
|
|
||||||
const isNotAllowed = !user?.superAdmin;
|
|
||||||
const onFormSubmit = async (formData: TDashboardForm) => {
|
|
||||||
try {
|
|
||||||
const {
|
|
||||||
allowedSignUpDomain,
|
|
||||||
trustSamlEmails,
|
|
||||||
trustLdapEmails,
|
|
||||||
trustOidcEmails,
|
|
||||||
authConsentContent,
|
|
||||||
pageFrameContent
|
|
||||||
} = formData;
|
|
||||||
|
|
||||||
await updateServerConfig({
|
|
||||||
defaultAuthOrgId: defaultAuthOrgId || null,
|
|
||||||
allowSignUp: signUpMode !== SignUpModes.Disabled,
|
|
||||||
allowedSignUpDomain: signUpMode === SignUpModes.Anyone ? allowedSignUpDomain : null,
|
|
||||||
trustSamlEmails,
|
|
||||||
trustLdapEmails,
|
|
||||||
trustOidcEmails,
|
|
||||||
authConsentContent,
|
|
||||||
pageFrameContent
|
|
||||||
});
|
|
||||||
createNotification({
|
|
||||||
text: "Successfully changed sign up setting.",
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
createNotification({
|
|
||||||
type: "error",
|
|
||||||
text: "Failed to update sign up setting."
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Helmet>
|
|
||||||
<title>{t("common.head-title", { title: t("admin.dashboard") })}</title>
|
|
||||||
<link rel="icon" href="/infisical.ico" />
|
|
||||||
<meta property="og:image" content="/images/message.png" />
|
|
||||||
<meta property="og:title" content={t("admin.dashboard.og-title") ?? ""} />
|
|
||||||
<meta name="og:description" content={t("admin.dashboard.og-description") ?? ""} />
|
|
||||||
</Helmet>
|
|
||||||
<div className="h-full">
|
|
||||||
<div className="container mx-auto max-w-7xl px-4 pb-12 text-white dark:[color-scheme:dark]">
|
|
||||||
<div className="mx-auto mb-6 w-full max-w-7xl pt-6">
|
|
||||||
<div className="mb-8 flex flex-col items-start justify-between text-xl">
|
|
||||||
<h1 className="text-3xl font-semibold">Server Admin Console</h1>
|
|
||||||
<p className="text-base text-bunker-300">
|
|
||||||
Manage your instance level configurations.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isNotAllowed ? (
|
|
||||||
<ContentLoader text={isNotAllowed ? "Redirecting to org page..." : undefined} />
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<Tabs defaultValue={TabSections.Settings}>
|
|
||||||
<TabList>
|
|
||||||
<div className="flex w-full flex-row border-b border-mineshaft-600">
|
|
||||||
<Tab value={TabSections.Settings}>General</Tab>
|
|
||||||
<Tab value={TabSections.Encryption}>Encryption</Tab>
|
|
||||||
<Tab value={TabSections.Auth}>Authentication</Tab>
|
|
||||||
<Tab value={TabSections.Integrations}>Integrations</Tab>
|
|
||||||
<Tab value={TabSections.Users}>User Identities</Tab>
|
|
||||||
<Tab value={TabSections.Identities}>Machine Identities</Tab>
|
|
||||||
<Tab value={TabSections.Caching}>Caching</Tab>
|
|
||||||
</div>
|
|
||||||
</TabList>
|
|
||||||
<TabPanel value={TabSections.Settings}>
|
|
||||||
<form
|
|
||||||
className="mb-6 space-y-8 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"
|
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
|
|
||||||
Allow user signups
|
|
||||||
</div>
|
|
||||||
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
|
||||||
Select if you want users to be able to signup freely into your Infisical
|
|
||||||
instance.
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="signUpMode"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
className="max-w-sm"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
className="w-full bg-mineshaft-700"
|
|
||||||
dropdownContainerClassName="bg-mineshaft-800"
|
|
||||||
defaultValue={field.value}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
{...field}
|
|
||||||
>
|
|
||||||
<SelectItem value={SignUpModes.Disabled}>Disabled</SelectItem>
|
|
||||||
<SelectItem value={SignUpModes.Anyone}>Anyone</SelectItem>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{signUpMode === "anyone" && (
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-4 text-xl font-semibold text-mineshaft-100">
|
|
||||||
Restrict signup by email domain(s)
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
defaultValue=""
|
|
||||||
name="allowedSignUpDomain"
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
label="Leave blank to allow any email domains"
|
|
||||||
className="w-72"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
{...field}
|
|
||||||
value={field.value || ""}
|
|
||||||
placeholder="gmail.com, aws.com, redhat.com"
|
|
||||||
leftIcon={<FontAwesomeIcon icon={faAt} />}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
|
|
||||||
Default organization
|
|
||||||
</div>
|
|
||||||
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
|
||||||
Select the default organization you want to set for SAML/LDAP/OIDC/Github
|
|
||||||
logins. When selected, user logins will be automatically scoped to the
|
|
||||||
selected organization.
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="defaultAuthOrgId"
|
|
||||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
className="max-w-sm"
|
|
||||||
errorText={error?.message}
|
|
||||||
isError={Boolean(error)}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
placeholder="Allow all organizations"
|
|
||||||
className="w-full bg-mineshaft-700"
|
|
||||||
dropdownContainerClassName="bg-mineshaft-800"
|
|
||||||
defaultValue={field.value ?? " "}
|
|
||||||
onValueChange={(e) => onChange(e)}
|
|
||||||
{...field}
|
|
||||||
>
|
|
||||||
<SelectClear
|
|
||||||
selectValue={defaultAuthOrgId}
|
|
||||||
onClear={() => {
|
|
||||||
onChange("");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Allow all organizations
|
|
||||||
</SelectClear>
|
|
||||||
{organizations.data?.map((org) => (
|
|
||||||
<SelectItem key={org.id} value={org.id}>
|
|
||||||
{org.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">
|
|
||||||
Trust emails
|
|
||||||
</div>
|
|
||||||
<div className="mb-4 max-w-sm text-sm text-mineshaft-400">
|
|
||||||
Select if you want Infisical to trust external emails from SAML/LDAP/OIDC
|
|
||||||
identity providers. If set to false, then Infisical will prompt
|
|
||||||
SAML/LDAP/OIDC provisioned users to verify their email upon their first
|
|
||||||
login.
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="trustSamlEmails"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Switch
|
|
||||||
id="trust-saml-emails"
|
|
||||||
onCheckedChange={(value) => field.onChange(value)}
|
|
||||||
isChecked={field.value}
|
|
||||||
>
|
|
||||||
<p className="w-full">Trust SAML emails</p>
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="trustLdapEmails"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Switch
|
|
||||||
id="trust-ldap-emails"
|
|
||||||
onCheckedChange={(value) => field.onChange(value)}
|
|
||||||
isChecked={field.value}
|
|
||||||
>
|
|
||||||
<p className="w-full">Trust LDAP emails</p>
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="trustOidcEmails"
|
|
||||||
render={({ field, fieldState: { error } }) => {
|
|
||||||
return (
|
|
||||||
<FormControl isError={Boolean(error)} errorText={error?.message}>
|
|
||||||
<Switch
|
|
||||||
id="trust-oidc-emails"
|
|
||||||
onCheckedChange={(value) => field.onChange(value)}
|
|
||||||
isChecked={field.value}
|
|
||||||
>
|
|
||||||
<p className="w-full">Trust OIDC emails</p>
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col justify-start">
|
|
||||||
<div className="mb-2 text-xl font-semibold text-mineshaft-100">Notices</div>
|
|
||||||
<div className="mb-4 max-w-lg text-sm text-mineshaft-400">
|
|
||||||
Configure system-wide notification banners and security messages. These
|
|
||||||
settings control the text displayed to users during authentication and
|
|
||||||
throughout their session
|
|
||||||
</div>
|
|
||||||
<Controller
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
label="Auth Consent Content"
|
|
||||||
tooltipText="Formatting supported: HTML, Markdown, Plain text"
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
placeholder="**Auth Consent Message**"
|
|
||||||
{...field}
|
|
||||||
rows={3}
|
|
||||||
className="thin-scrollbar h-48 max-w-lg !resize-none bg-mineshaft-800"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
control={control}
|
|
||||||
name="authConsentContent"
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
render={({ field, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
label="Page Frame Content"
|
|
||||||
tooltipText="Formatting supported: HTML, Markdown, Plain text"
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
placeholder='<div style="background-color: red">TOP SECRET</div>'
|
|
||||||
{...field}
|
|
||||||
rows={3}
|
|
||||||
className="thin-scrollbar h-48 max-w-lg !resize-none bg-mineshaft-800"
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
control={control}
|
|
||||||
name="pageFrameContent"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
isLoading={isSubmitting}
|
|
||||||
isDisabled={isSubmitting || !isDirty}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={TabSections.Encryption}>
|
|
||||||
<EncryptionPanel rootKmsDetails={serverRootKmsDetails} />
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={TabSections.Auth}>
|
|
||||||
<AuthPanel />
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={TabSections.Integrations}>
|
|
||||||
<IntegrationPanel />
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={TabSections.Users}>
|
|
||||||
<UserPanel />
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={TabSections.Identities}>
|
|
||||||
<IdentityPanel />
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={TabSections.Caching}>
|
|
||||||
<CachingPanel />
|
|
||||||
</TabPanel>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
import { OverviewPage } from "./OverviewPage";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authenticate/_inject-org-details/admin/_admin-layout/")({
|
|
||||||
component: OverviewPage
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Helmet } from "react-helmet";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PageHeader } from "@app/components/v2";
|
||||||
|
|
||||||
|
import { UserIdentitiesTable } from "./components";
|
||||||
|
|
||||||
|
export const UserIdentitiesResourcesPage = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full bg-bunker-800">
|
||||||
|
<Helmet>
|
||||||
|
<title>{t("common.head-title", { title: "Admin" })}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
|
<PageHeader
|
||||||
|
title="User Identities"
|
||||||
|
description="Manage all user identities within your Infisical instance."
|
||||||
|
/>
|
||||||
|
<UserIdentitiesTable />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+2
-5
@@ -213,7 +213,7 @@ const UserPanelTable = ({
|
|||||||
<Button
|
<Button
|
||||||
className="mt-4 py-3 text-sm"
|
className="mt-4 py-3 text-sm"
|
||||||
isFullWidth
|
isFullWidth
|
||||||
variant="star"
|
variant="outline_bg"
|
||||||
isLoading={isFetchingNextPage}
|
isLoading={isFetchingNextPage}
|
||||||
isDisabled={isFetchingNextPage || !hasNextPage}
|
isDisabled={isFetchingNextPage || !hasNextPage}
|
||||||
onClick={() => fetchNextPage()}
|
onClick={() => fetchNextPage()}
|
||||||
@@ -226,7 +226,7 @@ const UserPanelTable = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UserPanel = () => {
|
export const UserIdentitiesTable = () => {
|
||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
"removeUser",
|
"removeUser",
|
||||||
"upgradePlan",
|
"upgradePlan",
|
||||||
@@ -297,9 +297,6 @@ export const UserPanel = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<div className="mb-4">
|
|
||||||
<p className="text-xl font-semibold text-mineshaft-100">Users</p>
|
|
||||||
</div>
|
|
||||||
<UserPanelTable handlePopUpOpen={handlePopUpOpen} />
|
<UserPanelTable handlePopUpOpen={handlePopUpOpen} />
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.removeUser.isOpen}
|
isOpen={popUp.removeUser.isOpen}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { UserIdentitiesTable } from "./UserIdentitiesTable";
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
import { UserIdentitiesResourcesPage } from "./UserIdentitiesResourcesPage";
|
||||||
|
|
||||||
|
export const Route = createFileRoute(
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/user-identities"
|
||||||
|
)({
|
||||||
|
component: UserIdentitiesResourcesPage,
|
||||||
|
beforeLoad: async () => {
|
||||||
|
return {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
label: "Admin",
|
||||||
|
link: linkOptions({ to: "/admin" })
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "User Identities",
|
||||||
|
link: linkOptions({
|
||||||
|
to: "/admin/resources/user-identities"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -18,7 +18,7 @@ export const OverviewPage = () => {
|
|||||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="OverviewPage"
|
title="Overview Page"
|
||||||
description="Manage keys and perform cryptographic operations."
|
description="Manage keys and perform cryptographic operations."
|
||||||
/>
|
/>
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
|
|||||||
+208
-10
@@ -42,17 +42,24 @@ import { Route as adminLayoutImport } from './pages/admin/layout'
|
|||||||
import { Route as authProviderSuccessPageRouteImport } from './pages/auth/ProviderSuccessPage/route'
|
import { Route as authProviderSuccessPageRouteImport } from './pages/auth/ProviderSuccessPage/route'
|
||||||
import { Route as authProviderErrorPageRouteImport } from './pages/auth/ProviderErrorPage/route'
|
import { Route as authProviderErrorPageRouteImport } from './pages/auth/ProviderErrorPage/route'
|
||||||
import { Route as userPersonalSettingsPageRouteImport } from './pages/user/PersonalSettingsPage/route'
|
import { Route as userPersonalSettingsPageRouteImport } from './pages/user/PersonalSettingsPage/route'
|
||||||
|
import { Route as adminIntegrationsPageRouteImport } from './pages/admin/IntegrationsPage/route'
|
||||||
|
import { Route as adminEncryptionPageRouteImport } from './pages/admin/EncryptionPage/route'
|
||||||
|
import { Route as adminCachingPageRouteImport } from './pages/admin/CachingPage/route'
|
||||||
|
import { Route as adminAuthenticationPageRouteImport } from './pages/admin/AuthenticationPage/route'
|
||||||
import { Route as organizationSsoPageRouteImport } from './pages/organization/SsoPage/route'
|
import { Route as organizationSsoPageRouteImport } from './pages/organization/SsoPage/route'
|
||||||
import { Route as organizationSecretScanningPageRouteImport } from './pages/organization/SecretScanningPage/route'
|
import { Route as organizationSecretScanningPageRouteImport } from './pages/organization/SecretScanningPage/route'
|
||||||
import { Route as organizationBillingPageRouteImport } from './pages/organization/BillingPage/route'
|
import { Route as organizationBillingPageRouteImport } from './pages/organization/BillingPage/route'
|
||||||
import { Route as organizationAuditLogsPageRouteImport } from './pages/organization/AuditLogsPage/route'
|
import { Route as organizationAuditLogsPageRouteImport } from './pages/organization/AuditLogsPage/route'
|
||||||
import { Route as organizationAdminPageRouteImport } from './pages/organization/AdminPage/route'
|
import { Route as organizationAdminPageRouteImport } from './pages/organization/AdminPage/route'
|
||||||
import { Route as organizationAccessManagementPageRouteImport } from './pages/organization/AccessManagementPage/route'
|
import { Route as organizationAccessManagementPageRouteImport } from './pages/organization/AccessManagementPage/route'
|
||||||
import { Route as adminOverviewPageRouteImport } from './pages/admin/OverviewPage/route'
|
import { Route as adminGeneralPageRouteImport } from './pages/admin/GeneralPage/route'
|
||||||
import { Route as sshLayoutImport } from './pages/ssh/layout'
|
import { Route as sshLayoutImport } from './pages/ssh/layout'
|
||||||
import { Route as secretManagerLayoutImport } from './pages/secret-manager/layout'
|
import { Route as secretManagerLayoutImport } from './pages/secret-manager/layout'
|
||||||
import { Route as kmsLayoutImport } from './pages/kms/layout'
|
import { Route as kmsLayoutImport } from './pages/kms/layout'
|
||||||
import { Route as certManagerLayoutImport } from './pages/cert-manager/layout'
|
import { Route as certManagerLayoutImport } from './pages/cert-manager/layout'
|
||||||
|
import { Route as adminUserIdentitiesResourcesPageRouteImport } from './pages/admin/UserIdentitiesResourcesPage/route'
|
||||||
|
import { Route as adminOrganizationResourcesPageRouteImport } from './pages/admin/OrganizationResourcesPage/route'
|
||||||
|
import { Route as adminMachineIdentitiesResourcesPageRouteImport } from './pages/admin/MachineIdentitiesResourcesPage/route'
|
||||||
import { Route as organizationSshSettingsPageRouteImport } from './pages/organization/SshSettingsPage/route'
|
import { Route as organizationSshSettingsPageRouteImport } from './pages/organization/SshSettingsPage/route'
|
||||||
import { Route as organizationSshOverviewPageRouteImport } from './pages/organization/SshOverviewPage/route'
|
import { Route as organizationSshOverviewPageRouteImport } from './pages/organization/SshOverviewPage/route'
|
||||||
import { Route as organizationSecretSharingSettingsPageRouteImport } from './pages/organization/SecretSharingSettingsPage/route'
|
import { Route as organizationSecretSharingSettingsPageRouteImport } from './pages/organization/SecretSharingSettingsPage/route'
|
||||||
@@ -551,6 +558,33 @@ const AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdRoute =
|
|||||||
getParentRoute: () => organizationLayoutRoute,
|
getParentRoute: () => organizationLayoutRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
|
const adminIntegrationsPageRouteRoute = adminIntegrationsPageRouteImport.update(
|
||||||
|
{
|
||||||
|
id: '/integrations',
|
||||||
|
path: '/integrations',
|
||||||
|
getParentRoute: () => adminLayoutRoute,
|
||||||
|
} as any,
|
||||||
|
)
|
||||||
|
|
||||||
|
const adminEncryptionPageRouteRoute = adminEncryptionPageRouteImport.update({
|
||||||
|
id: '/encryption',
|
||||||
|
path: '/encryption',
|
||||||
|
getParentRoute: () => adminLayoutRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
const adminCachingPageRouteRoute = adminCachingPageRouteImport.update({
|
||||||
|
id: '/caching',
|
||||||
|
path: '/caching',
|
||||||
|
getParentRoute: () => adminLayoutRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
const adminAuthenticationPageRouteRoute =
|
||||||
|
adminAuthenticationPageRouteImport.update({
|
||||||
|
id: '/authentication',
|
||||||
|
path: '/authentication',
|
||||||
|
getParentRoute: () => adminLayoutRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const organizationSsoPageRouteRoute = organizationSsoPageRouteImport.update({
|
const organizationSsoPageRouteRoute = organizationSsoPageRouteImport.update({
|
||||||
id: '/sso',
|
id: '/sso',
|
||||||
path: '/sso',
|
path: '/sso',
|
||||||
@@ -598,7 +632,7 @@ const organizationAccessManagementPageRouteRoute =
|
|||||||
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
|
AuthenticateInjectOrgDetailsOrgLayoutOrganizationRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
const adminOverviewPageRouteRoute = adminOverviewPageRouteImport.update({
|
const adminGeneralPageRouteRoute = adminGeneralPageRouteImport.update({
|
||||||
id: '/',
|
id: '/',
|
||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => adminLayoutRoute,
|
getParentRoute: () => adminLayoutRoute,
|
||||||
@@ -626,6 +660,27 @@ const certManagerLayoutRoute = certManagerLayoutImport.update({
|
|||||||
AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdRoute,
|
AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
|
const adminUserIdentitiesResourcesPageRouteRoute =
|
||||||
|
adminUserIdentitiesResourcesPageRouteImport.update({
|
||||||
|
id: '/resources/user-identities',
|
||||||
|
path: '/resources/user-identities',
|
||||||
|
getParentRoute: () => adminLayoutRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
const adminOrganizationResourcesPageRouteRoute =
|
||||||
|
adminOrganizationResourcesPageRouteImport.update({
|
||||||
|
id: '/resources/organizations',
|
||||||
|
path: '/resources/organizations',
|
||||||
|
getParentRoute: () => adminLayoutRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
|
const adminMachineIdentitiesResourcesPageRouteRoute =
|
||||||
|
adminMachineIdentitiesResourcesPageRouteImport.update({
|
||||||
|
id: '/resources/machine-identities',
|
||||||
|
path: '/resources/machine-identities',
|
||||||
|
getParentRoute: () => adminLayoutRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const organizationSshSettingsPageRouteRoute =
|
const organizationSshSettingsPageRouteRoute =
|
||||||
organizationSshSettingsPageRouteImport.update({
|
organizationSshSettingsPageRouteImport.update({
|
||||||
id: '/ssh/settings',
|
id: '/ssh/settings',
|
||||||
@@ -2039,7 +2094,7 @@ declare module '@tanstack/react-router' {
|
|||||||
id: '/_authenticate/_inject-org-details/admin/_admin-layout/'
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/'
|
||||||
path: '/'
|
path: '/'
|
||||||
fullPath: '/admin/'
|
fullPath: '/admin/'
|
||||||
preLoaderRoute: typeof adminOverviewPageRouteImport
|
preLoaderRoute: typeof adminGeneralPageRouteImport
|
||||||
parentRoute: typeof adminLayoutImport
|
parentRoute: typeof adminLayoutImport
|
||||||
}
|
}
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/access-management': {
|
'/_authenticate/_inject-org-details/_org-layout/organization/access-management': {
|
||||||
@@ -2084,6 +2139,34 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof organizationSsoPageRouteImport
|
preLoaderRoute: typeof organizationSsoPageRouteImport
|
||||||
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
|
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
|
||||||
}
|
}
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/authentication': {
|
||||||
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/authentication'
|
||||||
|
path: '/authentication'
|
||||||
|
fullPath: '/admin/authentication'
|
||||||
|
preLoaderRoute: typeof adminAuthenticationPageRouteImport
|
||||||
|
parentRoute: typeof adminLayoutImport
|
||||||
|
}
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/caching': {
|
||||||
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/caching'
|
||||||
|
path: '/caching'
|
||||||
|
fullPath: '/admin/caching'
|
||||||
|
preLoaderRoute: typeof adminCachingPageRouteImport
|
||||||
|
parentRoute: typeof adminLayoutImport
|
||||||
|
}
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/encryption': {
|
||||||
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/encryption'
|
||||||
|
path: '/encryption'
|
||||||
|
fullPath: '/admin/encryption'
|
||||||
|
preLoaderRoute: typeof adminEncryptionPageRouteImport
|
||||||
|
parentRoute: typeof adminLayoutImport
|
||||||
|
}
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/integrations': {
|
||||||
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/integrations'
|
||||||
|
path: '/integrations'
|
||||||
|
fullPath: '/admin/integrations'
|
||||||
|
preLoaderRoute: typeof adminIntegrationsPageRouteImport
|
||||||
|
parentRoute: typeof adminLayoutImport
|
||||||
|
}
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId': {
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId': {
|
||||||
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId'
|
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId'
|
||||||
path: '/cert-manager/$projectId'
|
path: '/cert-manager/$projectId'
|
||||||
@@ -2259,6 +2342,27 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof organizationSshSettingsPageRouteImport
|
preLoaderRoute: typeof organizationSshSettingsPageRouteImport
|
||||||
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
|
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationImport
|
||||||
}
|
}
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/resources/machine-identities': {
|
||||||
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/resources/machine-identities'
|
||||||
|
path: '/resources/machine-identities'
|
||||||
|
fullPath: '/admin/resources/machine-identities'
|
||||||
|
preLoaderRoute: typeof adminMachineIdentitiesResourcesPageRouteImport
|
||||||
|
parentRoute: typeof adminLayoutImport
|
||||||
|
}
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/resources/organizations': {
|
||||||
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/resources/organizations'
|
||||||
|
path: '/resources/organizations'
|
||||||
|
fullPath: '/admin/resources/organizations'
|
||||||
|
preLoaderRoute: typeof adminOrganizationResourcesPageRouteImport
|
||||||
|
parentRoute: typeof adminLayoutImport
|
||||||
|
}
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/resources/user-identities': {
|
||||||
|
id: '/_authenticate/_inject-org-details/admin/_admin-layout/resources/user-identities'
|
||||||
|
path: '/resources/user-identities'
|
||||||
|
fullPath: '/admin/resources/user-identities'
|
||||||
|
preLoaderRoute: typeof adminUserIdentitiesResourcesPageRouteImport
|
||||||
|
parentRoute: typeof adminLayoutImport
|
||||||
|
}
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout': {
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout': {
|
||||||
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout'
|
id: '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout'
|
||||||
path: ''
|
path: ''
|
||||||
@@ -3902,11 +4006,28 @@ const organizationLayoutRouteWithChildren =
|
|||||||
organizationLayoutRoute._addFileChildren(organizationLayoutRouteChildren)
|
organizationLayoutRoute._addFileChildren(organizationLayoutRouteChildren)
|
||||||
|
|
||||||
interface adminLayoutRouteChildren {
|
interface adminLayoutRouteChildren {
|
||||||
adminOverviewPageRouteRoute: typeof adminOverviewPageRouteRoute
|
adminGeneralPageRouteRoute: typeof adminGeneralPageRouteRoute
|
||||||
|
adminAuthenticationPageRouteRoute: typeof adminAuthenticationPageRouteRoute
|
||||||
|
adminCachingPageRouteRoute: typeof adminCachingPageRouteRoute
|
||||||
|
adminEncryptionPageRouteRoute: typeof adminEncryptionPageRouteRoute
|
||||||
|
adminIntegrationsPageRouteRoute: typeof adminIntegrationsPageRouteRoute
|
||||||
|
adminMachineIdentitiesResourcesPageRouteRoute: typeof adminMachineIdentitiesResourcesPageRouteRoute
|
||||||
|
adminOrganizationResourcesPageRouteRoute: typeof adminOrganizationResourcesPageRouteRoute
|
||||||
|
adminUserIdentitiesResourcesPageRouteRoute: typeof adminUserIdentitiesResourcesPageRouteRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminLayoutRouteChildren: adminLayoutRouteChildren = {
|
const adminLayoutRouteChildren: adminLayoutRouteChildren = {
|
||||||
adminOverviewPageRouteRoute: adminOverviewPageRouteRoute,
|
adminGeneralPageRouteRoute: adminGeneralPageRouteRoute,
|
||||||
|
adminAuthenticationPageRouteRoute: adminAuthenticationPageRouteRoute,
|
||||||
|
adminCachingPageRouteRoute: adminCachingPageRouteRoute,
|
||||||
|
adminEncryptionPageRouteRoute: adminEncryptionPageRouteRoute,
|
||||||
|
adminIntegrationsPageRouteRoute: adminIntegrationsPageRouteRoute,
|
||||||
|
adminMachineIdentitiesResourcesPageRouteRoute:
|
||||||
|
adminMachineIdentitiesResourcesPageRouteRoute,
|
||||||
|
adminOrganizationResourcesPageRouteRoute:
|
||||||
|
adminOrganizationResourcesPageRouteRoute,
|
||||||
|
adminUserIdentitiesResourcesPageRouteRoute:
|
||||||
|
adminUserIdentitiesResourcesPageRouteRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminLayoutRouteWithChildren = adminLayoutRoute._addFileChildren(
|
const adminLayoutRouteWithChildren = adminLayoutRoute._addFileChildren(
|
||||||
@@ -4093,13 +4214,17 @@ export interface FileRoutesByFullPath {
|
|||||||
'/login/provider/success': typeof authProviderSuccessPageRouteRoute
|
'/login/provider/success': typeof authProviderSuccessPageRouteRoute
|
||||||
'/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutIntegrationsRouteWithChildren
|
'/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutIntegrationsRouteWithChildren
|
||||||
'/organization': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteWithChildren
|
'/organization': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteWithChildren
|
||||||
'/admin/': typeof adminOverviewPageRouteRoute
|
'/admin/': typeof adminGeneralPageRouteRoute
|
||||||
'/organization/access-management': typeof organizationAccessManagementPageRouteRoute
|
'/organization/access-management': typeof organizationAccessManagementPageRouteRoute
|
||||||
'/organization/admin': typeof organizationAdminPageRouteRoute
|
'/organization/admin': typeof organizationAdminPageRouteRoute
|
||||||
'/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
|
'/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
|
||||||
'/organization/billing': typeof organizationBillingPageRouteRoute
|
'/organization/billing': typeof organizationBillingPageRouteRoute
|
||||||
'/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute
|
'/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute
|
||||||
'/organization/sso': typeof organizationSsoPageRouteRoute
|
'/organization/sso': typeof organizationSsoPageRouteRoute
|
||||||
|
'/admin/authentication': typeof adminAuthenticationPageRouteRoute
|
||||||
|
'/admin/caching': typeof adminCachingPageRouteRoute
|
||||||
|
'/admin/encryption': typeof adminEncryptionPageRouteRoute
|
||||||
|
'/admin/integrations': typeof adminIntegrationsPageRouteRoute
|
||||||
'/cert-manager/$projectId': typeof certManagerLayoutRouteWithChildren
|
'/cert-manager/$projectId': typeof certManagerLayoutRouteWithChildren
|
||||||
'/kms/$projectId': typeof kmsLayoutRouteWithChildren
|
'/kms/$projectId': typeof kmsLayoutRouteWithChildren
|
||||||
'/organization/app-connections': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren
|
'/organization/app-connections': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren
|
||||||
@@ -4125,6 +4250,9 @@ export interface FileRoutesByFullPath {
|
|||||||
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
|
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
|
||||||
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
|
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
|
||||||
'/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
|
'/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
|
||||||
|
'/admin/resources/machine-identities': typeof adminMachineIdentitiesResourcesPageRouteRoute
|
||||||
|
'/admin/resources/organizations': typeof adminOrganizationResourcesPageRouteRoute
|
||||||
|
'/admin/resources/user-identities': typeof adminUserIdentitiesResourcesPageRouteRoute
|
||||||
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
|
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
|
||||||
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
|
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
|
||||||
'/cert-manager/$projectId/certificates': typeof certManagerCertificatesPageRouteRoute
|
'/cert-manager/$projectId/certificates': typeof certManagerCertificatesPageRouteRoute
|
||||||
@@ -4282,7 +4410,7 @@ export interface FileRoutesByTo {
|
|||||||
'/signup/sso': typeof authSignUpSsoPageRouteRoute
|
'/signup/sso': typeof authSignUpSsoPageRouteRoute
|
||||||
'/secret-request/secret/$secretRequestId': typeof publicViewSecretRequestByIDPageRouteRoute
|
'/secret-request/secret/$secretRequestId': typeof publicViewSecretRequestByIDPageRouteRoute
|
||||||
'/shared/secret/$secretId': typeof publicViewSharedSecretByIDPageRouteRoute
|
'/shared/secret/$secretId': typeof publicViewSharedSecretByIDPageRouteRoute
|
||||||
'/admin': typeof adminOverviewPageRouteRoute
|
'/admin': typeof adminGeneralPageRouteRoute
|
||||||
'/login/provider/error': typeof authProviderErrorPageRouteRoute
|
'/login/provider/error': typeof authProviderErrorPageRouteRoute
|
||||||
'/login/provider/success': typeof authProviderSuccessPageRouteRoute
|
'/login/provider/success': typeof authProviderSuccessPageRouteRoute
|
||||||
'/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutIntegrationsRouteWithChildren
|
'/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutIntegrationsRouteWithChildren
|
||||||
@@ -4293,6 +4421,10 @@ export interface FileRoutesByTo {
|
|||||||
'/organization/billing': typeof organizationBillingPageRouteRoute
|
'/organization/billing': typeof organizationBillingPageRouteRoute
|
||||||
'/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute
|
'/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute
|
||||||
'/organization/sso': typeof organizationSsoPageRouteRoute
|
'/organization/sso': typeof organizationSsoPageRouteRoute
|
||||||
|
'/admin/authentication': typeof adminAuthenticationPageRouteRoute
|
||||||
|
'/admin/caching': typeof adminCachingPageRouteRoute
|
||||||
|
'/admin/encryption': typeof adminEncryptionPageRouteRoute
|
||||||
|
'/admin/integrations': typeof adminIntegrationsPageRouteRoute
|
||||||
'/cert-manager/$projectId': typeof certManagerLayoutRouteWithChildren
|
'/cert-manager/$projectId': typeof certManagerLayoutRouteWithChildren
|
||||||
'/kms/$projectId': typeof kmsLayoutRouteWithChildren
|
'/kms/$projectId': typeof kmsLayoutRouteWithChildren
|
||||||
'/secret-manager/$projectId': typeof secretManagerLayoutRouteWithChildren
|
'/secret-manager/$projectId': typeof secretManagerLayoutRouteWithChildren
|
||||||
@@ -4314,6 +4446,9 @@ export interface FileRoutesByTo {
|
|||||||
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
|
'/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
|
||||||
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
|
'/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
|
||||||
'/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
|
'/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
|
||||||
|
'/admin/resources/machine-identities': typeof adminMachineIdentitiesResourcesPageRouteRoute
|
||||||
|
'/admin/resources/organizations': typeof adminOrganizationResourcesPageRouteRoute
|
||||||
|
'/admin/resources/user-identities': typeof adminUserIdentitiesResourcesPageRouteRoute
|
||||||
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
|
'/cert-manager/$projectId/alerting': typeof certManagerAlertingPageRouteRoute
|
||||||
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
|
'/cert-manager/$projectId/certificate-authorities': typeof certManagerCertificateAuthoritiesPageRouteRoute
|
||||||
'/cert-manager/$projectId/certificates': typeof certManagerCertificatesPageRouteRoute
|
'/cert-manager/$projectId/certificates': typeof certManagerCertificatesPageRouteRoute
|
||||||
@@ -4482,13 +4617,17 @@ export interface FileRoutesById {
|
|||||||
'/_authenticate/_inject-org-details/_org-layout/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutIntegrationsRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/integrations': typeof AuthenticateInjectOrgDetailsOrgLayoutIntegrationsRouteWithChildren
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/organization': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationRouteWithChildren
|
||||||
'/_authenticate/_inject-org-details/admin/_admin-layout': typeof adminLayoutRouteWithChildren
|
'/_authenticate/_inject-org-details/admin/_admin-layout': typeof adminLayoutRouteWithChildren
|
||||||
'/_authenticate/_inject-org-details/admin/_admin-layout/': typeof adminOverviewPageRouteRoute
|
'/_authenticate/_inject-org-details/admin/_admin-layout/': typeof adminGeneralPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/access-management': typeof organizationAccessManagementPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/access-management': typeof organizationAccessManagementPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/admin': typeof organizationAdminPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/admin': typeof organizationAdminPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/audit-logs': typeof organizationAuditLogsPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/billing': typeof organizationBillingPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/billing': typeof organizationBillingPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning': typeof organizationSecretScanningPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/sso': typeof organizationSsoPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/sso': typeof organizationSsoPageRouteRoute
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/authentication': typeof adminAuthenticationPageRouteRoute
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/caching': typeof adminCachingPageRouteRoute
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/encryption': typeof adminEncryptionPageRouteRoute
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/integrations': typeof adminIntegrationsPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdRouteWithChildren
|
||||||
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutKmsProjectIdRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId': typeof AuthenticateInjectOrgDetailsOrgLayoutKmsProjectIdRouteWithChildren
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/app-connections': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/organization/app-connections': typeof AuthenticateInjectOrgDetailsOrgLayoutOrganizationAppConnectionsRouteWithChildren
|
||||||
@@ -4514,6 +4653,9 @@ export interface FileRoutesById {
|
|||||||
'/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings': typeof organizationSecretSharingSettingsPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview': typeof organizationSshOverviewPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
|
'/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings': typeof organizationSshSettingsPageRouteRoute
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/resources/machine-identities': typeof adminMachineIdentitiesResourcesPageRouteRoute
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/resources/organizations': typeof adminOrganizationResourcesPageRouteRoute
|
||||||
|
'/_authenticate/_inject-org-details/admin/_admin-layout/resources/user-identities': typeof adminUserIdentitiesResourcesPageRouteRoute
|
||||||
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout': typeof certManagerLayoutRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout': typeof certManagerLayoutRouteWithChildren
|
||||||
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout': typeof kmsLayoutRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout': typeof kmsLayoutRouteWithChildren
|
||||||
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout': typeof secretManagerLayoutRouteWithChildren
|
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout': typeof secretManagerLayoutRouteWithChildren
|
||||||
@@ -4692,6 +4834,10 @@ export interface FileRouteTypes {
|
|||||||
| '/organization/billing'
|
| '/organization/billing'
|
||||||
| '/organization/secret-scanning'
|
| '/organization/secret-scanning'
|
||||||
| '/organization/sso'
|
| '/organization/sso'
|
||||||
|
| '/admin/authentication'
|
||||||
|
| '/admin/caching'
|
||||||
|
| '/admin/encryption'
|
||||||
|
| '/admin/integrations'
|
||||||
| '/cert-manager/$projectId'
|
| '/cert-manager/$projectId'
|
||||||
| '/kms/$projectId'
|
| '/kms/$projectId'
|
||||||
| '/organization/app-connections'
|
| '/organization/app-connections'
|
||||||
@@ -4717,6 +4863,9 @@ export interface FileRouteTypes {
|
|||||||
| '/organization/secret-sharing/settings'
|
| '/organization/secret-sharing/settings'
|
||||||
| '/organization/ssh/overview'
|
| '/organization/ssh/overview'
|
||||||
| '/organization/ssh/settings'
|
| '/organization/ssh/settings'
|
||||||
|
| '/admin/resources/machine-identities'
|
||||||
|
| '/admin/resources/organizations'
|
||||||
|
| '/admin/resources/user-identities'
|
||||||
| '/cert-manager/$projectId/alerting'
|
| '/cert-manager/$projectId/alerting'
|
||||||
| '/cert-manager/$projectId/certificate-authorities'
|
| '/cert-manager/$projectId/certificate-authorities'
|
||||||
| '/cert-manager/$projectId/certificates'
|
| '/cert-manager/$projectId/certificates'
|
||||||
@@ -4884,6 +5033,10 @@ export interface FileRouteTypes {
|
|||||||
| '/organization/billing'
|
| '/organization/billing'
|
||||||
| '/organization/secret-scanning'
|
| '/organization/secret-scanning'
|
||||||
| '/organization/sso'
|
| '/organization/sso'
|
||||||
|
| '/admin/authentication'
|
||||||
|
| '/admin/caching'
|
||||||
|
| '/admin/encryption'
|
||||||
|
| '/admin/integrations'
|
||||||
| '/cert-manager/$projectId'
|
| '/cert-manager/$projectId'
|
||||||
| '/kms/$projectId'
|
| '/kms/$projectId'
|
||||||
| '/secret-manager/$projectId'
|
| '/secret-manager/$projectId'
|
||||||
@@ -4905,6 +5058,9 @@ export interface FileRouteTypes {
|
|||||||
| '/organization/secret-sharing/settings'
|
| '/organization/secret-sharing/settings'
|
||||||
| '/organization/ssh/overview'
|
| '/organization/ssh/overview'
|
||||||
| '/organization/ssh/settings'
|
| '/organization/ssh/settings'
|
||||||
|
| '/admin/resources/machine-identities'
|
||||||
|
| '/admin/resources/organizations'
|
||||||
|
| '/admin/resources/user-identities'
|
||||||
| '/cert-manager/$projectId/alerting'
|
| '/cert-manager/$projectId/alerting'
|
||||||
| '/cert-manager/$projectId/certificate-authorities'
|
| '/cert-manager/$projectId/certificate-authorities'
|
||||||
| '/cert-manager/$projectId/certificates'
|
| '/cert-manager/$projectId/certificates'
|
||||||
@@ -5078,6 +5234,10 @@ export interface FileRouteTypes {
|
|||||||
| '/_authenticate/_inject-org-details/_org-layout/organization/billing'
|
| '/_authenticate/_inject-org-details/_org-layout/organization/billing'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning'
|
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-scanning'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/organization/sso'
|
| '/_authenticate/_inject-org-details/_org-layout/organization/sso'
|
||||||
|
| '/_authenticate/_inject-org-details/admin/_admin-layout/authentication'
|
||||||
|
| '/_authenticate/_inject-org-details/admin/_admin-layout/caching'
|
||||||
|
| '/_authenticate/_inject-org-details/admin/_admin-layout/encryption'
|
||||||
|
| '/_authenticate/_inject-org-details/admin/_admin-layout/integrations'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId'
|
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/kms/$projectId'
|
| '/_authenticate/_inject-org-details/_org-layout/kms/$projectId'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/organization/app-connections'
|
| '/_authenticate/_inject-org-details/_org-layout/organization/app-connections'
|
||||||
@@ -5103,6 +5263,9 @@ export interface FileRouteTypes {
|
|||||||
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings'
|
| '/_authenticate/_inject-org-details/_org-layout/organization/secret-sharing/settings'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview'
|
| '/_authenticate/_inject-org-details/_org-layout/organization/ssh/overview'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings'
|
| '/_authenticate/_inject-org-details/_org-layout/organization/ssh/settings'
|
||||||
|
| '/_authenticate/_inject-org-details/admin/_admin-layout/resources/machine-identities'
|
||||||
|
| '/_authenticate/_inject-org-details/admin/_admin-layout/resources/organizations'
|
||||||
|
| '/_authenticate/_inject-org-details/admin/_admin-layout/resources/user-identities'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout'
|
| '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout'
|
| '/_authenticate/_inject-org-details/_org-layout/kms/$projectId/_kms-layout'
|
||||||
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout'
|
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout'
|
||||||
@@ -5502,11 +5665,18 @@ export const routeTree = rootRoute
|
|||||||
"filePath": "admin/layout.tsx",
|
"filePath": "admin/layout.tsx",
|
||||||
"parent": "/_authenticate/_inject-org-details/admin",
|
"parent": "/_authenticate/_inject-org-details/admin",
|
||||||
"children": [
|
"children": [
|
||||||
"/_authenticate/_inject-org-details/admin/_admin-layout/"
|
"/_authenticate/_inject-org-details/admin/_admin-layout/",
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/authentication",
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/caching",
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/encryption",
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/integrations",
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/machine-identities",
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/organizations",
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/user-identities"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/_authenticate/_inject-org-details/admin/_admin-layout/": {
|
"/_authenticate/_inject-org-details/admin/_admin-layout/": {
|
||||||
"filePath": "admin/OverviewPage/route.tsx",
|
"filePath": "admin/GeneralPage/route.tsx",
|
||||||
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
},
|
},
|
||||||
"/_authenticate/_inject-org-details/_org-layout/organization/access-management": {
|
"/_authenticate/_inject-org-details/_org-layout/organization/access-management": {
|
||||||
@@ -5533,6 +5703,22 @@ export const routeTree = rootRoute
|
|||||||
"filePath": "organization/SsoPage/route.tsx",
|
"filePath": "organization/SsoPage/route.tsx",
|
||||||
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
|
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
|
||||||
},
|
},
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/authentication": {
|
||||||
|
"filePath": "admin/AuthenticationPage/route.tsx",
|
||||||
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
|
},
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/caching": {
|
||||||
|
"filePath": "admin/CachingPage/route.tsx",
|
||||||
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
|
},
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/encryption": {
|
||||||
|
"filePath": "admin/EncryptionPage/route.tsx",
|
||||||
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
|
},
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/integrations": {
|
||||||
|
"filePath": "admin/IntegrationsPage/route.tsx",
|
||||||
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
|
},
|
||||||
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId": {
|
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId": {
|
||||||
"filePath": "",
|
"filePath": "",
|
||||||
"parent": "/_authenticate/_inject-org-details/_org-layout",
|
"parent": "/_authenticate/_inject-org-details/_org-layout",
|
||||||
@@ -5660,6 +5846,18 @@ export const routeTree = rootRoute
|
|||||||
"filePath": "organization/SshSettingsPage/route.tsx",
|
"filePath": "organization/SshSettingsPage/route.tsx",
|
||||||
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
|
"parent": "/_authenticate/_inject-org-details/_org-layout/organization"
|
||||||
},
|
},
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/machine-identities": {
|
||||||
|
"filePath": "admin/MachineIdentitiesResourcesPage/route.tsx",
|
||||||
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
|
},
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/organizations": {
|
||||||
|
"filePath": "admin/OrganizationResourcesPage/route.tsx",
|
||||||
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
|
},
|
||||||
|
"/_authenticate/_inject-org-details/admin/_admin-layout/resources/user-identities": {
|
||||||
|
"filePath": "admin/UserIdentitiesResourcesPage/route.tsx",
|
||||||
|
"parent": "/_authenticate/_inject-org-details/admin/_admin-layout"
|
||||||
|
},
|
||||||
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout": {
|
"/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout": {
|
||||||
"filePath": "cert-manager/layout.tsx",
|
"filePath": "cert-manager/layout.tsx",
|
||||||
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId",
|
"parent": "/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId",
|
||||||
|
|||||||
+10
-1
@@ -4,7 +4,16 @@ const middleware = (fileName: string, virtualRoutes: VirtualRouteNode[]) =>
|
|||||||
layout(`middlewares/${fileName}`, virtualRoutes);
|
layout(`middlewares/${fileName}`, virtualRoutes);
|
||||||
|
|
||||||
const adminRoute = route("/admin", [
|
const adminRoute = route("/admin", [
|
||||||
layout("admin-layout", "admin/layout.tsx", [index("admin/OverviewPage/route.tsx")])
|
layout("admin-layout", "admin/layout.tsx", [
|
||||||
|
index("admin/GeneralPage/route.tsx"),
|
||||||
|
route("/encryption", "admin/EncryptionPage/route.tsx"),
|
||||||
|
route("/authentication", "admin/AuthenticationPage/route.tsx"),
|
||||||
|
route("/integrations", "admin/IntegrationsPage/route.tsx"),
|
||||||
|
route("/caching", "admin/CachingPage/route.tsx"),
|
||||||
|
route("/resources/organizations", "admin/OrganizationResourcesPage/route.tsx"),
|
||||||
|
route("/resources/user-identities", "admin/UserIdentitiesResourcesPage/route.tsx"),
|
||||||
|
route("/resources/machine-identities", "admin/MachineIdentitiesResourcesPage/route.tsx")
|
||||||
|
])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const organizationRoutes = route("/organization", [
|
const organizationRoutes = route("/organization", [
|
||||||
|
|||||||
Reference in New Issue
Block a user