Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/admin/password input masking #825

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions ui/admin/app/components/model-providers/ModelProviderForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ControlledInput } from "~/components/form/controlledInputs";
import {
ModelProviderConfigurationLinks,
ModelProviderRequiredTooltips,
ModelProviderSensitiveFields,
} from "~/components/model-providers/constants";
import { Alert, AlertDescription, AlertTitle } from "~/components/ui/alert";
import { Button } from "~/components/ui/button";
Expand Down Expand Up @@ -211,26 +212,34 @@ export function ModelProviderForm({
className="flex flex-col gap-4"
>
{requiredConfigParamFields.fields.map(
(field, i) => (
<div
key={field.id}
className="flex gap-2 items-center justify-center"
>
<ControlledInput
(field, i) => {
const type = ModelProviderSensitiveFields[
field.name
]
? "password"
: "text";

return (
<div
key={field.id}
label={renderLabelWithTooltip(
field.label
)}
control={form.control}
name={`requiredConfigParams.${i}.value`}
type="password"
classNames={{
wrapper:
"flex-auto bg-background",
}}
/>
</div>
)
className="flex gap-2 items-center justify-center"
>
<ControlledInput
key={field.id}
label={renderLabelWithTooltip(
field.label
)}
control={form.control}
name={`requiredConfigParams.${i}.value`}
type={type}
classNames={{
wrapper:
"flex-auto bg-background",
}}
/>
</div>
);
}
)}
</form>
</Form>
Expand Down
20 changes: 20 additions & 0 deletions ui/admin/app/components/model-providers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,23 @@ export const ModelProviderRequiredTooltips: {
"Container that holds related Azure resources. Can typically be found in Azure Portal > Resource Groups > [OpenAI Resource Group] > Overview",
},
};

export const ModelProviderSensitiveFields: Record<string, boolean | undefined> =
{
// OpenAI
OTTO8_OPENAI_MODEL_PROVIDER_API_KEY: true,

// Azure OpenAI
OTTO8_AZURE_OPENAI_MODEL_PROVIDER_ENDPOINT: false,
OTTO8_AZURE_OPENAI_MODEL_PROVIDER_CLIENT_ID: false,
OTTO8_AZURE_OPENAI_MODEL_PROVIDER_CLIENT_SECRET: true,
OTTO8_AZURE_OPENAI_MODEL_PROVIDER_TENANT_ID: false,
OTTO8_AZURE_OPENAI_MODEL_PROVIDER_SUBSCRIPTION_ID: false,
OTTO8_AZURE_OPENAI_MODEL_PROVIDER_RESOURCE_GROUP: false,

// Anthropic
OTTO8_ANTHROPIC_MODEL_PROVIDER_API_KEY: true,

// Voyage
OTTO8_VOYAGE_MODEL_PROVIDER_API_KEY: true,
};
62 changes: 51 additions & 11 deletions ui/admin/app/components/ui/input.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { type VariantProps, cva } from "class-variance-authority";
import { EyeIcon, EyeOffIcon } from "lucide-react";
import * as React from "react";

import { cn } from "~/lib/utils";

import { buttonVariants } from "~/components/ui/button";

const inputVariants = cva(
"flex h-9 w-full rounded-md px-3 bg-transparent border border-input text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
cn(
"flex items-center h-9 w-full rounded-md bg-transparent border border-input text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-ring has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50"
),
{
variants: {
variant: {
default: "",
ghost: "shadow-none cursor-pointer hover:border-primary px-0 mb-0 font-bold outline-none border-transparent focus:border-primary",
ghost: "shadow-none cursor-pointer hover:border-primary px-0 mb-0 font-bold outline-none border-transparent has-[:focus-visible]:border-primary",
},
},
defaultVariants: {
Expand All @@ -20,18 +25,53 @@ const inputVariants = cva(

export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement>,
VariantProps<typeof inputVariants> {}
VariantProps<typeof inputVariants> {
disableToggle?: boolean;
}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, variant, type, ...props }, ref) => {
({ className, variant, type, disableToggle = false, ...props }, ref) => {
const isPassword = type === "password";
const [isVisible, setIsVisible] = React.useState(false);

const internalType = isPassword
? isVisible && !disableToggle
? "text"
: "password"
: type;

const toggleVisible = React.useCallback(
() => setIsVisible((prev) => !prev),
[]
);

return (
<input
type={type}
data-1p-ignore={type !== "password"}
className={cn(inputVariants({ variant, className }))}
ref={ref}
{...props}
/>
<div className={cn(inputVariants({ variant, className }))}>
<input
type={internalType}
data-1p-ignore={!isPassword}
className={cn(
"w-full p-3 bg-transparent border-none focus-visible:border-none focus-visible:outline-none rounded-full"
)}
ref={ref}
{...props}
/>

{isPassword && !disableToggle && (
<button
type="button"
onClick={toggleVisible}
className={buttonVariants({
variant: "ghost",
size: "icon",
shape: "default",
className: "min-h-full !h-full rounded-s-none",
})}
>
{!isVisible ? <EyeIcon /> : <EyeOffIcon />}
</button>
)}
</div>
);
}
);
Expand Down