mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 13:26:31 +00:00
improvements: address feedback
This commit is contained in:
@@ -850,7 +850,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
name: GenericResourceNameSchema,
|
name: GenericResourceNameSchema,
|
||||||
inviteAdminEmails: z.string().email().array()
|
inviteAdminEmails: z.string().email().array().min(1)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ interface OrganizationAssignmentTemplateProps extends Omit<BaseEmailWrapperProps
|
|||||||
inviterFirstName?: string;
|
inviterFirstName?: string;
|
||||||
inviterUsername?: string;
|
inviterUsername?: string;
|
||||||
organizationName: string;
|
organizationName: string;
|
||||||
email: string;
|
|
||||||
callback_url: string;
|
callback_url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ type TSuperAdminServiceFactoryDep = {
|
|||||||
kmsRootConfigDAL: TKmsRootConfigDALFactory;
|
kmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||||
orgService: Pick<TOrgServiceFactory, "createOrganization" | "inviteUserToOrganization">;
|
orgService: Pick<TOrgServiceFactory, "createOrganization" | "inviteUserToOrganization">;
|
||||||
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry" | "deleteItem" | "deleteItems">;
|
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry" | "deleteItem" | "deleteItems">;
|
||||||
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures">;
|
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "updateSubscriptionOrgMemberCount">;
|
||||||
microsoftTeamsService: Pick<TMicrosoftTeamsServiceFactory, "initializeTeamsBot">;
|
microsoftTeamsService: Pick<TMicrosoftTeamsServiceFactory, "initializeTeamsBot">;
|
||||||
invalidateCacheQueue: TInvalidateCacheQueueFactory;
|
invalidateCacheQueue: TInvalidateCacheQueueFactory;
|
||||||
smtpService: Pick<TSmtpService, "sendMail">;
|
smtpService: Pick<TSmtpService, "sendMail">;
|
||||||
@@ -811,13 +811,13 @@ export const superAdminServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const inviteeUserId = inviteeUser?.id;
|
const inviteeUserId = inviteeUser?.id;
|
||||||
const existingEncrytionKey = await userDAL.findUserEncKeyByUserId(inviteeUserId, tx);
|
const existingEncryptionKey = await userDAL.findUserEncKeyByUserId(inviteeUserId, tx);
|
||||||
|
|
||||||
// when user is missing the encrytion keys
|
// when user is missing the encrytion keys
|
||||||
// this could happen either if user doesn't exist or user didn't find step 3 of generating the encryption keys of srp
|
// this could happen either if user doesn't exist or user didn't find step 3 of generating the encryption keys of srp
|
||||||
// So what we do is we generate a random secure password and then encrypt it with a random pub-private key
|
// So what we do is we generate a random secure password and then encrypt it with a random pub-private key
|
||||||
// Then when user sign in (as login is not possible as isAccepted is false) we rencrypt the private key with the user password
|
// Then when user sign in (as login is not possible as isAccepted is false) we rencrypt the private key with the user password
|
||||||
if (!inviteeUser || (inviteeUser && !inviteeUser?.isAccepted && !existingEncrytionKey)) {
|
if (!inviteeUser || (inviteeUser && !inviteeUser?.isAccepted && !existingEncryptionKey)) {
|
||||||
await userDAL.createUserEncryption(
|
await userDAL.createUserEncryption(
|
||||||
{
|
{
|
||||||
userId: inviteeUserId,
|
userId: inviteeUserId,
|
||||||
@@ -853,6 +853,8 @@ export const superAdminServiceFactory = ({
|
|||||||
return { organization: org, users };
|
return { organization: org, users };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await licenseService.updateSubscriptionOrgMemberCount(organization.id);
|
||||||
|
|
||||||
await Promise.allSettled(
|
await Promise.allSettled(
|
||||||
usersToEmail.map(async (user) => {
|
usersToEmail.map(async (user) => {
|
||||||
if (!user.email) return;
|
if (!user.email) return;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { OrgMembershipStatus } from "@app/hooks/api/organization/types";
|
||||||
|
|
||||||
import { Organization } from "../types";
|
import { Organization } from "../types";
|
||||||
|
|
||||||
export enum LoginMethod {
|
export enum LoginMethod {
|
||||||
@@ -20,7 +22,7 @@ export type OrganizationWithProjects = Organization & {
|
|||||||
lastName: string | null;
|
lastName: string | null;
|
||||||
};
|
};
|
||||||
membershipId: string;
|
membershipId: string;
|
||||||
status: string;
|
status: OrgMembershipStatus;
|
||||||
role: string;
|
role: string;
|
||||||
roleId: string | null;
|
roleId: string | null;
|
||||||
}[];
|
}[];
|
||||||
|
|||||||
@@ -159,3 +159,8 @@ export enum OrgIdentityOrderBy {
|
|||||||
Name = "name",
|
Name = "name",
|
||||||
Role = "role"
|
Role = "role"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgMembershipStatus {
|
||||||
|
Invited = "invited",
|
||||||
|
Accepted = "accepted"
|
||||||
|
}
|
||||||
|
|||||||
@@ -192,14 +192,18 @@ export const Navbar = () => {
|
|||||||
<p className="pl-1 pr-3 text-lg text-mineshaft-400/70">/</p>
|
<p className="pl-1 pr-3 text-lg text-mineshaft-400/70">/</p>
|
||||||
{isServerAdminPanel ? (
|
{isServerAdminPanel ? (
|
||||||
<>
|
<>
|
||||||
<div className="group flex cursor-pointer items-center gap-2 text-sm text-white transition-all duration-100 hover:text-primary">
|
<Link
|
||||||
|
to="/admin"
|
||||||
|
className="group flex cursor-pointer items-center gap-2 text-sm text-white transition-all duration-100 hover:text-primary"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<FontAwesomeIcon icon={faServer} className="text-xs text-bunker-300" />
|
<FontAwesomeIcon icon={faServer} className="text-xs text-bunker-300" />
|
||||||
</div>
|
</div>
|
||||||
<div className="whitespace-nowrap">Server Console</div>
|
<div className="whitespace-nowrap">Server Console</div>
|
||||||
</div>
|
</Link>
|
||||||
<p className="pl-3 pr-3 text-lg text-mineshaft-400/70">/</p>
|
<p className="pl-3 pr-3 text-lg text-mineshaft-400/70">/</p>
|
||||||
{breadcrumbs ? (
|
{breadcrumbs ? (
|
||||||
|
// scott: remove /admin as we show server console above
|
||||||
<BreadcrumbContainer breadcrumbs={breadcrumbs.slice(1) as TBreadcrumbFormat[]} />
|
<BreadcrumbContainer breadcrumbs={breadcrumbs.slice(1) as TBreadcrumbFormat[]} />
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -89,11 +89,7 @@ const Content = ({ onClose }: ContentProps) => {
|
|||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<Controller
|
<Controller
|
||||||
render={({ field, fieldState: { error } }) => (
|
render={({ field, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl isError={Boolean(error)} errorText={error?.message} label="User">
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
label="Assign Organization Admins"
|
|
||||||
>
|
|
||||||
<FilterableSelect
|
<FilterableSelect
|
||||||
isLoading={searchUserFilter !== debouncedSearchTerm || isFetching}
|
isLoading={searchUserFilter !== debouncedSearchTerm || isFetching}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@@ -120,7 +116,7 @@ const Content = ({ onClose }: ContentProps) => {
|
|||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
colorSchema="secondary"
|
colorSchema="secondary"
|
||||||
>
|
>
|
||||||
Grant Server Admin
|
Grant
|
||||||
</Button>
|
</Button>
|
||||||
<Button onClick={() => onClose()} variant="plain" colorSchema="secondary">
|
<Button onClick={() => onClose()} variant="plain" colorSchema="secondary">
|
||||||
Cancel
|
Cancel
|
||||||
@@ -133,7 +129,11 @@ const Content = ({ onClose }: ContentProps) => {
|
|||||||
export const AddServerAdminModal = ({ isOpen, onOpenChange }: Props) => {
|
export const AddServerAdminModal = ({ isOpen, onOpenChange }: Props) => {
|
||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
|
||||||
<ModalContent bodyClassName="overflow-visible" title="Grant Server Admin">
|
<ModalContent
|
||||||
|
bodyClassName="overflow-visible"
|
||||||
|
title="Grant Server Admin"
|
||||||
|
subTitle="Grant server admin status to a user"
|
||||||
|
>
|
||||||
<Content onClose={() => onOpenChange(false)} />
|
<Content onClose={() => onOpenChange(false)} />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@@ -424,14 +424,14 @@ export const ServerAdminsTable = () => {
|
|||||||
/>
|
/>
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.removeUsers.isOpen}
|
isOpen={popUp.removeUsers.isOpen}
|
||||||
title="Are you sure you want to remove the following users?"
|
title="Are you sure you want to delete the following users?"
|
||||||
onChange={(isOpen) => handlePopUpToggle("removeUsers", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("removeUsers", isOpen)}
|
||||||
deleteKey="confirm"
|
deleteKey="confirm"
|
||||||
onDeleteApproved={() => handleRemoveUsers()}
|
onDeleteApproved={() => handleRemoveUsers()}
|
||||||
buttonText="Remove"
|
buttonText="Remove"
|
||||||
>
|
>
|
||||||
<div className="mt-4 text-sm text-mineshaft-400">
|
<div className="mt-4 text-sm text-mineshaft-400">
|
||||||
The following members will be removed:
|
The following users will be deleted:
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 max-h-[20rem] overflow-y-auto rounded border border-mineshaft-600 bg-red/10 p-4 pl-8 text-sm text-red-200">
|
<div className="mt-2 max-h-[20rem] overflow-y-auto rounded border border-mineshaft-600 bg-red/10 p-4 pl-8 text-sm text-red-200">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
@@ -458,7 +458,7 @@ export const ServerAdminsTable = () => {
|
|||||||
className="ml-1 mt-[0.05rem] inline-flex w-min items-center gap-1.5 whitespace-nowrap"
|
className="ml-1 mt-[0.05rem] inline-flex w-min items-center gap-1.5 whitespace-nowrap"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faWarning} />
|
<FontAwesomeIcon icon={faWarning} />
|
||||||
<span>Removing Yourself</span>
|
<span>Deleting Yourself</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
faBuilding,
|
faBuilding,
|
||||||
faCircleQuestion,
|
faCircleQuestion,
|
||||||
faEllipsisV,
|
faEllipsisV,
|
||||||
|
faEnvelope,
|
||||||
faEye,
|
faEye,
|
||||||
faMagnifyingGlass,
|
faMagnifyingGlass,
|
||||||
faPlus,
|
faPlus,
|
||||||
@@ -52,6 +53,7 @@ import {
|
|||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { OrganizationWithProjects } from "@app/hooks/api/admin/types";
|
import { OrganizationWithProjects } from "@app/hooks/api/admin/types";
|
||||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||||
|
import { OrgMembershipStatus } from "@app/hooks/api/organization/types";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
import { AddOrganizationModal } from "@app/pages/admin/ResourceOverviewPage/components/AddOrganizationModal";
|
import { AddOrganizationModal } from "@app/pages/admin/ResourceOverviewPage/components/AddOrganizationModal";
|
||||||
|
|
||||||
@@ -120,7 +122,7 @@ const ViewMembersModalContent = ({
|
|||||||
switch (orderBy) {
|
switch (orderBy) {
|
||||||
case MembersOrderBy.Email:
|
case MembersOrderBy.Email:
|
||||||
valueOne = memberOne.user.email || memberOne.user.username;
|
valueOne = memberOne.user.email || memberOne.user.username;
|
||||||
valueTwo = memberTwo.user.email || memberOne.user.username;
|
valueTwo = memberTwo.user.email || memberTwo.user.username;
|
||||||
break;
|
break;
|
||||||
case MembersOrderBy.Name:
|
case MembersOrderBy.Name:
|
||||||
default:
|
default:
|
||||||
@@ -255,23 +257,26 @@ const ViewMembersModalContent = ({
|
|||||||
<Td className="max-w-0">
|
<Td className="max-w-0">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<p className="truncate">{username || email}</p>
|
<p className="truncate">{username || email}</p>
|
||||||
{role === OrgMembershipRole.Admin && status !== "accepted" && (
|
{role === OrgMembershipRole.Admin &&
|
||||||
<Button
|
status !== OrgMembershipStatus.Accepted && (
|
||||||
isDisabled={resendOrgInvite.isPending}
|
<Button
|
||||||
className="ml-2 h-8 border-mineshaft-600 bg-mineshaft-800/50 font-normal"
|
isDisabled={resendOrgInvite.isPending}
|
||||||
colorSchema="primary"
|
className="ml-2 h-7 border-mineshaft-600 bg-mineshaft-800/50 font-normal"
|
||||||
variant="outline_bg"
|
colorSchema="primary"
|
||||||
isLoading={
|
variant="outline_bg"
|
||||||
resendOrgInvite.isPending && resendInviteId === member.membershipId
|
size="xs"
|
||||||
}
|
isLoading={
|
||||||
onClick={(e) => {
|
resendOrgInvite.isPending && resendInviteId === member.membershipId
|
||||||
onResendInvite(member.membershipId);
|
}
|
||||||
e.stopPropagation();
|
leftIcon={<FontAwesomeIcon icon={faEnvelope} />}
|
||||||
}}
|
onClick={(e) => {
|
||||||
>
|
onResendInvite(member.membershipId);
|
||||||
Resend Invite
|
e.stopPropagation();
|
||||||
</Button>
|
}}
|
||||||
)}
|
>
|
||||||
|
Resend Invite
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
@@ -485,9 +490,9 @@ const OrganizationsPanelTable = ({
|
|||||||
{!org.members.some(
|
{!org.members.some(
|
||||||
(member) =>
|
(member) =>
|
||||||
member.role === OrgMembershipRole.Admin &&
|
member.role === OrgMembershipRole.Admin &&
|
||||||
member.status === "accepted"
|
member.status === OrgMembershipStatus.Accepted
|
||||||
) && (
|
) && (
|
||||||
<Tooltip content="No admins have accepted their invites.">
|
<Tooltip content="No admins have accepted their invitations.">
|
||||||
<div className="ml-1">
|
<div className="ml-1">
|
||||||
<Badge>
|
<Badge>
|
||||||
<FontAwesomeIcon icon={faWarning} />
|
<FontAwesomeIcon icon={faWarning} />
|
||||||
|
|||||||
@@ -521,14 +521,14 @@ export const UserIdentitiesTable = () => {
|
|||||||
/>
|
/>
|
||||||
<DeleteActionModal
|
<DeleteActionModal
|
||||||
isOpen={popUp.removeUsers.isOpen}
|
isOpen={popUp.removeUsers.isOpen}
|
||||||
title="Are you sure you want to remove the following users?"
|
title="Are you sure you want to delete the following users?"
|
||||||
onChange={(isOpen) => handlePopUpToggle("removeUsers", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("removeUsers", isOpen)}
|
||||||
deleteKey="confirm"
|
deleteKey="confirm"
|
||||||
onDeleteApproved={() => handleRemoveUsers()}
|
onDeleteApproved={() => handleRemoveUsers()}
|
||||||
buttonText="Remove"
|
buttonText="Delete"
|
||||||
>
|
>
|
||||||
<div className="mt-4 text-sm text-mineshaft-400">
|
<div className="mt-4 text-sm text-mineshaft-400">
|
||||||
The following members will be removed:
|
The following users will be deleted:
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 max-h-[20rem] overflow-y-auto rounded border border-mineshaft-600 bg-red/10 p-4 pl-8 text-sm text-red-200">
|
<div className="mt-2 max-h-[20rem] overflow-y-auto rounded border border-mineshaft-600 bg-red/10 p-4 pl-8 text-sm text-red-200">
|
||||||
<ul className="list-disc">
|
<ul className="list-disc">
|
||||||
@@ -555,7 +555,7 @@ export const UserIdentitiesTable = () => {
|
|||||||
className="ml-1 mt-[0.05rem] inline-flex w-min items-center gap-1.5 whitespace-nowrap"
|
className="ml-1 mt-[0.05rem] inline-flex w-min items-center gap-1.5 whitespace-nowrap"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faWarning} />
|
<FontAwesomeIcon icon={faWarning} />
|
||||||
<span>Removing Yourself</span>
|
<span>Deleting Yourself</span>
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|||||||
Reference in New Issue
Block a user