feat(secret-sync): 1Password Secret Sync

This commit is contained in:
x032205
2025-05-22 17:51:09 -04:00
parent 6d509d85f4
commit a64f8ac776
33 changed files with 586 additions and 19 deletions

View File

@@ -0,0 +1,17 @@
import {
CreateOnePassSyncSchema,
OnePassSyncSchema,
UpdateOnePassSyncSchema
} from "@app/services/secret-sync/1password";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
export const registerOnePassSyncRouter = async (server: FastifyZodProvider) =>
registerSyncSecretsEndpoints({
destination: SecretSync.OnePass,
server,
responseSchema: OnePassSyncSchema,
createSchema: CreateOnePassSyncSchema,
updateSchema: UpdateOnePassSyncSchema
});

View File

@@ -1,5 +1,6 @@
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { registerOnePassSyncRouter } from "./1password-sync-router";
import { registerAwsParameterStoreSyncRouter } from "./aws-parameter-store-sync-router";
import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-router";
import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router";
@@ -33,5 +34,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
[SecretSync.Windmill]: registerWindmillSyncRouter,
[SecretSync.HCVault]: registerHCVaultSyncRouter,
[SecretSync.TeamCity]: registerTeamCitySyncRouter,
[SecretSync.OCIVault]: registerOCIVaultSyncRouter
[SecretSync.OCIVault]: registerOCIVaultSyncRouter,
[SecretSync.OnePass]: registerOnePassSyncRouter
};

View File

@@ -5,6 +5,7 @@ import { ApiDocsTags, SecretSyncs } from "@app/lib/api-docs";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { OnePassSyncListItemSchema, OnePassSyncSchema } from "@app/services/secret-sync/1password";
import {
AwsParameterStoreSyncListItemSchema,
AwsParameterStoreSyncSchema
@@ -45,7 +46,8 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
WindmillSyncSchema,
HCVaultSyncSchema,
TeamCitySyncSchema,
OCIVaultSyncSchema
OCIVaultSyncSchema,
OnePassSyncSchema
]);
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
@@ -63,7 +65,8 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
WindmillSyncListItemSchema,
HCVaultSyncListItemSchema,
TeamCitySyncListItemSchema,
OCIVaultSyncListItemSchema
OCIVaultSyncListItemSchema,
OnePassSyncListItemSchema
]);
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {

View File

@@ -0,0 +1,10 @@
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
export const ONEPASS_SYNC_LIST_OPTION: TSecretSyncListItem = {
name: "1Password",
destination: SecretSync.OnePass,
connection: AppConnection.OnePass,
canImportSecrets: true
};

View File

@@ -0,0 +1,226 @@
import { request } from "@app/lib/config/request";
import { getOnePassInstanceUrl } from "@app/services/app-connection/1password";
import {
TDeleteOnePassVariable,
TOnePassListVariables,
TOnePassListVariablesResponse,
TOnePassSyncWithCredentials,
TOnePassVariable,
TOnePassVariableDetails,
TPostOnePassVariable,
TPutOnePassVariable
} from "@app/services/secret-sync/1password/1password-sync-types";
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
const listOnePassItems = async ({ instanceUrl, apiToken, vaultId }: TOnePassListVariables) => {
const { data } = await request.get<TOnePassListVariablesResponse>(`${instanceUrl}/v1/vaults/${vaultId}/items`, {
headers: {
Authorization: `Bearer ${apiToken}`,
Accept: "application/json"
}
});
const result: Record<string, TOnePassVariable & { value: string; fieldId: string }> = {};
for await (const s of data) {
const { data: secret } = await request.get<TOnePassVariableDetails>(
`${instanceUrl}/v1/vaults/${vaultId}/items/${s.id}`,
{
headers: {
Authorization: `Bearer ${apiToken}`,
Accept: "application/json"
}
}
);
const value = secret.fields.find((f) => f.label === "value")?.value;
const fieldId = secret.fields.find((f) => f.label === "value")?.id;
// eslint-disable-next-line no-continue
if (!value || !fieldId) continue;
result[s.title] = {
...secret,
value,
fieldId
};
}
return result;
};
const createOnePassItem = async ({ instanceUrl, apiToken, vaultId, itemTitle, itemValue }: TPostOnePassVariable) => {
return request.post(
`${instanceUrl}/v1/vaults/${vaultId}/items`,
{
title: itemTitle,
category: "API_CREDENTIAL",
vault: {
id: vaultId
},
tags: ["synced-from-infisical"],
fields: [
{
label: "value",
value: itemValue,
type: "CONCEALED"
}
]
},
{
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json"
}
}
);
};
const updateOnePassItem = async ({
instanceUrl,
apiToken,
vaultId,
itemId,
fieldId,
itemTitle,
itemValue
}: TPutOnePassVariable) => {
return request.put(
`${instanceUrl}/v1/vaults/${vaultId}/items/${itemId}`,
{
id: itemId,
title: itemTitle,
category: "API_CREDENTIAL",
vault: {
id: vaultId
},
tags: ["synced-from-infisical"],
fields: [
{
id: fieldId,
label: "value",
value: itemValue,
type: "CONCEALED"
}
]
},
{
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json"
}
}
);
};
const deleteOnePassItem = async ({ instanceUrl, apiToken, vaultId, itemId }: TDeleteOnePassVariable) => {
return request.delete(`${instanceUrl}/v1/vaults/${vaultId}/items/${itemId}`, {
headers: {
Authorization: `Bearer ${apiToken}`
}
});
};
export const OnePassSyncFns = {
syncSecrets: async (secretSync: TOnePassSyncWithCredentials, secretMap: TSecretMap) => {
const {
connection,
destinationConfig: { vaultId }
} = secretSync;
const instanceUrl = await getOnePassInstanceUrl(connection);
const { apiToken } = connection.credentials;
const items = await listOnePassItems({ instanceUrl, apiToken, vaultId });
for await (const entry of Object.entries(secretMap)) {
const [key, { value }] = entry;
try {
if (key in items) {
await updateOnePassItem({
instanceUrl,
apiToken,
vaultId,
itemTitle: key,
itemValue: value,
itemId: items[key].id,
fieldId: items[key].fieldId
});
} else {
await createOnePassItem({ instanceUrl, apiToken, vaultId, itemTitle: key, itemValue: value });
}
} catch (error) {
throw new SecretSyncError({
error,
secretKey: key
});
}
}
if (secretSync.syncOptions.disableSecretDeletion) return;
for await (const [key, variable] of Object.entries(items)) {
// eslint-disable-next-line no-continue
if (!matchesSchema(key, secretSync.syncOptions.keySchema)) continue;
if (!(key in secretMap)) {
try {
await deleteOnePassItem({
instanceUrl,
apiToken,
vaultId,
itemId: variable.id
});
} catch (error) {
throw new SecretSyncError({
error,
secretKey: key
});
}
}
}
},
removeSecrets: async (secretSync: TOnePassSyncWithCredentials, secretMap: TSecretMap) => {
const {
connection,
destinationConfig: { vaultId }
} = secretSync;
const instanceUrl = await getOnePassInstanceUrl(connection);
const { apiToken } = connection.credentials;
const items = await listOnePassItems({ instanceUrl, apiToken, vaultId });
for await (const [key, item] of Object.entries(items)) {
if (key in secretMap) {
try {
await deleteOnePassItem({
apiToken,
vaultId,
instanceUrl,
itemId: item.id
});
} catch (error) {
throw new SecretSyncError({
error,
secretKey: key
});
}
}
}
},
getSecrets: async (secretSync: TOnePassSyncWithCredentials) => {
const {
connection,
destinationConfig: { vaultId }
} = secretSync;
const instanceUrl = await getOnePassInstanceUrl(connection);
const { apiToken } = connection.credentials;
return listOnePassItems({ instanceUrl, apiToken, vaultId });
}
};

View File

@@ -0,0 +1,43 @@
import { z } from "zod";
import { SecretSyncs } from "@app/lib/api-docs";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import {
BaseSecretSyncSchema,
GenericCreateSecretSyncFieldsSchema,
GenericUpdateSecretSyncFieldsSchema
} from "@app/services/secret-sync/secret-sync-schemas";
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
const OnePassSyncDestinationConfigSchema = z.object({
vaultId: z.string().trim().min(1, "Vault required").describe(SecretSyncs.DESTINATION_CONFIG.ONEPASS.vaultId)
});
const OnePassSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
export const OnePassSyncSchema = BaseSecretSyncSchema(SecretSync.OnePass, OnePassSyncOptionsConfig).extend({
destination: z.literal(SecretSync.OnePass),
destinationConfig: OnePassSyncDestinationConfigSchema
});
export const CreateOnePassSyncSchema = GenericCreateSecretSyncFieldsSchema(
SecretSync.OnePass,
OnePassSyncOptionsConfig
).extend({
destinationConfig: OnePassSyncDestinationConfigSchema
});
export const UpdateOnePassSyncSchema = GenericUpdateSecretSyncFieldsSchema(
SecretSync.OnePass,
OnePassSyncOptionsConfig
).extend({
destinationConfig: OnePassSyncDestinationConfigSchema.optional()
});
export const OnePassSyncListItemSchema = z.object({
name: z.literal("1Password"),
connection: z.literal(AppConnection.OnePass),
destination: z.literal(SecretSync.OnePass),
canImportSecrets: z.literal(true)
});

View File

@@ -0,0 +1,54 @@
import { z } from "zod";
import { TOnePassConnection } from "@app/services/app-connection/1password";
import { CreateOnePassSyncSchema, OnePassSyncListItemSchema, OnePassSyncSchema } from "./1password-sync-schemas";
export type TOnePassSync = z.infer<typeof OnePassSyncSchema>;
export type TOnePassSyncInput = z.infer<typeof CreateOnePassSyncSchema>;
export type TOnePassSyncListItem = z.infer<typeof OnePassSyncListItemSchema>;
export type TOnePassSyncWithCredentials = TOnePassSync & {
connection: TOnePassConnection;
};
export type TOnePassVariable = {
id: string;
title: string;
category: string; // API_CREDENTIAL, SECURE_NOTE, LOGIN, etc
};
export type TOnePassVariableDetails = TOnePassVariable & {
fields: {
id: string;
type: string; // CONCEALED, STRING
label: string;
value: string;
}[];
};
export type TOnePassListVariablesResponse = TOnePassVariable[];
export type TOnePassListVariables = {
apiToken: string;
instanceUrl: string;
vaultId: string;
};
export type TPostOnePassVariable = TOnePassListVariables & {
itemTitle: string;
itemValue: string;
};
export type TPutOnePassVariable = TOnePassListVariables & {
itemId: string;
fieldId: string;
itemTitle: string;
itemValue: string;
};
export type TDeleteOnePassVariable = TOnePassListVariables & {
itemId: string;
};

View File

@@ -0,0 +1,4 @@
export * from "./1password-sync-constants";
export * from "./1password-sync-fns";
export * from "./1password-sync-schemas";
export * from "./1password-sync-types";

View File

@@ -13,7 +13,8 @@ export enum SecretSync {
Windmill = "windmill",
HCVault = "hashicorp-vault",
TeamCity = "teamcity",
OCIVault = "oci-vault"
OCIVault = "oci-vault",
OnePass = "1password"
}
export enum SecretSyncInitialSyncBehavior {

View File

@@ -21,6 +21,7 @@ import {
import { TAppConnectionDALFactory } from "../app-connection/app-connection-dal";
import { TKmsServiceFactory } from "../kms/kms-service";
import { ONEPASS_SYNC_LIST_OPTION, OnePassSyncFns } from "./1password";
import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFactory } from "./azure-app-configuration";
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
@@ -50,7 +51,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
[SecretSync.Windmill]: WINDMILL_SYNC_LIST_OPTION,
[SecretSync.HCVault]: HC_VAULT_SYNC_LIST_OPTION,
[SecretSync.TeamCity]: TEAMCITY_SYNC_LIST_OPTION,
[SecretSync.OCIVault]: OCI_VAULT_SYNC_LIST_OPTION
[SecretSync.OCIVault]: OCI_VAULT_SYNC_LIST_OPTION,
[SecretSync.OnePass]: ONEPASS_SYNC_LIST_OPTION
};
export const listSecretSyncOptions = () => {
@@ -171,6 +173,8 @@ export const SecretSyncFns = {
return TeamCitySyncFns.syncSecrets(secretSync, schemaSecretMap);
case SecretSync.OCIVault:
return OCIVaultSyncFns.syncSecrets(secretSync, schemaSecretMap);
case SecretSync.OnePass:
return OnePassSyncFns.syncSecrets(secretSync, schemaSecretMap);
default:
throw new Error(
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
@@ -239,6 +243,9 @@ export const SecretSyncFns = {
case SecretSync.OCIVault:
secretMap = await OCIVaultSyncFns.getSecrets(secretSync);
break;
case SecretSync.OnePass:
secretMap = await OnePassSyncFns.getSecrets(secretSync);
break;
default:
throw new Error(
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
@@ -297,6 +304,8 @@ export const SecretSyncFns = {
return TeamCitySyncFns.removeSecrets(secretSync, schemaSecretMap);
case SecretSync.OCIVault:
return OCIVaultSyncFns.removeSecrets(secretSync, schemaSecretMap);
case SecretSync.OnePass:
return OnePassSyncFns.removeSecrets(secretSync, schemaSecretMap);
default:
throw new Error(
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`

View File

@@ -16,7 +16,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
[SecretSync.Windmill]: "Windmill",
[SecretSync.HCVault]: "Hashicorp Vault",
[SecretSync.TeamCity]: "TeamCity",
[SecretSync.OCIVault]: "OCI Vault"
[SecretSync.OCIVault]: "OCI Vault",
[SecretSync.OnePass]: "1Password"
};
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
@@ -34,5 +35,6 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.Windmill]: AppConnection.Windmill,
[SecretSync.HCVault]: AppConnection.HCVault,
[SecretSync.TeamCity]: AppConnection.TeamCity,
[SecretSync.OCIVault]: AppConnection.OCI
[SecretSync.OCIVault]: AppConnection.OCI,
[SecretSync.OnePass]: AppConnection.OnePass
};

View File

@@ -36,6 +36,12 @@ import {
TWindmillSyncWithCredentials
} from "@app/services/secret-sync/windmill";
import {
TOnePassSync,
TOnePassSyncInput,
TOnePassSyncListItem,
TOnePassSyncWithCredentials
} from "./1password/1password-sync-types";
import {
TAwsParameterStoreSync,
TAwsParameterStoreSyncInput,
@@ -97,7 +103,8 @@ export type TSecretSync =
| TWindmillSync
| THCVaultSync
| TTeamCitySync
| TOCIVaultSync;
| TOCIVaultSync
| TOnePassSync;
export type TSecretSyncWithCredentials =
| TAwsParameterStoreSyncWithCredentials
@@ -114,7 +121,8 @@ export type TSecretSyncWithCredentials =
| TWindmillSyncWithCredentials
| THCVaultSyncWithCredentials
| TTeamCitySyncWithCredentials
| TOCIVaultSyncWithCredentials;
| TOCIVaultSyncWithCredentials
| TOnePassSyncWithCredentials;
export type TSecretSyncInput =
| TAwsParameterStoreSyncInput
@@ -131,7 +139,8 @@ export type TSecretSyncInput =
| TWindmillSyncInput
| THCVaultSyncInput
| TTeamCitySyncInput
| TOCIVaultSyncInput;
| TOCIVaultSyncInput
| TOnePassSyncInput;
export type TSecretSyncListItem =
| TAwsParameterStoreSyncListItem
@@ -148,7 +157,8 @@ export type TSecretSyncListItem =
| TWindmillSyncListItem
| THCVaultSyncListItem
| TTeamCitySyncListItem
| TOCIVaultSyncListItem;
| TOCIVaultSyncListItem
| TOnePassSyncListItem;
export type TSyncOptionsConfig = {
canImportSecrets: boolean;

View File

@@ -0,0 +1,78 @@
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { SingleValue } from "react-select";
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
import { FilterableSelect, FormControl, Tooltip } from "@app/components/v2";
import {
TOnePassVault,
useOnePassConnectionListVaults
} from "@app/hooks/api/appConnections/1password";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { TSecretSyncForm } from "../schemas";
export const OnePassSyncFields = () => {
const { control, setValue } = useFormContext<
TSecretSyncForm & { destination: SecretSync.OnePass }
>();
const connectionId = useWatch({ name: "connection.id", control });
const { data: vaults, isLoading: isVaultsLoading } = useOnePassConnectionListVaults(
connectionId,
{
enabled: Boolean(connectionId)
}
);
return (
<>
<SecretSyncConnectionField
onChange={() => {
setValue("destinationConfig.vaultId", "");
}}
/>
<Controller
name="destinationConfig.vaultId"
control={control}
render={({ field: { value, onChange }, fieldState: { error } }) => (
<FormControl
isError={Boolean(error)}
errorText={error?.message}
label="Vault"
helperText={
<Tooltip
className="max-w-md"
content="Ensure the vault exists in the connection's OnePass instance URL."
>
<div>
<span>Don&#39;t see the vault you&#39;re looking for?</span>{" "}
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
</div>
</Tooltip>
}
>
<FilterableSelect
menuPlacement="top"
isLoading={isVaultsLoading && Boolean(connectionId)}
isDisabled={!connectionId}
value={vaults?.find((v) => v.id === value) ?? null}
onChange={(option) => onChange((option as SingleValue<TOnePassVault>)?.id ?? null)}
options={vaults}
placeholder="Select a vault..."
getOptionLabel={(option) => option.name}
getOptionValue={(option) => option.id}
/>
</FormControl>
)}
/>
<span className="text-sm text-bunker-300">
Not selecting a Build Configuration will sync your secrets to the entire project.
</span>
</>
);
};

View File

@@ -3,6 +3,7 @@ import { useFormContext } from "react-hook-form";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { TSecretSyncForm } from "../schemas";
import { OnePassSyncFields } from "./1PasswordSyncFields";
import { AwsParameterStoreSyncFields } from "./AwsParameterStoreSyncFields";
import { AwsSecretsManagerSyncFields } from "./AwsSecretsManagerSyncFields";
import { AzureAppConfigurationSyncFields } from "./AzureAppConfigurationSyncFields";
@@ -55,6 +56,8 @@ export const SecretSyncDestinationFields = () => {
return <TeamCitySyncFields />;
case SecretSync.OCIVault:
return <OCIVaultSyncFields />;
case SecretSync.OnePass:
return <OnePassSyncFields />;
default:
throw new Error(`Unhandled Destination Config Field: ${destination}`);
}

View File

@@ -49,6 +49,7 @@ export const SecretSyncOptionsFields = ({ hideInitialSync }: Props) => {
case SecretSync.Windmill:
case SecretSync.HCVault:
case SecretSync.TeamCity:
case SecretSync.OnePass:
case SecretSync.OCIVault:
AdditionalSyncOptionsFieldsComponent = null;
break;

View File

@@ -0,0 +1,16 @@
import { useFormContext } from "react-hook-form";
import { TSecretSyncForm } from "@app/components/secret-syncs/forms/schemas";
import { GenericFieldLabel } from "@app/components/v2";
import { SecretSync } from "@app/hooks/api/secretSyncs";
export const OnePassSyncReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm & { destination: SecretSync.OnePass }>();
const vaultId = watch("destinationConfig.vaultId");
return (
<>
<GenericFieldLabel label="Vault ID">{vaultId}</GenericFieldLabel>
</>
);
};

View File

@@ -28,6 +28,7 @@ import { TeamCitySyncReviewFields } from "./TeamCitySyncReviewFields";
import { TerraformCloudSyncReviewFields } from "./TerraformCloudSyncReviewFields";
import { VercelSyncReviewFields } from "./VercelSyncReviewFields";
import { WindmillSyncReviewFields } from "./WindmillSyncReviewFields";
import { OnePassSyncReviewFields } from "./OnePassSyncReviewFields";
export const SecretSyncReviewFields = () => {
const { watch } = useFormContext<TSecretSyncForm>();
@@ -96,6 +97,9 @@ export const SecretSyncReviewFields = () => {
case SecretSync.OCIVault:
DestinationFieldsComponent = <OCIVaultSyncReviewFields />;
break;
case SecretSync.OnePass:
DestinationFieldsComponent = <OnePassSyncReviewFields />;
break;
default:
throw new Error(`Unhandled Destination Review Fields: ${destination}`);
}

View File

@@ -0,0 +1,13 @@
import { z } from "zod";
import { BaseSecretSyncSchema } from "@app/components/secret-syncs/forms/schemas/base-secret-sync-schema";
import { SecretSync } from "@app/hooks/api/secretSyncs";
export const OnePassSyncDestinationSchema = BaseSecretSyncSchema().merge(
z.object({
destination: z.literal(SecretSync.OnePass),
destinationConfig: z.object({
vaultId: z.string().trim().min(1, "Vault ID required")
})
})
);

View File

@@ -1,5 +1,6 @@
import { z } from "zod";
import { OnePassSyncDestinationSchema } from "./1password-sync-destination-schema";
import { AwsParameterStoreSyncDestinationSchema } from "./aws-parameter-store-sync-destination-schema";
import { AwsSecretsManagerSyncDestinationSchema } from "./aws-secrets-manager-sync-destination-schema";
import { AzureAppConfigurationSyncDestinationSchema } from "./azure-app-configuration-sync-destination-schema";
@@ -31,7 +32,8 @@ const SecretSyncUnionSchema = z.discriminatedUnion("destination", [
WindmillSyncDestinationSchema,
HCVaultSyncDestinationSchema,
TeamCitySyncDestinationSchema,
OCIVaultSyncDestinationSchema
OCIVaultSyncDestinationSchema,
OnePassSyncDestinationSchema
]);
export const SecretSyncFormSchema = SecretSyncUnionSchema;

View File

@@ -23,10 +23,10 @@ import {
HumanitecConnectionMethod,
LdapConnectionMethod,
MsSqlConnectionMethod,
OnePassConnectionMethod,
PostgresConnectionMethod,
TAppConnection,
TeamCityConnectionMethod,
OnePassConnectionMethod,
TerraformCloudConnectionMethod,
VercelConnectionMethod,
WindmillConnectionMethod

View File

@@ -51,6 +51,10 @@ export const SECRET_SYNC_MAP: Record<SecretSync, { name: string; image: string }
[SecretSync.OCIVault]: {
name: "OCI Vault",
image: "Oracle.png"
},
[SecretSync.OnePass]: {
name: "1Password",
image: "1Password.png"
}
};
@@ -69,7 +73,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.Windmill]: AppConnection.Windmill,
[SecretSync.HCVault]: AppConnection.HCVault,
[SecretSync.TeamCity]: AppConnection.TeamCity,
[SecretSync.OCIVault]: AppConnection.OCI
[SecretSync.OCIVault]: AppConnection.OCI,
[SecretSync.OnePass]: AppConnection.OnePass
};
export const SECRET_SYNC_INITIAL_SYNC_BEHAVIOR_MAP: Record<

View File

@@ -88,6 +88,10 @@ export type TOCIConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.OCI;
};
export type TOnePassConnectionOption = TAppConnectionOptionBase & {
app: AppConnection.OnePass;
};
export type TAppConnectionOption =
| TAwsConnectionOption
| TGitHubConnectionOption
@@ -106,7 +110,8 @@ export type TAppConnectionOption =
| TAuth0ConnectionOption
| THCVaultConnectionOption
| TTeamCityConnectionOption
| TOCIConnectionOption;
| TOCIConnectionOption
| TOnePassConnectionOption;
export type TAppConnectionOptionMap = {
[AppConnection.AWS]: TAwsConnectionOption;
@@ -128,4 +133,5 @@ export type TAppConnectionOptionMap = {
[AppConnection.LDAP]: TLdapConnectionOption;
[AppConnection.TeamCity]: TTeamCityConnectionOption;
[AppConnection.OCI]: TOCIConnectionOption;
[AppConnection.OnePass]: TOnePassConnectionOption;
};

View File

@@ -1,6 +1,6 @@
import { AppConnection } from "../enums";
import { TAppConnectionOption } from "./app-options";
import { TOnePassConnection } from "./1password-connection";
import { TAppConnectionOption } from "./app-options";
import { TAuth0Connection } from "./auth0-connection";
import { TAwsConnection } from "./aws-connection";
import { TAzureAppConfigurationConnection } from "./azure-app-configuration-connection";

View File

@@ -13,7 +13,8 @@ export enum SecretSync {
Windmill = "windmill",
HCVault = "hashicorp-vault",
TeamCity = "teamcity",
OCIVault = "oci-vault"
OCIVault = "oci-vault",
OnePass = "1password"
}
export enum SecretSyncStatus {

View File

@@ -0,0 +1,15 @@
import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { SecretSync } from "@app/hooks/api/secretSyncs";
import { TRootSecretSync } from "@app/hooks/api/secretSyncs/types/root-sync";
export type TOnePassSync = TRootSecretSync & {
destination: SecretSync.OnePass;
destinationConfig: {
vaultId: string;
};
connection: {
app: AppConnection.OnePass;
name: string;
id: string;
};
};

View File

@@ -1,6 +1,7 @@
import { SecretSync, SecretSyncImportBehavior } from "@app/hooks/api/secretSyncs";
import { DiscriminativePick } from "@app/types";
import { TOnePassSync } from "./1password-sync";
import { TAwsParameterStoreSync } from "./aws-parameter-store-sync";
import { TAwsSecretsManagerSync } from "./aws-secrets-manager-sync";
import { TAzureAppConfigurationSync } from "./azure-app-configuration-sync";
@@ -38,7 +39,8 @@ export type TSecretSync =
| TWindmillSync
| THCVaultSync
| TTeamCitySync
| TOCIVaultSync;
| TOCIVaultSync
| TOnePassSync;
export type TListSecretSyncs = { secretSyncs: TSecretSync[] };

View File

@@ -9,6 +9,7 @@ import { AppConnection } from "@app/hooks/api/appConnections/enums";
import { DiscriminativePick } from "@app/types";
import { AppConnectionHeader } from "../AppConnectionHeader";
import { OnePassConnectionForm } from "./1PasswordConnectionForm";
import { Auth0ConnectionForm } from "./Auth0ConnectionForm";
import { AwsConnectionForm } from "./AwsConnectionForm";
import { AzureAppConfigurationConnectionForm } from "./AzureAppConfigurationConnectionForm";
@@ -25,7 +26,6 @@ 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";

View File

@@ -0,0 +1,14 @@
import { TOnePassSync } from "@app/hooks/api/secretSyncs/types/1password-sync";
import { getSecretSyncDestinationColValues } from "../helpers";
import { SecretSyncTableCell } from "../SecretSyncTableCell";
type Props = {
secretSync: TOnePassSync;
};
export const OnePassSyncDestinationCol = ({ secretSync }: Props) => {
const { primaryText, secondaryText } = getSecretSyncDestinationColValues(secretSync);
return <SecretSyncTableCell primaryText={primaryText} secondaryText={secondaryText} />;
};

View File

@@ -1,5 +1,6 @@
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
import { OnePassSyncDestinationCol } from "./1PasswordSyncDestinationCol";
import { AwsParameterStoreSyncDestinationCol } from "./AwsParameterStoreSyncDestinationCol";
import { AwsSecretsManagerSyncDestinationCol } from "./AwsSecretsManagerSyncDestinationCol";
import { AzureAppConfigurationDestinationSyncCol } from "./AzureAppConfigurationDestinationSyncCol";
@@ -52,6 +53,8 @@ export const SecretSyncDestinationCol = ({ secretSync }: Props) => {
return <TeamCitySyncDestinationCol secretSync={secretSync} />;
case SecretSync.OCIVault:
return <OCIVaultSyncDestinationCol secretSync={secretSync} />;
case SecretSync.OnePass:
return <OnePassSyncDestinationCol secretSync={secretSync} />;
default:
throw new Error(
`Unhandled Secret Sync Destination Col: ${(secretSync as TSecretSync).destination}`

View File

@@ -106,6 +106,10 @@ export const getSecretSyncDestinationColValues = (secretSync: TSecretSync) => {
primaryText = destinationConfig.compartmentOcid;
secondaryText = destinationConfig.vaultOcid;
break;
case SecretSync.OnePass:
primaryText = destinationConfig.vaultId;
secondaryText = "Vault ID";
break;
default:
throw new Error(`Unhandled Destination Col Values ${destination}`);
}

View File

@@ -0,0 +1,14 @@
import { GenericFieldLabel } from "@app/components/secret-syncs";
import { TOnePassSync } from "@app/hooks/api/secretSyncs/types/1password-sync";
type Props = {
secretSync: TOnePassSync;
};
export const OnePassSyncDestinationSection = ({ secretSync }: Props) => {
const {
destinationConfig: { vaultId }
} = secretSync;
return <GenericFieldLabel label="Vault ID">{vaultId}</GenericFieldLabel>;
};

View File

@@ -10,6 +10,7 @@ import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissi
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
import { OnePassSyncDestinationSection } from "./1PasswordSyncDestinationSection";
import { AwsParameterStoreSyncDestinationSection } from "./AwsParameterStoreSyncDestinationSection";
import { AwsSecretsManagerSyncDestinationSection } from "./AwsSecretsManagerSyncDestinationSection";
import { AzureAppConfigurationSyncDestinationSection } from "./AzureAppConfigurationSyncDestinationSection";
@@ -85,6 +86,9 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
case SecretSync.OCIVault:
DestinationComponents = <OCIVaultSyncDestinationSection secretSync={secretSync} />;
break;
case SecretSync.OnePass:
DestinationComponents = <OnePassSyncDestinationSection secretSync={secretSync} />;
break;
default:
throw new Error(`Unhandled Destination Section components: ${destination}`);
}

View File

@@ -50,6 +50,7 @@ export const SecretSyncOptionsSection = ({ secretSync, onEditOptions }: Props) =
case SecretSync.HCVault:
case SecretSync.TeamCity:
case SecretSync.OCIVault:
case SecretSync.OnePass:
AdditionalSyncOptionsComponent = null;
break;
default: