mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 20:27:43 +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({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/bootstrap",
|
url: "/bootstrap",
|
||||||
@@ -450,9 +486,23 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
message: z.string(),
|
message: z.string(),
|
||||||
user: UsersSchema,
|
user: UsersSchema.pick({
|
||||||
organization: OrganizationsSchema,
|
username: true,
|
||||||
identity: IdentitiesSchema.extend({
|
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({
|
credentials: z.object({
|
||||||
token: z.string()
|
token: z.string()
|
||||||
}) // would just be Token AUTH for now
|
}) // would just be Token AUTH for now
|
||||||
@@ -478,7 +528,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: "Successfully boostrapped instance",
|
message: "Successfully bootstrapped instance",
|
||||||
user: user.user,
|
user: user.user,
|
||||||
organization,
|
organization,
|
||||||
identity: machineIdentity
|
identity: machineIdentity
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { UserAliasType } from "../user-alias/user-alias-types";
|
|||||||
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
import { TSuperAdminDALFactory } from "./super-admin-dal";
|
||||||
import {
|
import {
|
||||||
LoginMethod,
|
LoginMethod,
|
||||||
TAdminBoostrapInstanceDTO,
|
TAdminBootstrapInstanceDTO,
|
||||||
TAdminGetIdentitiesDTO,
|
TAdminGetIdentitiesDTO,
|
||||||
TAdminGetUsersDTO,
|
TAdminGetUsersDTO,
|
||||||
TAdminSignUpDTO
|
TAdminSignUpDTO
|
||||||
@@ -291,7 +291,7 @@ export const superAdminServiceFactory = ({
|
|||||||
return { token, user: userInfo, organization };
|
return { token, user: userInfo, organization };
|
||||||
};
|
};
|
||||||
|
|
||||||
const bootstrapInstance = async ({ email, password, organizationName }: TAdminBoostrapInstanceDTO) => {
|
const bootstrapInstance = async ({ email, password, organizationName }: TAdminBootstrapInstanceDTO) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID);
|
const serverCfg = await serverCfgDAL.findById(ADMIN_CONFIG_DB_UUID);
|
||||||
if (serverCfg?.initialized) {
|
if (serverCfg?.initialized) {
|
||||||
@@ -453,6 +453,17 @@ export const superAdminServiceFactory = ({
|
|||||||
return identity;
|
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 getIdentities = async ({ offset, limit, searchTerm }: TAdminGetIdentitiesDTO) => {
|
||||||
const identities = await identityDAL.getIdentitiesByFilter({
|
const identities = await identityDAL.getIdentitiesByFilter({
|
||||||
limit,
|
limit,
|
||||||
@@ -571,6 +582,7 @@ export const superAdminServiceFactory = ({
|
|||||||
updateRootEncryptionStrategy,
|
updateRootEncryptionStrategy,
|
||||||
getConfiguredEncryptionStrategies,
|
getConfiguredEncryptionStrategies,
|
||||||
grantServerAdminAccessToUser,
|
grantServerAdminAccessToUser,
|
||||||
deleteIdentitySuperAdminAccess
|
deleteIdentitySuperAdminAccess,
|
||||||
|
deleteUserSuperAdminAccess
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export type TAdminSignUpDTO = {
|
|||||||
userAgent: string;
|
userAgent: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TAdminBoostrapInstanceDTO = {
|
export type TAdminBootstrapInstanceDTO = {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
organizationName: string;
|
organizationName: string;
|
||||||
|
|||||||
@@ -601,8 +601,8 @@ func CallGatewayHeartBeatV1(httpClient *resty.Client) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (*BootstrapInstanceResponse, error) {
|
func CallBootstrapInstance(httpClient *resty.Client, request BootstrapInstanceRequest) (map[string]interface{}, error) {
|
||||||
var resBody BootstrapInstanceResponse
|
var resBody map[string]interface{}
|
||||||
response, err := httpClient.
|
response, err := httpClient.
|
||||||
R().
|
R().
|
||||||
SetResult(&resBody).
|
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 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"`
|
Organization string `json:"organization"`
|
||||||
Domain string `json:"domain"`
|
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 {
|
if err != nil {
|
||||||
log.Error().Msgf("Failed to bootstrap instance: %v", err)
|
log.Error().Msgf("Failed to bootstrap instance: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
responseJSON, err := json.MarshalIndent(bootstrapResponse, "", " ")
|
responseJSON, err := json.MarshalIndent(bootstrapResponse, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Msgf("Failed to convert response to JSON: %v", err)
|
log.Fatal().Msgf("Failed to convert response to JSON: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
fmt.Println(string(responseJSON))
|
fmt.Println(string(responseJSON))
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export {
|
|||||||
useAdminGrantServerAdminAccess,
|
useAdminGrantServerAdminAccess,
|
||||||
useAdminRemoveIdentitySuperAdminAccess,
|
useAdminRemoveIdentitySuperAdminAccess,
|
||||||
useCreateAdminUser,
|
useCreateAdminUser,
|
||||||
|
useRemoveUserServerAdminAccess,
|
||||||
useUpdateAdminSlackConfig,
|
useUpdateAdminSlackConfig,
|
||||||
useUpdateServerConfig,
|
useUpdateServerConfig,
|
||||||
useUpdateServerEncryptionStrategy
|
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 = () => {
|
export const useAdminGrantServerAdminAccess = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
return useMutation({
|
return useMutation({
|
||||||
|
|||||||
@@ -33,22 +33,26 @@ import {
|
|||||||
THead,
|
THead,
|
||||||
Tr
|
Tr
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { useSubscription, useUser } from "@app/context";
|
import { useSubscription } from "@app/context";
|
||||||
import { useDebounce, usePopUp } from "@app/hooks";
|
import { useDebounce, usePopUp } from "@app/hooks";
|
||||||
import {
|
import {
|
||||||
useAdminDeleteUser,
|
useAdminDeleteUser,
|
||||||
useAdminGetUsers,
|
useAdminGetUsers,
|
||||||
useAdminGrantServerAdminAccess
|
useAdminGrantServerAdminAccess,
|
||||||
|
useRemoveUserServerAdminAccess
|
||||||
} from "@app/hooks/api";
|
} from "@app/hooks/api";
|
||||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||||
|
|
||||||
const addServerAdminUpgradePlanMessage = "Granting another user Server Admin permissions";
|
const addServerAdminUpgradePlanMessage = "Granting another user Server Admin permissions";
|
||||||
|
const removeServerAdminUpgradePlanMessage = "Removing Server Admin permissions from user";
|
||||||
|
|
||||||
const UserPanelTable = ({
|
const UserPanelTable = ({
|
||||||
handlePopUpOpen
|
handlePopUpOpen
|
||||||
}: {
|
}: {
|
||||||
handlePopUpOpen: (
|
handlePopUpOpen: (
|
||||||
popUpName: keyof UsePopUpState<["removeUser", "upgradePlan", "upgradeToServerAdmin"]>,
|
popUpName: keyof UsePopUpState<
|
||||||
|
["removeUser", "upgradePlan", "upgradeToServerAdmin", "removeServerAdmin"]
|
||||||
|
>,
|
||||||
data?: {
|
data?: {
|
||||||
username: string;
|
username: string;
|
||||||
id: string;
|
id: string;
|
||||||
@@ -58,8 +62,6 @@ const UserPanelTable = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [searchUserFilter, setSearchUserFilter] = useState("");
|
const [searchUserFilter, setSearchUserFilter] = useState("");
|
||||||
const [adminsOnly, setAdminsOnly] = useState(false);
|
const [adminsOnly, setAdminsOnly] = useState(false);
|
||||||
const { user } = useUser();
|
|
||||||
const userId = user?.id || "";
|
|
||||||
const [debouncedSearchTerm] = useDebounce(searchUserFilter, 500);
|
const [debouncedSearchTerm] = useDebounce(searchUserFilter, 500);
|
||||||
const { subscription } = useSubscription();
|
const { subscription } = useSubscription();
|
||||||
|
|
||||||
@@ -143,7 +145,6 @@ const UserPanelTable = ({
|
|||||||
</Td>
|
</Td>
|
||||||
<Td className="w-5/12">{email}</Td>
|
<Td className="w-5/12">{email}</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{userId !== id && (
|
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||||
@@ -178,10 +179,27 @@ const UserPanelTable = ({
|
|||||||
Make User Server Admin
|
Make User Server Admin
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
)}
|
||||||
|
{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>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
);
|
);
|
||||||
@@ -212,11 +230,13 @@ export const UserPanel = () => {
|
|||||||
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||||
"removeUser",
|
"removeUser",
|
||||||
"upgradePlan",
|
"upgradePlan",
|
||||||
"upgradeToServerAdmin"
|
"upgradeToServerAdmin",
|
||||||
|
"removeServerAdmin"
|
||||||
] as const);
|
] as const);
|
||||||
|
|
||||||
const { mutateAsync: deleteUser } = useAdminDeleteUser();
|
const { mutateAsync: deleteUser } = useAdminDeleteUser();
|
||||||
const { mutateAsync: grantAdminAccess } = useAdminGrantServerAdminAccess();
|
const { mutateAsync: grantAdminAccess } = useAdminGrantServerAdminAccess();
|
||||||
|
const { mutateAsync: removeAdminAccess } = useRemoveUserServerAdminAccess();
|
||||||
|
|
||||||
const handleRemoveUser = async () => {
|
const handleRemoveUser = async () => {
|
||||||
const { id } = popUp?.removeUser?.data as { id: string; username: string };
|
const { id } = popUp?.removeUser?.data as { id: string; username: string };
|
||||||
@@ -256,6 +276,25 @@ export const UserPanel = () => {
|
|||||||
handlePopUpClose("upgradeToServerAdmin");
|
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 (
|
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">
|
<div className="mb-4">
|
||||||
@@ -282,6 +321,17 @@ export const UserPanel = () => {
|
|||||||
onDeleteApproved={handleGrantServerAdminAccess}
|
onDeleteApproved={handleGrantServerAdminAccess}
|
||||||
buttonText="Grant Access"
|
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
|
<UpgradePlanModal
|
||||||
isOpen={popUp.upgradePlan.isOpen}
|
isOpen={popUp.upgradePlan.isOpen}
|
||||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||||
|
|||||||
Reference in New Issue
Block a user