mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 11:29:42 +00:00
feat(app-connections): 1Password App Connection
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
@@ -26,6 +26,7 @@ import {
|
||||
PostgresConnectionMethod,
|
||||
TAppConnection,
|
||||
TeamCityConnectionMethod,
|
||||
OnePassConnectionMethod,
|
||||
TerraformCloudConnectionMethod,
|
||||
VercelConnectionMethod,
|
||||
WindmillConnectionMethod
|
||||
@@ -63,7 +64,8 @@ export const APP_CONNECTION_MAP: Record<
|
||||
[AppConnection.HCVault]: { name: "Hashicorp Vault", image: "Vault.png", size: 65 },
|
||||
[AppConnection.LDAP]: { name: "LDAP", image: "LDAP.png", size: 65 },
|
||||
[AppConnection.TeamCity]: { name: "TeamCity", image: "TeamCity.png" },
|
||||
[AppConnection.OCI]: { name: "OCI", image: "Oracle.png" }
|
||||
[AppConnection.OCI]: { name: "OCI", image: "Oracle.png" },
|
||||
[AppConnection.OnePass]: { name: "1Password", image: "1Password.png" }
|
||||
};
|
||||
|
||||
export const getAppConnectionMethodDetails = (method: TAppConnection["method"]) => {
|
||||
@@ -89,6 +91,7 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
|
||||
case HumanitecConnectionMethod.ApiToken:
|
||||
case TerraformCloudConnectionMethod.ApiToken:
|
||||
case VercelConnectionMethod.ApiToken:
|
||||
case OnePassConnectionMethod.ApiToken:
|
||||
return { name: "API Token", icon: faKey };
|
||||
case PostgresConnectionMethod.UsernameAndPassword:
|
||||
case MsSqlConnectionMethod.UsernameAndPassword:
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./queries";
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useQuery, UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { appConnectionKeys } from "../queries";
|
||||
import { TOnePassVault } from "./types";
|
||||
|
||||
const onePassConnectionKeys = {
|
||||
all: [...appConnectionKeys.all, "1password"] as const,
|
||||
listVaults: (connectionId: string) =>
|
||||
[...onePassConnectionKeys.all, "vaults", connectionId] as const
|
||||
};
|
||||
|
||||
export const useOnePassConnectionListVaults = (
|
||||
connectionId: string,
|
||||
options?: Omit<
|
||||
UseQueryOptions<
|
||||
TOnePassVault[],
|
||||
unknown,
|
||||
TOnePassVault[],
|
||||
ReturnType<typeof onePassConnectionKeys.listVaults>
|
||||
>,
|
||||
"queryKey" | "queryFn"
|
||||
>
|
||||
) => {
|
||||
return useQuery({
|
||||
queryKey: onePassConnectionKeys.listVaults(connectionId),
|
||||
queryFn: async () => {
|
||||
const { data } = await apiRequest.get<TOnePassVault[]>(
|
||||
`/api/v1/app-connections/1password/${connectionId}/vaults`
|
||||
);
|
||||
|
||||
return data;
|
||||
},
|
||||
...options
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
export type TOnePassVault = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: string;
|
||||
items: number;
|
||||
|
||||
attributeVersion: number;
|
||||
contentVersion: number;
|
||||
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
@@ -17,5 +17,6 @@ export enum AppConnection {
|
||||
HCVault = "hashicorp-vault",
|
||||
LDAP = "ldap",
|
||||
TeamCity = "teamcity",
|
||||
OCI = "oci"
|
||||
OCI = "oci",
|
||||
OnePass = "1password"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
import { TRootAppConnection } from "@app/hooks/api/appConnections/types/root-connection";
|
||||
|
||||
export enum OnePassConnectionMethod {
|
||||
ApiToken = "api-token"
|
||||
}
|
||||
|
||||
export type TOnePassConnection = TRootAppConnection & { app: AppConnection.OnePass } & {
|
||||
method: OnePassConnectionMethod.ApiToken;
|
||||
credentials: {
|
||||
apiToken: string;
|
||||
instanceUrl: string;
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AppConnection } from "../enums";
|
||||
import { TAppConnectionOption } from "./app-options";
|
||||
import { TOnePassConnection } from "./1password-connection";
|
||||
import { TAuth0Connection } from "./auth0-connection";
|
||||
import { TAwsConnection } from "./aws-connection";
|
||||
import { TAzureAppConfigurationConnection } from "./azure-app-configuration-connection";
|
||||
@@ -20,6 +21,7 @@ import { TTerraformCloudConnection } from "./terraform-cloud-connection";
|
||||
import { TVercelConnection } from "./vercel-connection";
|
||||
import { TWindmillConnection } from "./windmill-connection";
|
||||
|
||||
export * from "./1password-connection";
|
||||
export * from "./auth0-connection";
|
||||
export * from "./aws-connection";
|
||||
export * from "./azure-app-configuration-connection";
|
||||
@@ -59,7 +61,8 @@ export type TAppConnection =
|
||||
| THCVaultConnection
|
||||
| TLdapConnection
|
||||
| TTeamCityConnection
|
||||
| TOCIConnection;
|
||||
| TOCIConnection
|
||||
| TOnePassConnection;
|
||||
|
||||
export type TAvailableAppConnection = Pick<TAppConnection, "name" | "id">;
|
||||
|
||||
@@ -106,4 +109,5 @@ export type TAppConnectionMap = {
|
||||
[AppConnection.LDAP]: TLdapConnection;
|
||||
[AppConnection.TeamCity]: TTeamCityConnection;
|
||||
[AppConnection.OCI]: TOCIConnection;
|
||||
[AppConnection.OnePass]: TOnePassConnection;
|
||||
};
|
||||
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
Input,
|
||||
ModalClose,
|
||||
SecretInput,
|
||||
Select,
|
||||
SelectItem
|
||||
} from "@app/components/v2";
|
||||
import { APP_CONNECTION_MAP, getAppConnectionMethodDetails } from "@app/helpers/appConnections";
|
||||
import { OnePassConnectionMethod, TOnePassConnection } from "@app/hooks/api/appConnections";
|
||||
import { AppConnection } from "@app/hooks/api/appConnections/enums";
|
||||
|
||||
import {
|
||||
genericAppConnectionFieldsSchema,
|
||||
GenericAppConnectionsFields
|
||||
} from "./GenericAppConnectionFields";
|
||||
|
||||
type Props = {
|
||||
appConnection?: TOnePassConnection;
|
||||
onSubmit: (formData: FormData) => void;
|
||||
};
|
||||
|
||||
const rootSchema = genericAppConnectionFieldsSchema.extend({
|
||||
app: z.literal(AppConnection.OnePass)
|
||||
});
|
||||
|
||||
const formSchema = z.discriminatedUnion("method", [
|
||||
rootSchema.extend({
|
||||
method: z.literal(OnePassConnectionMethod.ApiToken),
|
||||
credentials: z.object({
|
||||
apiToken: z.string().trim().min(1, "API Token required"),
|
||||
instanceUrl: z.string().trim().url("Invalid Connect Server instance URL")
|
||||
})
|
||||
})
|
||||
]);
|
||||
|
||||
type FormData = z.infer<typeof formSchema>;
|
||||
|
||||
export const OnePassConnectionForm = ({ appConnection, onSubmit }: Props) => {
|
||||
const isUpdate = Boolean(appConnection);
|
||||
|
||||
const form = useForm<FormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: appConnection ?? {
|
||||
app: AppConnection.OnePass,
|
||||
method: OnePassConnectionMethod.ApiToken
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { isSubmitting, isDirty }
|
||||
} = form;
|
||||
|
||||
return (
|
||||
<FormProvider {...form}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
{!isUpdate && <GenericAppConnectionsFields />}
|
||||
<Controller
|
||||
name="method"
|
||||
control={control}
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
tooltipText={`The method you would like to use to connect with ${
|
||||
APP_CONNECTION_MAP[AppConnection.OnePass].name
|
||||
}. This field cannot be changed after creation.`}
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Method"
|
||||
>
|
||||
<Select
|
||||
isDisabled={isUpdate}
|
||||
value={value}
|
||||
onValueChange={(val) => onChange(val)}
|
||||
className="w-full border border-mineshaft-500"
|
||||
position="popper"
|
||||
dropdownContainerClassName="max-w-none"
|
||||
>
|
||||
{Object.values(OnePassConnectionMethod).map((method) => {
|
||||
return (
|
||||
<SelectItem value={method} key={method}>
|
||||
{getAppConnectionMethodDetails(method).name}{" "}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="credentials.instanceUrl"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="Instance URL"
|
||||
tooltipClassName="max-w-sm"
|
||||
tooltipText="The URL of the 1Password Connect Server instance to authenticate with."
|
||||
>
|
||||
<Input {...field} placeholder="https://1pass.example.com" />
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="credentials.apiToken"
|
||||
control={control}
|
||||
shouldUnregister
|
||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error?.message)}
|
||||
label="API Token"
|
||||
>
|
||||
<SecretInput
|
||||
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
type="submit"
|
||||
colorSchema="secondary"
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting || !isDirty}
|
||||
>
|
||||
{isUpdate ? "Update Credentials" : "Connect to 1Password"}
|
||||
</Button>
|
||||
<ModalClose asChild>
|
||||
<Button colorSchema="secondary" variant="plain">
|
||||
Cancel
|
||||
</Button>
|
||||
</ModalClose>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
+5
@@ -25,6 +25,7 @@ import { MsSqlConnectionForm } from "./MsSqlConnectionForm";
|
||||
import { OCIConnectionForm } from "./OCIConnectionForm";
|
||||
import { PostgresConnectionForm } from "./PostgresConnectionForm";
|
||||
import { TeamCityConnectionForm } from "./TeamCityConnectionForm";
|
||||
import { OnePassConnectionForm } from "./1PasswordConnectionForm";
|
||||
import { TerraformCloudConnectionForm } from "./TerraformCloudConnectionForm";
|
||||
import { VercelConnectionForm } from "./VercelConnectionForm";
|
||||
import { WindmillConnectionForm } from "./WindmillConnectionForm";
|
||||
@@ -104,6 +105,8 @@ const CreateForm = ({ app, onComplete }: CreateFormProps) => {
|
||||
return <TeamCityConnectionForm onSubmit={onSubmit} />;
|
||||
case AppConnection.OCI:
|
||||
return <OCIConnectionForm onSubmit={onSubmit} />;
|
||||
case AppConnection.OnePass:
|
||||
return <OnePassConnectionForm onSubmit={onSubmit} />;
|
||||
default:
|
||||
throw new Error(`Unhandled App ${app}`);
|
||||
}
|
||||
@@ -178,6 +181,8 @@ const UpdateForm = ({ appConnection, onComplete }: UpdateFormProps) => {
|
||||
return <TeamCityConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
case AppConnection.OCI:
|
||||
return <OCIConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
case AppConnection.OnePass:
|
||||
return <OnePassConnectionForm onSubmit={onSubmit} appConnection={appConnection} />;
|
||||
|
||||
default:
|
||||
throw new Error(`Unhandled App ${(appConnection as TAppConnection).app}`);
|
||||
|
||||
Reference in New Issue
Block a user