mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge branch 'feat/automated-instance-bootstrapping' of https://github.com/Infisical/infisical into feat/automated-instance-bootstrapping
This commit is contained in:
@@ -435,6 +435,42 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/user-management/users/:userId/admin-access",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
userId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
id: true
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: (req, res, done) => {
|
||||
verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN])(req, res, () => {
|
||||
verifySuperAdmin(req, res, done);
|
||||
});
|
||||
},
|
||||
handler: async (req) => {
|
||||
const user = await server.services.superAdmin.deleteUserSuperAdminAccess(req.params.userId);
|
||||
|
||||
return {
|
||||
user
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/bootstrap",
|
||||
@@ -450,9 +486,23 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string(),
|
||||
user: UsersSchema,
|
||||
organization: OrganizationsSchema,
|
||||
identity: IdentitiesSchema.extend({
|
||||
user: UsersSchema.pick({
|
||||
username: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
email: true,
|
||||
id: true,
|
||||
superAdmin: true
|
||||
}),
|
||||
organization: OrganizationsSchema.pick({
|
||||
id: true,
|
||||
name: true,
|
||||
slug: true
|
||||
}),
|
||||
identity: IdentitiesSchema.pick({
|
||||
id: true,
|
||||
name: true
|
||||
}).extend({
|
||||
credentials: z.object({
|
||||
token: z.string()
|
||||
}) // would just be Token AUTH for now
|
||||
@@ -478,7 +528,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
|
||||
return {
|
||||
message: "Successfully boostrapped instance",
|
||||
message: "Successfully bootstrapped instance",
|
||||
user: user.user,
|
||||
organization,
|
||||
identity: machineIdentity
|
||||
|
||||
@@ -27,7 +27,7 @@ import { UserAliasType } from "../user-alias/user-alias-types";
|
||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||
import {
|
||||
LoginMethod,
|
||||
TAdminBoostrapInstanceDTO,
|
||||
TAdminBootstrapInstanceDTO,
|
||||
TAdminGetIdentitiesDTO,
|
||||
TAdminGetUsersDTO,
|
||||
TAdminSignUpDTO
|
||||
@@ -291,7 +291,7 @@ export const superAdminServiceFactory = ({
|
||||
return { token, user: userInfo, organization };
|
||||
};
|
||||
|
||||
const bootstrapInstance = async ({ email, password, organizationName }: TAdminBoostrapInstanceDTO) => {
|
||||
const bootstrapInstance = async ({ email, password, organizationName }: TAdminBootstrapInstanceDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID);
|
||||
if (serverCfg?.initialized) {
|
||||
@@ -453,6 +453,17 @@ export const superAdminServiceFactory = ({
|
||||
return identity;
|
||||
};
|
||||
|
||||
const deleteUserSuperAdminAccess = async (userId: string) => {
|
||||
const user = await userDAL.findById(userId);
|
||||
if (!user) {
|
||||
throw new NotFoundError({ name: "User", message: "User not found" });
|
||||
}
|
||||
|
||||
const updatedUser = userDAL.updateById(userId, { superAdmin: false });
|
||||
|
||||
return updatedUser;
|
||||
};
|
||||
|
||||
const getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||
const identities = await identityDAL.getIdentitiesByFilter({
|
||||
limit,
|
||||
@@ -571,6 +582,7 @@ export const superAdminServiceFactory = ({
|
||||
updateRootEncryptionStrategy,
|
||||
getConfiguredEncryptionStrategies,
|
||||
grantServerAdminAccessToUser,
|
||||
deleteIdentitySuperAdminAccess
|
||||
deleteIdentitySuperAdminAccess,
|
||||
deleteUserSuperAdminAccess
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ export type TAdminSignUpDTO = {
|
||||
userAgent: string;
|
||||
};
|
||||
|
||||
export type TAdminBoostrapInstanceDTO = {
|
||||
export type TAdminBootstrapInstanceDTO = {
|
||||
email: string;
|
||||
password: string;
|
||||
organizationName: string;
|
||||
|
||||
@@ -601,8 +601,8 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (*BootstrapInstanceResponse, error) {
|
||||
var resBody BootstrapInstanceResponse
|
||||
func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (map[string]interface{}, error) {
|
||||
var resBody map[string]interface{}
|
||||
response, err := httpClient.
|
||||
R().
|
||||
SetResult(&resBody).
|
||||
@@ -618,5 +618,5 @@ func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRe
|
||||
return nil, fmt.Errorf("CallBootstrapInstance: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
||||
}
|
||||
|
||||
return &resBody, nil
|
||||
return resBody, nil
|
||||
}
|
||||
|
||||
@@ -661,20 +661,3 @@ type BootstrapInstanceRequest struct {
|
||||
Organization string `json:"organization"`
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type BootstrapInstanceResponseOrganization struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type BootstrapInstanceResponseIdentity struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Credentials interface{} `json:"credentials"`
|
||||
}
|
||||
|
||||
type BootstrapInstanceResponse struct {
|
||||
Message string `json:"message"`
|
||||
Organization BootstrapInstanceResponseOrganization `json:"organization"`
|
||||
Identity BootstrapInstanceResponseIdentity `json:"identity"`
|
||||
}
|
||||
|
||||
@@ -70,11 +70,13 @@ var bootstrapCmd = &cobra.Command{
|
||||
|
||||
if err != nil {
|
||||
log.Error().Msgf("Failed to bootstrap instance: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
responseJSON, err := json.MarshalIndent(bootstrapResponse, "", " ")
|
||||
if err != nil {
|
||||
log.Fatal().Msgf("Failed to convert response to JSON: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(responseJSON))
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ export {
|
||||
useAdminGrantServerAdminAccess,
|
||||
useAdminRemoveIdentitySuperAdminAccess,
|
||||
useCreateAdminUser,
|
||||
useRemoveUserServerAdminAccess,
|
||||
useUpdateAdminSlackConfig,
|
||||
useUpdateServerConfig,
|
||||
useUpdateServerEncryptionStrategy
|
||||
|
||||
@@ -88,6 +88,22 @@ export const useAdminRemoveIdentitySuperAdminAccess = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoveUserServerAdminAccess = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (userId: string) => {
|
||||
await apiRequest.delete(`/api/v1/admin/user-management/users/${userId}/admin-access`);
|
||||
|
||||
return {};
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [adminStandaloneKeys.getUsers]
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useAdminGrantServerAdminAccess = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
|
||||
@@ -33,22 +33,26 @@ import {
|
||||
THead,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { useSubscription, useUser } from "@app/context";
|
||||
import { useSubscription } from "@app/context";
|
||||
import { useDebounce, usePopUp } from "@app/hooks";
|
||||
import {
|
||||
useAdminDeleteUser,
|
||||
useAdminGetUsers,
|
||||
useAdminGrantServerAdminAccess
|
||||
useAdminGrantServerAdminAccess,
|
||||
useRemoveUserServerAdminAccess
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const addServerAdminUpgradePlanMessage = "Granting another user Server Admin permissions";
|
||||
const removeServerAdminUpgradePlanMessage = "Removing Server Admin permissions from user";
|
||||
|
||||
const UserPanelTable = ({
|
||||
handlePopUpOpen
|
||||
}: {
|
||||
handlePopUpOpen: (
|
||||
popUpName: keyof UsePopUpState<["removeUser", "upgradePlan", "upgradeToServerAdmin"]>,
|
||||
popUpName: keyof UsePopUpState<
|
||||
["removeUser", "upgradePlan", "upgradeToServerAdmin", "removeServerAdmin"]
|
||||
>,
|
||||
data?: {
|
||||
username: string;
|
||||
id: string;
|
||||
@@ -58,8 +62,6 @@ const UserPanelTable = ({
|
||||
}) => {
|
||||
const [searchUserFilter, setSearchUserFilter] = useState("");
|
||||
const [adminsOnly, setAdminsOnly] = useState(false);
|
||||
const { user } = useUser();
|
||||
const userId = user?.id || "";
|
||||
const [debouncedSearchTerm] = useDebounce(searchUserFilter, 500);
|
||||
const { subscription } = useSubscription();
|
||||
|
||||
@@ -143,45 +145,61 @@ const UserPanelTable = ({
|
||||
</Td>
|
||||
<Td className="w-5/12">{email}</Td>
|
||||
<Td>
|
||||
{userId !== id && (
|
||||
<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">
|
||||
<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("removeUser", { username, id });
|
||||
}}
|
||||
>
|
||||
Remove User
|
||||
</DropdownMenuItem>
|
||||
{!superAdmin && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePopUpOpen("removeUser", { username, id });
|
||||
if (!subscription?.instanceUserManagement) {
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
username,
|
||||
id,
|
||||
message: addServerAdminUpgradePlanMessage
|
||||
});
|
||||
return;
|
||||
}
|
||||
handlePopUpOpen("upgradeToServerAdmin", { username, id });
|
||||
}}
|
||||
>
|
||||
Remove User
|
||||
Make User Server Admin
|
||||
</DropdownMenuItem>
|
||||
{!superAdmin && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (!subscription?.instanceUserManagement) {
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
username,
|
||||
id,
|
||||
message: addServerAdminUpgradePlanMessage
|
||||
});
|
||||
return;
|
||||
}
|
||||
handlePopUpOpen("upgradeToServerAdmin", { username, id });
|
||||
}}
|
||||
>
|
||||
Make User Server Admin
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
{superAdmin && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (!subscription?.instanceUserManagement) {
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
username,
|
||||
id,
|
||||
message: removeServerAdminUpgradePlanMessage
|
||||
});
|
||||
return;
|
||||
}
|
||||
handlePopUpOpen("removeServerAdmin", { username, id });
|
||||
}}
|
||||
>
|
||||
Remove Server Admin
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
@@ -212,11 +230,13 @@ export const UserPanel = () => {
|
||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
"removeUser",
|
||||
"upgradePlan",
|
||||
"upgradeToServerAdmin"
|
||||
"upgradeToServerAdmin",
|
||||
"removeServerAdmin"
|
||||
] as const);
|
||||
|
||||
const { mutateAsync: deleteUser } = useAdminDeleteUser();
|
||||
const { mutateAsync: grantAdminAccess } = useAdminGrantServerAdminAccess();
|
||||
const { mutateAsync: removeAdminAccess } = useRemoveUserServerAdminAccess();
|
||||
|
||||
const handleRemoveUser = async () => {
|
||||
const { id } = popUp?.removeUser?.data as { id: string; username: string };
|
||||
@@ -256,6 +276,25 @@ export const UserPanel = () => {
|
||||
handlePopUpClose("upgradeToServerAdmin");
|
||||
};
|
||||
|
||||
const handleRemoveServerAdminAccess = async () => {
|
||||
const { id } = popUp?.removeServerAdmin?.data as { id: string; username: string };
|
||||
|
||||
try {
|
||||
await removeAdminAccess(id);
|
||||
createNotification({
|
||||
type: "success",
|
||||
text: "Successfully removed server admin access from user"
|
||||
});
|
||||
} catch {
|
||||
createNotification({
|
||||
type: "error",
|
||||
text: "Error removing server admin access from user"
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpClose("removeServerAdmin");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-4">
|
||||
@@ -282,6 +321,17 @@ export const UserPanel = () => {
|
||||
onDeleteApproved={handleGrantServerAdminAccess}
|
||||
buttonText="Grant Access"
|
||||
/>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.removeServerAdmin.isOpen}
|
||||
title={`Are you sure want to remove Server Admin permissions from ${
|
||||
(popUp?.removeServerAdmin?.data as { id: string; username: string })?.username || ""
|
||||
}?`}
|
||||
subTitle=""
|
||||
onChange={(isOpen) => handlePopUpToggle("removeServerAdmin", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={handleRemoveServerAdminAccess}
|
||||
buttonText="Remove Access"
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
|
||||
Reference in New Issue
Block a user