mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
improvements: address feedback
This commit is contained in:
@@ -850,7 +850,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
schema: {
|
||||
body: z.object({
|
||||
name: GenericResourceNameSchema,
|
||||
inviteAdminEmails: z.string().email().array()
|
||||
inviteAdminEmails: z.string().email().array().min(1)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
|
||||
@@ -9,7 +9,6 @@ interface OrganizationAssignmentTemplateProps extends Omit<BaseEmailWrapperProps
|
||||
inviterFirstName?: string;
|
||||
inviterUsername?: string;
|
||||
organizationName: string;
|
||||
email: string;
|
||||
callback_url: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ type TSuperAdminServiceFactoryDep = {
|
||||
kmsRootConfigDAL: TKmsRootConfigDALFactory;
|
||||
orgService: Pick<TOrgServiceFactory, "createOrganization" | "inviteUserToOrganization">;
|
||||
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry" | "deleteItem" | "deleteItems">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "onPremFeatures" | "updateSubscriptionOrgMemberCount">;
|
||||
microsoftTeamsService: Pick<TMicrosoftTeamsServiceFactory, "initializeTeamsBot">;
|
||||
invalidateCacheQueue: TInvalidateCacheQueueFactory;
|
||||
smtpService: Pick<TSmtpService, "sendMail">;
|
||||
@@ -811,13 +811,13 @@ export const superAdminServiceFactory = ({
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
// 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(
|
||||
{
|
||||
userId: inviteeUserId,
|
||||
@@ -853,6 +853,8 @@ export const superAdminServiceFactory = ({
|
||||
return { organization: org, users };
|
||||
});
|
||||
|
||||
await licenseService.updateSubscriptionOrgMemberCount(organization.id);
|
||||
|
||||
await Promise.allSettled(
|
||||
usersToEmail.map(async (user) => {
|
||||
if (!user.email) return;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { OrgMembershipStatus } from "@app/hooks/api/organization/types";
|
||||
|
||||
import { Organization } from "../types";
|
||||
|
||||
export enum LoginMethod {
|
||||
@@ -20,7 +22,7 @@ export type OrganizationWithProjects = Organization & {
|
||||
lastName: string | null;
|
||||
};
|
||||
membershipId: string;
|
||||
status: string;
|
||||
status: OrgMembershipStatus;
|
||||
role: string;
|
||||
roleId: string | null;
|
||||
}[];
|
||||
|
||||
@@ -159,3 +159,8 @@ export enum OrgIdentityOrderBy {
|
||||
Name = "name",
|
||||
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>
|
||||
{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>
|
||||
<FontAwesomeIcon icon={faServer} className="text-xs text-bunker-300" />
|
||||
</div>
|
||||
<div className="whitespace-nowrap">Server Console</div>
|
||||
</div>
|
||||
</Link>
|
||||
<p className="pl-3 pr-3 text-lg text-mineshaft-400/70">/</p>
|
||||
{breadcrumbs ? (
|
||||
// scott: remove /admin as we show server console above
|
||||
<BreadcrumbContainer breadcrumbs={breadcrumbs.slice(1) as TBreadcrumbFormat[]} />
|
||||
) : null}
|
||||
</>
|
||||
|
||||
@@ -89,11 +89,7 @@ const Content = ({ onClose }: ContentProps) => {
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Controller
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
isError={Boolean(error)}
|
||||
errorText={error?.message}
|
||||
label="Assign Organization Admins"
|
||||
>
|
||||
<FormControl isError={Boolean(error)} errorText={error?.message} label="User">
|
||||
<FilterableSelect
|
||||
isLoading={searchUserFilter !== debouncedSearchTerm || isFetching}
|
||||
className="w-full"
|
||||
@@ -120,7 +116,7 @@ const Content = ({ onClose }: ContentProps) => {
|
||||
isDisabled={isSubmitting}
|
||||
colorSchema="secondary"
|
||||
>
|
||||
Grant Server Admin
|
||||
Grant
|
||||
</Button>
|
||||
<Button onClick={() => onClose()} variant="plain" colorSchema="secondary">
|
||||
Cancel
|
||||
@@ -133,7 +129,11 @@ const Content = ({ onClose }: ContentProps) => {
|
||||
export const AddServerAdminModal = ({ isOpen, onOpenChange }: Props) => {
|
||||
return (
|
||||
<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)} />
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
@@ -424,14 +424,14 @@ export const ServerAdminsTable = () => {
|
||||
/>
|
||||
<DeleteActionModal
|
||||
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)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() => handleRemoveUsers()}
|
||||
buttonText="Remove"
|
||||
>
|
||||
<div className="mt-4 text-sm text-mineshaft-400">
|
||||
The following members will be removed:
|
||||
The following users will be deleted:
|
||||
</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">
|
||||
<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"
|
||||
>
|
||||
<FontAwesomeIcon icon={faWarning} />
|
||||
<span>Removing Yourself</span>
|
||||
<span>Deleting Yourself</span>
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
faBuilding,
|
||||
faCircleQuestion,
|
||||
faEllipsisV,
|
||||
faEnvelope,
|
||||
faEye,
|
||||
faMagnifyingGlass,
|
||||
faPlus,
|
||||
@@ -52,6 +53,7 @@ import {
|
||||
} from "@app/hooks/api";
|
||||
import { OrganizationWithProjects } from "@app/hooks/api/admin/types";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { OrgMembershipStatus } from "@app/hooks/api/organization/types";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
import { AddOrganizationModal } from "@app/pages/admin/ResourceOverviewPage/components/AddOrganizationModal";
|
||||
|
||||
@@ -120,7 +122,7 @@ const ViewMembersModalContent = ({
|
||||
switch (orderBy) {
|
||||
case MembersOrderBy.Email:
|
||||
valueOne = memberOne.user.email || memberOne.user.username;
|
||||
valueTwo = memberTwo.user.email || memberOne.user.username;
|
||||
valueTwo = memberTwo.user.email || memberTwo.user.username;
|
||||
break;
|
||||
case MembersOrderBy.Name:
|
||||
default:
|
||||
@@ -255,23 +257,26 @@ const ViewMembersModalContent = ({
|
||||
<Td className="max-w-0">
|
||||
<div className="flex items-center">
|
||||
<p className="truncate">{username || email}</p>
|
||||
{role === OrgMembershipRole.Admin && status !== "accepted" && (
|
||||
<Button
|
||||
isDisabled={resendOrgInvite.isPending}
|
||||
className="ml-2 h-8 border-mineshaft-600 bg-mineshaft-800/50 font-normal"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
isLoading={
|
||||
resendOrgInvite.isPending && resendInviteId === member.membershipId
|
||||
}
|
||||
onClick={(e) => {
|
||||
onResendInvite(member.membershipId);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
Resend Invite
|
||||
</Button>
|
||||
)}
|
||||
{role === OrgMembershipRole.Admin &&
|
||||
status !== OrgMembershipStatus.Accepted && (
|
||||
<Button
|
||||
isDisabled={resendOrgInvite.isPending}
|
||||
className="ml-2 h-7 border-mineshaft-600 bg-mineshaft-800/50 font-normal"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
size="xs"
|
||||
isLoading={
|
||||
resendOrgInvite.isPending && resendInviteId === member.membershipId
|
||||
}
|
||||
leftIcon={<FontAwesomeIcon icon={faEnvelope} />}
|
||||
onClick={(e) => {
|
||||
onResendInvite(member.membershipId);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
Resend Invite
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
@@ -485,9 +490,9 @@ const OrganizationsPanelTable = ({
|
||||
{!org.members.some(
|
||||
(member) =>
|
||||
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">
|
||||
<Badge>
|
||||
<FontAwesomeIcon icon={faWarning} />
|
||||
|
||||
@@ -521,14 +521,14 @@ export const UserIdentitiesTable = () => {
|
||||
/>
|
||||
<DeleteActionModal
|
||||
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)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() => handleRemoveUsers()}
|
||||
buttonText="Remove"
|
||||
buttonText="Delete"
|
||||
>
|
||||
<div className="mt-4 text-sm text-mineshaft-400">
|
||||
The following members will be removed:
|
||||
The following users will be deleted:
|
||||
</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">
|
||||
<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"
|
||||
>
|
||||
<FontAwesomeIcon icon={faWarning} />
|
||||
<span>Removing Yourself</span>
|
||||
<span>Deleting Yourself</span>
|
||||
</Badge>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
Reference in New Issue
Block a user