diff --git a/app/lib/kafka.server.ts b/app/lib/kafka.server.ts index 0943877d8..da1632a01 100644 --- a/app/lib/kafka.server.ts +++ b/app/lib/kafka.server.ts @@ -9,7 +9,6 @@ import { tables } from '@architect/functions' import { paginateScan } from '@aws-sdk/lib-dynamodb' import type { DynamoDBDocument } from '@aws-sdk/lib-dynamodb' import crypto from 'crypto' -import type { AclFilter } from 'gcn-kafka' import { Kafka } from 'gcn-kafka' import type { AclEntry } from 'kafkajs' import { @@ -113,101 +112,6 @@ function validateUser(user: User) { throw new Response(null, { status: 403 }) } -export async function createKafkaACL( - user: User, - userClientType: UserClientType, - resourceName: string, - group: string, - permissionType: number, - resourceType: number, - includePrefixed: boolean -) { - const acls: KafkaACL[] = - userClientType == 'consumer' - ? consumerOperations.map((operation) => { - return { - resourceName, - principal: `User:${group}`, - host: '*', - operation, - permissionType, - resourcePatternType: 3, - resourceType, - } - }) - : producerOperations.map((operation) => { - return { - resourceName, - principal: `User:${group}`, - host: '*', - operation, - permissionType, - resourcePatternType: 3, - resourceType, - } - }) - - if (includePrefixed) { - const prefixedAcls = - userClientType === 'consumer' - ? consumerOperations.map((operation) => { - return { - resourceName, - principal: `User:${group}`, - host: '*', - operation, - permissionType, - resourcePatternType: 4, - resourceType, - } - }) - : producerOperations.map((operation) => { - return { - resourceName, - principal: `User:${group}`, - host: '*', - operation, - permissionType, - resourcePatternType: 4, - resourceType, - } - }) - acls.push(...prefixedAcls) - } - - await createKafkaACLInternal(user, acls) -} - -async function createKafkaACLInternal(user: User, acls: KafkaACL[]) { - validateUser(user) - // Save to db - const db = await tables() - await Promise.all( - acls.map((acl) => db.kafka_acls.put({ ...acl, aclId: crypto.randomUUID() })) - ) - - // Add to Kafka - const adminClient = adminKafka.admin() - await adminClient.connect() - if (acls.some((acl) => acl.resourceType === AclResourceTypes.TOPIC)) - await Promise.all( - acls - .filter((acl) => acl.resourceType === AclResourceTypes.TOPIC) - .map((acl) => - adminClient.createTopics({ - topics: [ - { - topic: acl.resourceName, - }, - ], - }) - ) - ) - - await adminClient.createAcls({ acl: acls }) - await adminClient.disconnect() -} - export async function getKafkaACLsFromDynamoDB(user: User, filter?: string) { validateUser(user) const db = await tables() @@ -250,8 +154,6 @@ export async function getAclsFromBrokers() { const results: KafkaACL[] = [] for (const item of acls.resources) { - console.log('Item:', item) - results.push( ...item.acls.map((acl) => { return { @@ -267,27 +169,6 @@ export async function getAclsFromBrokers() { return results } -export async function deleteKafkaACL(user: User, aclIds: string[]) { - validateUser(user) - const db = await tables() - const acls: KafkaACL[] = await Promise.all( - aclIds.map((aclId) => db.kafka_acls.get({ aclId })) - ) - - const adminClient = adminKafka.admin() - await adminClient.connect() - await adminClient.deleteAcls({ filters: acls as AclFilter[] }) - await adminClient.disconnect() - - await Promise.all( - acls.map((acl) => - db.kafka_acls.delete({ - aclId: acl.aclId, - }) - ) - ) -} - export async function updateDbFromBrokers(user: User) { const kafkaDefinedAcls = await getAclsFromBrokers() const db = await tables() diff --git a/app/routes/admin.kafka._index.tsx b/app/routes/admin.kafka._index.tsx index b4329b1c4..df1d4d3e7 100644 --- a/app/routes/admin.kafka._index.tsx +++ b/app/routes/admin.kafka._index.tsx @@ -7,31 +7,17 @@ */ import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node' import { useFetcher, useLoaderData } from '@remix-run/react' -import type { ModalRef } from '@trussworks/react-uswds' -import { - Button, - Grid, - Icon, - Label, - Modal, - ModalFooter, - ModalHeading, - ModalToggleButton, - TextInput, -} from '@trussworks/react-uswds' +import { Button, Label, Table, TextInput } from '@trussworks/react-uswds' import { groupBy, sortBy } from 'lodash' -import { useEffect, useRef, useState } from 'react' +import { useEffect, useState } from 'react' import { getUser } from './_auth/user.server' -import HeadingWithAddButton from '~/components/HeadingWithAddButton' import SegmentedCards from '~/components/SegmentedCards' import Spinner from '~/components/Spinner' import TimeAgo from '~/components/TimeAgo' -import type { KafkaACL, UserClientType } from '~/lib/kafka.server' +import type { KafkaACL } from '~/lib/kafka.server' import { adminGroup, - createKafkaACL, - deleteKafkaACL, getKafkaACLsFromDynamoDB, getLastSyncDate, updateDbFromBrokers, @@ -63,58 +49,7 @@ export async function action({ request }: ActionFunctionArgs) { if (intent === 'migrateFromBroker') { await updateDbFromBrokers(user) - return null } - - const promises = [] - - switch (intent) { - case 'delete': - const aclId = getFormDataString(data, 'aclId') - if (!aclId) throw new Response(null, { status: 400 }) - promises.push(deleteKafkaACL(user, [aclId])) - break - case 'create': - const resourceName = getFormDataString(data, 'resourceName') - const userClientType = getFormDataString( - data, - 'userClientType' - ) as UserClientType - const group = getFormDataString(data, 'group') - const permissionTypeString = getFormDataString(data, 'permissionType') - const includePrefixed = getFormDataString(data, 'includePrefixed') - const resourceTypeString = getFormDataString(data, 'resourceType') - - if ( - !resourceName || - !userClientType || - !group || - !permissionTypeString || - !resourceTypeString - ) - throw new Response(null, { status: 400 }) - - const permissionType = parseInt(permissionTypeString) // Allow, deny - const resourceType = parseInt(resourceTypeString) - - promises.push( - createKafkaACL( - user, - userClientType, - resourceName, - group, - permissionType, - resourceType, - Boolean(includePrefixed) - ) - ) - - break - default: - break - } - await Promise.all(promises) - return null } @@ -131,7 +66,7 @@ export default function Index() { return ( <> - Kafka +

Kafka

Kafka ACLs

Kafka Access Control Lists (ACLs) are a security mechanism used to @@ -198,10 +133,24 @@ export default function Index() { .sort((a, b) => a.localeCompare(b)) .flatMap((key) => ( -

Resource: {key}

- {aclData[key].map((acl, index) => ( - - ))} +
+

Resource: {key}

+
+ + + + + + + + + + + {aclData[key].map((acl, index) => ( + + ))} + +
TypeGroupPermissionOperation
))} @@ -212,10 +161,6 @@ export default function Index() { } function KafkaAclCard({ acl }: { acl: KafkaACL }) { - const ref = useRef(null) - const fetcher = useFetcher() - const disabled = fetcher.state !== 'idle' - // TODO: These maps can probably be refactored, since they are // just inverting the enum from kafka, but importing them // directly here causes some errors. Same for mapping them to @@ -252,64 +197,11 @@ function KafkaAclCard({ acl }: { acl: KafkaACL }) { } return ( - <> - -
-
- Type: {resourceTypeMap[acl.resourceType]} -
-
- Group: {acl.principal} -
-
- Permission: {permissionMap[acl.permissionType]} -
-
- Operation: {operationMap[acl.operation]} -
-
-
- - - Delete - -
-
- - - - - Delete Kafka ACL - - - - - Cancel - - - - - - + + {resourceTypeMap[acl.resourceType]} + {acl.principal} + {permissionMap[acl.permissionType]} + {operationMap[acl.operation]} + ) } diff --git a/app/routes/admin.kafka.edit.tsx b/app/routes/admin.kafka.edit.tsx deleted file mode 100644 index f3f3c6887..000000000 --- a/app/routes/admin.kafka.edit.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/*! - * Copyright © 2023 United States Government as represented by the - * Administrator of the National Aeronautics and Space Administration. - * All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ -import type { LoaderFunctionArgs } from '@remix-run/node' -import { Form, useLoaderData } from '@remix-run/react' -import { - Button, - Checkbox, - Label, - Select, - TextInput, -} from '@trussworks/react-uswds' - -import { getUser } from './_auth/user.server' -import { getGroups } from '~/lib/cognito.server' - -export async function loader({ request }: LoaderFunctionArgs) { - const user = await getUser(request) - if (!user) throw new Response(null, { status: 403 }) - const userGroups = (await getGroups()) - .filter((group) => group.GroupName?.startsWith('gcn.nasa.gov/')) - .map((group) => group.GroupName) - - return { userGroups } -} - -export default function Kafka() { - const { userGroups } = useLoaderData() - return -} - -function KafkaAclForm({ groups }: { groups: string[] }) { - return ( - <> -

Create Kafka ACLs

-
- - - - - - - - Producer will generate ACLs for the Create, Write, and Describe - operations. Consumer will generate ACLs for the Read and Describe - operations - - - - - - -
- - If yes, submission will also trigger th generation of ACLs for the - provided topic name as a PREFIXED topic with a period included at - the end. For example, if checked, a topic of `gcn.notices.icecube` - will result in ACLs for both `gcn.notices.icecube` (literal) and - `gcn.notices.icecube.` (prefixed). - -
- - - - ) -}