Skip to content

Commit

Permalink
Add first pass at public keys addition to access key management
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonesy committed May 4, 2023
1 parent 581da54 commit 522cb37
Show file tree
Hide file tree
Showing 11 changed files with 517 additions and 36 deletions.
4 changes: 4 additions & 0 deletions src/mocks/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
fullfillRequestHandler,
gatewayServicesHandler,
getAccessRequestsHandler,
getAccessRequestForm,
getAllConsumerGroupLabelsHandler,
getConsumersHandler,
getConsumerHandler,
Expand Down Expand Up @@ -73,6 +74,7 @@ import { handleAllDatasets } from './resolvers/datasets';
import {
createServiceAccountHandler,
getAllServiceAccountsHandler,
getMyServiceAccessHandlers,
} from './resolvers/service-accounts';
import {
allServicesHandler,
Expand Down Expand Up @@ -229,6 +231,7 @@ export const handlers = [
keystone.query('GetAccessRequests', getAccessRequestsHandler),
keystone.query('GetConsumerEditDetails', getConsumerProdEnvAccessHandler),
keystone.query('GetAccessRequestAuth', accessRequestAuthHandler),
keystone.query('GetAccessRequestForm', getAccessRequestForm),
keystone.query('GetFilterConsumers', getConsumersFilterHandler),
keystone.query('GetAllConsumerGroupLabels', getAllConsumerGroupLabelsHandler),
keystone.query('GetControlContent', gatewayServicesHandler),
Expand Down Expand Up @@ -268,6 +271,7 @@ export const handlers = [
keystone.query('GetMetrics', getMetricsHandler),
// Service accounts
keystone.query('GetAllServiceAccounts', getAllServiceAccountsHandler),
keystone.query('GetMyServiceAccesses', getMyServiceAccessHandlers),
keystone.query('GetGatewayService', getGatewayServiceHandler),
keystone.query('GetGatewayServiceFilters', getGatewayServiceFilterHandler),
keystone.mutation('CreateServiceAccount', createServiceAccountHandler),
Expand Down
67 changes: 67 additions & 0 deletions src/mocks/resolvers/consumers.js
Original file line number Diff line number Diff line change
Expand Up @@ -607,3 +607,70 @@ export const getAllConsumerGroupLabelsHandler = (_, res, ctx) => {
})
);
};

export const getAccessRequestForm = (_, res, ctx) => {
return res(
ctx.data({
allProductsByNamespace: [],
allDiscoverableProducts: [
{
id: 'p1',
name: 'eRX Demo API',
environments: [
{
id: 'e1',
approval: true,
name: 'dev',
active: true,
flow: 'kong-api-key-acl',
additionalDetailsToRequest: '',
legal: {
title: 'Terms of Use for API Gateway',
description: null,
link:
'https://www2.gov.bc.ca/gov/content/data/open-data/api-terms-of-use-for-ogl-information',
reference: 'terms-of-use-for-api-gateway-1',
},
credentialIssuer: null,
},
{
id: 'e2',
approval: true,
name: 'prod',
active: true,
flow: 'client-credentials',
additionalDetailsToRequest: '',
legal: null,
credentialIssuer: {
clientAuthenticator: 'client-jwt-jwks-url',
},
},
],
},
],
myApplications: [
{
id: '111',
appId: 'appID1111',
name: 'Demo App',
owner: {
name: 'XT:Jones, Joshua CITZ:IN',
},
},
],
mySelf: {
legalsAgreed:
'[{"reference":"terms-of-use-for-api-gateway-1","agreedTimestamp":"2023-05-01T18:02:22.973Z"}]',
},
allTemporaryIdentities: [
{
id: 'temp1',
userId: '2',
name: 'XT:Jones, Joshua CITZ:IN',
providerUsername: 'JOSHJONE',
email: 'joshua@general-metrics.com',
},
],
})
);
};
119 changes: 100 additions & 19 deletions src/mocks/resolvers/service-accounts.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { v4 as uuidv4 } from 'uuid';
import { v4 as uuidv4 } from "uuid";

const allNamespaceServiceAccounts = [
{
id: '1',
name: 'sa-sampler-ca33333-3202ij0iwef',
createdAt: '2022-09-21T18:20:55.655Z',
id: "1",
name: "sa-sampler-ca33333-3202ij0iwef",
createdAt: "2022-09-21T18:20:55.655Z",
},
{
id: '2',
name: 'sa-sampler-ca44444-123iej20dj2',
createdAt: '2021-10-25T19:30:47.422Z',
id: "2",
name: "sa-sampler-ca44444-123iej20dj2",
createdAt: "2021-10-25T19:30:47.422Z",
},
{
id: '3',
name: 'sa-sampler-asfadf-asdfasdf',
createdAt: '2021-10-19T17:56:43.786Z',
id: "3",
name: "sa-sampler-asfadf-asdfasdf",
createdAt: "2021-10-19T17:56:43.786Z",
},
{
id: '4',
name: 'sa-sampler-asdfkjsaldf',
createdAt: '2021-07-10T18:09:11.555Z',
id: "4",
name: "sa-sampler-asdfkjsaldf",
createdAt: "2021-07-10T18:09:11.555Z",
},
];
export const getAllServiceAccountsHandler = (req, res, ctx) => {
Expand All @@ -28,11 +28,11 @@ export const getAllServiceAccountsHandler = (req, res, ctx) => {
allNamespaceServiceAccounts,
allTemporaryIdentities: [
{
id: '1',
userId: 'u1',
id: "1",
userId: "u1",
},
],
})
}),
);
};

Expand All @@ -50,13 +50,94 @@ export const createServiceAccountHandler = (req, res, ctx) => {
createServiceAccount: {
...record,
credentials: JSON.stringify({
flow: 'client-credentials',
flow: "client-credentials",
clientId: record.name,
clientSecret: uuidv4(),
tokenEndpoint:
'https://host.gov.bc.ca/auth/realms/aps/protocol/openid-connect/token',
"https://host.gov.bc.ca/auth/realms/aps/protocol/openid-connect/token",
}),
},
})
}),
);
};

export const getMyServiceAccessHandlers = (_, res, ctx) => {
return res(
ctx.data({
myServiceAccesses: [
{
id: "1581",
name: "BE2BD769-5BDA36EB527",
active: false,
application: {
name: "Demo App",
},
productEnvironment: {
id: "175",
name: "dev",
flow: "kong-api-key-acl",
product: {
id: "166",
name: "eRX Demo API",
},
clientId: "asdfjkl1231qwerty",
},
},
],
myAccessRequests: [
{
id: "111",
name: "aiofja90sdfja0s9fj",
active: true,
application: {
name: "Shoppers Drug Mart 123",
},
productEnvironment: {
id: "175",
name: "dev",
flow: "client-jwt-jwks-url",
product: {
id: "166",
name: "Demo App",
},
clientId: "asdfjkl1231qwerty",
},
controls: JSON.stringify({
clientGenCertificate: true,
jwksUrl: "https://example.com/.well-known/jwks.json",
}),
isComplete: true,
isApproved: true,
isIssued: true,
},
{
id: "211",
name: "asf9oas0f9jwf",
active: true,
application: {
name: "Shoppers Drug Mart 456",
},
productEnvironment: {
id: "175",
name: "dev",
flow: "client-jwt-jwks-url",
product: {
id: "166",
name: "Demo App",
},
clientId: "qwertyasdfjkl1231",
},
controls: JSON.stringify({
clientGenCertificate: true,
publicKey: `-----BEGIN PUBLIC KEY-----
asdf0jas0dfja0sdfj0as9dfja0sd9fj09sdfj0asjdf0as9jf09asjf0a9sjfa0s9djf0asdjf0asdjf0asdjy/aosdf0oaisdfj0a9sdfj+0a9sdf0a9jfoasdjf0asdjf+a0osdifja-sdfj+)9uasdfjlaojksdjfoasdfj+oajsd0fiajsd0f/laksdjflasdkfj
2Q1AGNYP8cZOQ9NzNnIYsGTsHw8GvDn6l/b1N+aklsjdfoasdjf0oaisdjf0asidfj/asodfj0asidfj.asdf0jasdf/asodifjas0d-pfj/asddofijas0dfj/asdopija0opsdjB
-----END PUBLIC KEY-----`,
}),
isComplete: true,
isApproved: true,
isIssued: true,
},
],
}),
);
};
1 change: 1 addition & 0 deletions src/nextapp/components/access-list/access-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const AccessListItem: React.FC<AccessListItemProps> = ({
<Th>Status</Th>
<Th>Environments</Th>
<Th>Application</Th>
<Th>Client ID</Th>
<Th />
</Tr>
</Thead>
Expand Down
33 changes: 32 additions & 1 deletion src/nextapp/components/access-list/access-list-row.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import {
Flex,
Icon,
IconButton,
Menu,
Expand All @@ -12,12 +13,15 @@ import {
useDisclosure,
} from '@chakra-ui/react';
import has from 'lodash/has';
import { IoCopy } from 'react-icons/io5';
import type { ServiceAccess, AccessRequest } from '@/shared/types/query.types';

import AccessStatus from './access-status';
import GenerateCredentialsDialog from '../access-request-form/generate-credentials-dialog';
import RegenerateCredentialsDialog from '../access-request-form/regenerate-credentials-dialog';
import { IoEllipsisHorizontal } from 'react-icons/io5';
import JwksDialog from '../access-request-form/jwks-dialog';
import PublicKeyDialog from '../access-request-form/public-key-dialog';

interface AccessListRowProps {
data: AccessRequest & ServiceAccess;
Expand All @@ -31,6 +35,8 @@ const AccessListRow: React.FC<AccessListRowProps> = ({
onRevoke,
}) => {
const { isOpen, onClose, onOpen } = useDisclosure();
const jwksDialog = useDisclosure();
const publicKeyDialog = useDisclosure();
const handleRevoke = React.useCallback(
(id, isRequest) => async (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
Expand All @@ -40,6 +46,7 @@ const AccessListRow: React.FC<AccessListRowProps> = ({
},
[onRevoke]
);
const controls = data?.controls ? JSON.parse(data.controls) : {};

return (
<Tr>
Expand All @@ -60,6 +67,7 @@ const AccessListRow: React.FC<AccessListRowProps> = ({
</Tag>
</Td>
<Td>{data.application?.name}</Td>
<Td color="bc-blue">{data.productEnvironment.clientId}</Td>
<Td isNumeric data-testid={`access-generate-credentials-${index}`}>
{(has(data, 'isApproved') ||
has(data, 'isIssued') ||
Expand All @@ -72,6 +80,8 @@ const AccessListRow: React.FC<AccessListRowProps> = ({
isOpen={isOpen}
onClose={onClose}
/>
<JwksDialog id={data.id} isOpen={jwksDialog.isOpen} onClose={jwksDialog.onClose}/>
<PublicKeyDialog id={data.id} isOpen={publicKeyDialog.isOpen} onClose={publicKeyDialog.onClose}/>
<Menu placement="bottom-end">
<MenuButton
as={IconButton}
Expand All @@ -86,7 +96,28 @@ const AccessListRow: React.FC<AccessListRowProps> = ({
'kong-api-key-acl',
'client-credentials',
].includes(data.productEnvironment.flow) && (
<MenuItem data-testid="regenerate-credentials-btn" onClick={onOpen}>Regenerate Credentials</MenuItem>
<MenuItem
data-testid="regenerate-credentials-btn"
onClick={onOpen}
>
Regenerate Credentials
</MenuItem>
)}
{data.productEnvironment?.flow === 'client-jwt-jwks-url' && controls.jwksUrl && (
<MenuItem
data-testid="regenerate-credentials-btn"
onClick={jwksDialog.onOpen}
>
Update JWKS URL
</MenuItem>
)}
{data.productEnvironment?.flow === 'client-jwt-jwks-url' && controls.publicKey && (
<MenuItem
data-testid="regenerate-credentials-btn"
onClick={publicKeyDialog.onOpen}
>
Update Public Key
</MenuItem>
)}
<MenuItem
color="bc-error"
Expand Down
Loading

0 comments on commit 522cb37

Please sign in to comment.