gRPC authentication

This commit is contained in:
Gregory Schier
2024-02-17 23:47:28 -08:00
parent 5565a9db9a
commit d61b22dd87
16 changed files with 246 additions and 123 deletions

View File

@@ -1,49 +1,64 @@
import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest';
import { useUpdateHttpRequest } from '../hooks/useUpdateHttpRequest';
import type { HttpRequest } from '../lib/models';
import type { GrpcRequest, HttpRequest } from '../lib/models';
import { Input } from './core/Input';
import { VStack } from './core/Stacks';
interface Props {
requestId: string;
authentication: HttpRequest['authentication'];
interface Props<T> {
request: T;
}
export function BasicAuth({ requestId, authentication }: Props) {
const updateRequest = useUpdateHttpRequest(requestId);
export function BasicAuth<T extends HttpRequest | GrpcRequest>({ request }: Props<T>) {
const updateHttpRequest = useUpdateHttpRequest(request.id);
const updateGrpcRequest = useUpdateGrpcRequest(request.id);
return (
<VStack className="my-2" space={2}>
<Input
useTemplating
autocompleteVariables
forceUpdateKey={requestId}
forceUpdateKey={request.id}
placeholder="username"
label="Username"
name="username"
size="sm"
defaultValue={`${authentication.username}`}
defaultValue={`${request.authentication.username}`}
onChange={(username: string) => {
updateRequest.mutate((r) => ({
...r,
authentication: { password: r.authentication.password, username },
}));
if (request.model === 'http_request') {
updateHttpRequest.mutate((r) => ({
...r,
authentication: { password: r.authentication.password, username },
}));
} else {
updateGrpcRequest.mutate((r) => ({
...r,
authentication: { password: r.authentication.password, username },
}));
}
}}
/>
<Input
useTemplating
autocompleteVariables
forceUpdateKey={requestId}
forceUpdateKey={request?.id}
placeholder="password"
label="Password"
name="password"
size="sm"
type="password"
defaultValue={`${authentication.password}`}
defaultValue={`${request.authentication.password}`}
onChange={(password: string) => {
updateRequest.mutate((r) => ({
...r,
authentication: { username: r.authentication.username, password },
}));
if (request.model === 'http_request') {
updateHttpRequest.mutate((r) => ({
...r,
authentication: { username: r.authentication.username, password },
}));
} else {
updateGrpcRequest.mutate((r) => ({
...r,
authentication: { username: r.authentication.username, password },
}));
}
}}
/>
</VStack>

View File

@@ -1,15 +1,16 @@
import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest';
import { useUpdateHttpRequest } from '../hooks/useUpdateHttpRequest';
import type { HttpRequest } from '../lib/models';
import type { GrpcRequest, HttpRequest } from '../lib/models';
import { Input } from './core/Input';
import { VStack } from './core/Stacks';
interface Props {
requestId: string;
authentication: HttpRequest['authentication'];
interface Props<T> {
request: T;
}
export function BearerAuth({ requestId, authentication }: Props) {
const updateRequest = useUpdateHttpRequest(requestId);
export function BearerAuth<T extends HttpRequest | GrpcRequest>({ request }: Props<T>) {
const updateHttpRequest = useUpdateHttpRequest(request?.id ?? null);
const updateGrpcRequest = useUpdateGrpcRequest(request?.id ?? null);
return (
<VStack className="my-2" space={2}>
@@ -21,12 +22,19 @@ export function BearerAuth({ requestId, authentication }: Props) {
label="Token"
name="token"
size="sm"
defaultValue={`${authentication.token}`}
defaultValue={`${request.authentication.token}`}
onChange={(token: string) => {
updateRequest.mutate((r) => ({
...r,
authentication: { token },
}));
if (request.model === 'http_request') {
updateHttpRequest.mutate((r) => ({
...r,
authentication: { token },
}));
} else {
updateGrpcRequest.mutate((r) => ({
...r,
authentication: { token },
}));
}
}}
/>
</VStack>

View File

@@ -80,10 +80,7 @@ export function GrpcConnectionLayout({ style }: Props) {
style={style}
activeRequest={activeRequest}
methodType={methodType}
onUnary={grpc.unary.mutate}
onServerStreaming={grpc.serverStreaming.mutate}
onClientStreaming={grpc.clientStreaming.mutate}
onStreaming={grpc.streaming.mutate}
onGo={grpc.go.mutate}
onCommit={grpc.commit.mutate}
onCancel={grpc.cancel.mutate}
onSend={grpc.send.mutate}
@@ -93,7 +90,7 @@ export function GrpcConnectionLayout({ style }: Props) {
/>
)}
secondSlot={({ style }) =>
!grpc.unary.isLoading && (
!grpc.go.isLoading && (
<div
style={style}
className={classNames(
@@ -102,9 +99,9 @@ export function GrpcConnectionLayout({ style }: Props) {
'shadow shadow-gray-100 dark:shadow-gray-0 relative',
)}
>
{grpc.unary.error ? (
{grpc.go.error ? (
<Banner color="danger" className="m-2">
{grpc.unary.error}
{grpc.go.error}
</Banner>
) : messages.length >= 0 ? (
<GrpcConnectionMessagesPane activeRequest={activeRequest} methodType={methodType} />

View File

@@ -5,9 +5,12 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
import { createGlobalState } from 'react-use';
import type { ReflectResponseService } from '../hooks/useGrpc';
import { useGrpcConnections } from '../hooks/useGrpcConnections';
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
import { useUpdateGrpcRequest } from '../hooks/useUpdateGrpcRequest';
import type { GrpcRequest } from '../lib/models';
import { AUTH_TYPE_BASIC, AUTH_TYPE_BEARER, AUTH_TYPE_NONE } from '../lib/models';
import { BasicAuth } from './BasicAuth';
import { BearerAuth } from './BearerAuth';
import { Button } from './core/Button';
import { Icon } from './core/Icon';
import { IconButton } from './core/IconButton';
@@ -15,6 +18,7 @@ import { RadioDropdown } from './core/RadioDropdown';
import { HStack, VStack } from './core/Stacks';
import type { TabItem } from './core/Tabs/Tabs';
import { TabContent, Tabs } from './core/Tabs/Tabs';
import { EmptyStateText } from './EmptyStateText';
import { GrpcEditor } from './GrpcEditor';
import { UrlBar } from './UrlBar';
@@ -31,13 +35,10 @@ interface Props {
| 'streaming'
| 'no-schema'
| 'no-method';
onUnary: () => void;
onCommit: () => void;
onCancel: () => void;
onSend: (v: { message: string }) => void;
onClientStreaming: () => void;
onServerStreaming: () => void;
onStreaming: () => void;
onGo: () => void;
services: ReflectResponseService[] | null;
}
@@ -50,19 +51,17 @@ export function GrpcConnectionSetupPane({
activeRequest,
reflectionError,
reflectionLoading,
onStreaming,
onClientStreaming,
onServerStreaming,
onGo,
onCommit,
onCancel,
onSend,
onUnary,
}: Props) {
const connections = useGrpcConnections(activeRequest.id ?? null);
const updateRequest = useUpdateGrpcRequest(activeRequest?.id ?? null);
const activeConnection = connections[0] ?? null;
const isStreaming = activeConnection?.elapsed === 0;
const [activeTab, setActiveTab] = useActiveTab();
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
const [paneSize, setPaneSize] = useState(99999);
const urlContainerEl = useRef<HTMLDivElement>(null);
@@ -116,17 +115,9 @@ export function GrpcConnectionSetupPane({
body: 'Service or method not selected',
});
}
if (methodType === 'streaming') {
onStreaming();
} else if (methodType === 'server_streaming') {
onServerStreaming();
} else if (methodType === 'client_streaming') {
onClientStreaming();
} else {
onUnary();
}
onGo();
},
[activeRequest, methodType, onStreaming, onServerStreaming, onClientStreaming, onUnary],
[activeRequest, onGo],
);
const tabs: TabItem[] = useMemo(
@@ -136,7 +127,7 @@ export function GrpcConnectionSetupPane({
value: 'auth',
label: 'Auth',
options: {
value: AUTH_TYPE_NONE, // TODO
value: activeRequest.authenticationType,
items: [
{ label: 'Basic Auth', shortLabel: 'Basic', value: AUTH_TYPE_BASIC },
{ label: 'Bearer Token', shortLabel: 'Bearer', value: AUTH_TYPE_BEARER },
@@ -144,13 +135,24 @@ export function GrpcConnectionSetupPane({
{ label: 'No Authentication', shortLabel: 'Auth', value: AUTH_TYPE_NONE },
],
onChange: async (authenticationType) => {
// TODO
let authentication: GrpcRequest['authentication'] = activeRequest.authentication;
if (authenticationType === AUTH_TYPE_BASIC) {
authentication = {
username: authentication.username ?? '',
password: authentication.password ?? '',
};
} else if (authenticationType === AUTH_TYPE_BEARER) {
authentication = {
token: authentication.token ?? '',
};
}
await updateRequest.mutateAsync({ authenticationType, authentication });
},
},
},
{ value: 'metadata', label: 'Metadata' },
],
[],
[activeRequest.authentication, activeRequest.authenticationType, updateRequest],
);
return (
@@ -166,7 +168,7 @@ export function GrpcConnectionSetupPane({
url={activeRequest.url ?? ''}
method={null}
submitIcon={null}
forceUpdateKey={activeRequest?.id ?? ''}
forceUpdateKey={forceUpdateKey}
placeholder="localhost:50051"
onSubmit={handleConnect}
onUrlChange={handleChangeUrl}
@@ -270,6 +272,15 @@ export function GrpcConnectionSetupPane({
request={activeRequest}
/>
</TabContent>
<TabContent value="auth">
{activeRequest.authenticationType === AUTH_TYPE_BASIC ? (
<BasicAuth key={forceUpdateKey} request={activeRequest} />
) : activeRequest.authenticationType === AUTH_TYPE_BEARER ? (
<BearerAuth key={forceUpdateKey} request={activeRequest} />
) : (
<EmptyStateText>No Authentication {activeRequest.authenticationType}</EmptyStateText>
)}
</TabContent>
</Tabs>
</VStack>
);

View File

@@ -154,7 +154,7 @@ export const RequestPane = memo(function RequestPane({
token: authentication.token ?? '',
};
}
updateRequest.mutate({ authenticationType, authentication });
await updateRequest.mutateAsync({ authenticationType, authentication });
},
},
},
@@ -226,17 +226,9 @@ export const RequestPane = memo(function RequestPane({
>
<TabContent value="auth">
{activeRequest.authenticationType === AUTH_TYPE_BASIC ? (
<BasicAuth
key={forceUpdateKey}
requestId={activeRequest.id}
authentication={activeRequest.authentication}
/>
<BasicAuth key={forceUpdateKey} request={activeRequest} />
) : activeRequest.authenticationType === AUTH_TYPE_BEARER ? (
<BearerAuth
key={forceUpdateKey}
requestId={activeRequest.id}
authentication={activeRequest.authentication}
/>
<BearerAuth key={forceUpdateKey} request={activeRequest} />
) : (
<EmptyStateText>
No Authentication {activeRequest.authenticationType}

View File

@@ -82,7 +82,7 @@ export function Tabs({
{tabs.map((t) => {
const isActive = t.value === value;
const btnClassName = classNames(
isActive ? '' : 'text-gray-600 hover:text-gray-800',
isActive ? 'text-gray-800' : 'text-gray-600 hover:text-gray-700',
'!px-2 ml-[1px]',
);