Skip to content

Commit

Permalink
fix : tfvars decryption fix
Browse files Browse the repository at this point in the history
  • Loading branch information
psiddharthdesign committed Aug 6, 2024
1 parent a584917 commit d3bafff
Show file tree
Hide file tree
Showing 11 changed files with 799 additions and 332 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
"use client";

import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { T } from "@/components/ui/Typography";
import { addTFVar } from "@/data/admin/env-vars";
import { useSAToastMutation } from "@/hooks/useSAToastMutation";
import { zodResolver } from "@hookform/resolvers/zod";
import { Plus } from "lucide-react";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";




export default function AddTFVarForm({
projectId,
organizationId,
isAllowedSecrets,
envVarNames,
}: {
projectId: string;
organizationId: string;
isAllowedSecrets: boolean;
envVarNames: string[];
}) {
const [showForm, setShowForm] = useState(false);

const addTFVarSchema = z.object({
name: z.string()
.min(1, "Name is required")
.refine((value) => !envVarNames.includes(value), {
message: "Environment variable name already exists",
}),
value: z.string().min(1, "Value is required"),
is_secret: z.boolean(),
});

const { mutate: addEnvVar, isLoading: isAddingEnvVar } = useSAToastMutation(
async (data: z.infer<typeof addTFVarSchema>) =>
addTFVar(data.name, data.value, data.is_secret, projectId, organizationId),
{
loadingMessage: "Adding environment variable...",
successMessage: "Environment variable added!",
errorMessage: "Failed to add environment variable",
onSuccess: () => {
reset();
setShowForm(false);
},
}
)

const { handleSubmit, register, reset, control, watch, formState: { errors } } = useForm<z.infer<typeof addTFVarSchema>>({
resolver: zodResolver(addTFVarSchema),
defaultValues: {
name: '',
value: '',
is_secret: false,
},
});

const onSubmit = (data: z.infer<typeof addTFVarSchema>) => {
addEnvVar(data);
}

return (
<>
{showForm ? (
<Card className="p-5 mt-4 bg-muted/50 rounded-lg">
<T.H4 className=" mt-1">Add a new environment variable</T.H4>
<T.P className="mt-0 pt-0 mb-4">Enter the environment variable details. These variables will be assigned to your project</T.P>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="grid gap-4 grid-cols-1 md:grid-cols-3">
<div className="relative">
<Label htmlFor="varName">Variable Name</Label>
<Input
id="varName"
placeholder="e.g., API_KEY"
{...register('name')}
onChange={(e) => e.target.value = e.target.value.toUpperCase()}
className={errors.name ? "border-destructive" : ""}
/>
{errors.name && <p className="text-destructive text-sm mt-1 absolute -bottom-6">{errors.name.message}</p>}
</div>
<div className="relative">
<Label htmlFor="varValue">Variable Value</Label>
<Controller
name="value"
control={control}
render={({ field }) => (
<Input
id="varValue"
type={watch('is_secret') ? "password" : "text"}
placeholder="Enter value"
{...field}
className={errors.value ? "border-destructive" : ""}
/>
)}
/>
{errors.value && <p className="text-destructive text-sm mt-1 absolute -bottom-6">{errors.value.message}</p>}
</div>
<div>
<Label htmlFor="varType">Variable Type</Label>
<Controller
name="is_secret"
control={control}
render={({ field }) => (
<Select
value={field.value ? "secret" : "plain_text"}
onValueChange={(value) => field.onChange(value === "secret")}
>
<SelectTrigger>
<SelectValue placeholder="Select type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="plain_text">Plain Text</SelectItem>
<SelectItem value="secret">Secret</SelectItem>
</SelectContent>
</Select>
)}
/>
</div>
</div>
<div className="mt-4 flex justify-end w-full">
<Button type="submit" variant='outline' disabled={isAddingEnvVar}>
<Plus className="h-4 w-4 mr-2" />
{isAddingEnvVar ? 'Adding...' : 'Add Variable'}
</Button>
</div>
</form>
</Card>
) : (
<div className="mt-4 flex justify-end w-full" >
<Button onClick={() => setShowForm(true)} variant='outline' disabled={!isAllowedSecrets} >
<Plus className="h-4 w-4 mr-2" />
Add a new environment variable
</Button>
</div>
)}
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client'

import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { bulkUpdateTFVars } from "@/data/admin/env-vars";
import { useSAToastMutation } from "@/hooks/useSAToastMutation";
import { useState } from "react";

export function BulkEditTFVars({ projectId, organizationId, initialBulkEditValue, setIsBulkEditing }: {
projectId: string;
organizationId: string;
initialBulkEditValue: string;
setIsBulkEditing: (value: boolean) => void;
}) {
const [bulkEditValue, setBulkEditValue] = useState(initialBulkEditValue);


const { mutate: bulkEditTFVars, isLoading: isBulkEditingTFVars } = useSAToastMutation(
async (data: string) => bulkUpdateTFVars(data, projectId, organizationId), {
loadingMessage: "Applying bulk edit...",
successMessage: "Bulk edit applied!",
errorMessage: "Failed to apply bulk edit",
onSuccess: (response) => {
setBulkEditValue(JSON.stringify(response, null, 2));
setIsBulkEditing(false);
},
}
)

return (
<div className="space-y-4" >
<Textarea
value={bulkEditValue}
onChange={(e) => setBulkEditValue(e.target.value)}
rows={24}
className="font-mono"
/>
<div className="flex gap-2 w-full justify-between">
<Button variant="outline" onClick={() => setIsBulkEditing(false)}>Cancel</Button>
<Button onClick={() => bulkEditTFVars(bulkEditValue)} disabled={isBulkEditingTFVars}>
{isBulkEditingTFVars ? 'Applying...' : 'Apply Bulk Edit'}
</Button>
</div>
</div >
)
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
'use client';

import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { decryptWithPrivateKey, encryptWithPublicKey, formatKey } from "@/utils/crypto";
import { motion } from 'framer-motion';
import { useState } from 'react';

export default function EncryptionTestComponent({
organizationId,
publicKey: publicKeyFromProps,
}: {
organizationId: string;
publicKey: string | null;
}) {


const [envVar, setEnvVar] = useState('');
const [encryptedVar, setEncryptedVar] = useState('');
const [decryptedVar, setDecryptedVar] = useState('');
const [isMatch, setIsMatch] = useState<boolean | null>(null);


const [publicKey, setPublicKey] = useState<string | null>(publicKeyFromProps || null);
const [privateKey, setPrivateKey] = useState('');

const formattedPublicKey = publicKey ? formatKey(publicKey, 'public') : '';
const formattedPrivateKey = privateKey ? formatKey(privateKey, 'private') : '';


const handleEncrypt = async () => {
try {
if (!formattedPublicKey) {
throw new Error('Public key is not available');
}
console.log('---------------------------------');
console.log('formattedPublicKey', formattedPublicKey);

const encrypted = encryptWithPublicKey(envVar, formattedPublicKey);
console.log('encrypted', encrypted);
setEncryptedVar(encrypted);
} catch (error) {
console.error('Encryption error:', error);
setEncryptedVar('Encryption failed: ' + (error instanceof Error ? error.message : String(error)));
}
};

const handleDecrypt = async () => {
try {
console.log('---------------------------------');
console.log('formattedPrivateKey', formattedPrivateKey);
const decrypted = decryptWithPrivateKey(encryptedVar, formattedPrivateKey);
console.log('decrypted', decrypted);
setDecryptedVar(decrypted);
setIsMatch(decrypted === envVar);
} catch (error) {
console.error('Decryption error:', error);
setDecryptedVar(`Decryption failed: ${error instanceof Error ? error.message : String(error)}`);
setIsMatch(false);
}
};

return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<pre>{JSON.stringify(publicKey, null, 2)}</pre>
<pre>{JSON.stringify(privateKey, null, 2)}</pre>
<Card>
<CardHeader>
<CardTitle>Encryption/Decryption Test</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="envVar">Environment Variable</Label>
<Input
id="envVar"
value={envVar}
onChange={(e) => setEnvVar(e.target.value)}
placeholder="Enter environment variable"
/>
<Button onClick={handleEncrypt} className="mt-2">Encrypt</Button>
</div>

<div>
<Label htmlFor="encryptedVar">Encrypted Variable</Label>
<Input
id="encryptedVar"
value={encryptedVar}
readOnly
placeholder="Encrypted value will appear here"
/>
</div>

<div>
<Label htmlFor="privateKey">Private Key</Label>
<Textarea
id="privateKey"
value={privateKey}
onChange={(e) => setPrivateKey(e.target.value)}
placeholder="Enter private key"
className="w-full h-32 p-2 border rounded"
/>
<Button onClick={handleDecrypt} className="mt-2">Decrypt</Button>
</div>

<div>
<Label htmlFor="decryptedVar">Decrypted Variable</Label>
<Input
id="decryptedVar"
value={decryptedVar}
readOnly
placeholder="Decrypted value will appear here"
/>
{decryptedVar && <p>Decrypted: {decryptedVar}</p>}
{encryptedVar && <p>Encrypted: {encryptedVar}</p>}
</div>

{isMatch !== null && (
<div>
{isMatch
? <p className="text-green-500">The decrypted value matches the original value!</p>
: <p className="text-red-500">The decrypted value does not match the original value.</p>
}
</div>
)}
</CardContent>
</Card>
</motion.div>
);
}
Loading

0 comments on commit d3bafff

Please sign in to comment.