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 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) => (
-
- This will delete the DynamoDB entry and remove the ACL from the
- broker. Do you wish to continue?
- Resource: {key}
- {aclData[key].map((acl, index) => (
- Resource: {key}
+
+
+
))}
@@ -212,10 +161,6 @@ export default function Index() {
}
function KafkaAclCard({ acl }: { acl: KafkaACL }) {
- const ref = useRef
+
+
+
+ {aclData[key].map((acl, index) => (
+ Type
+ Group
+ Permission
+ 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{resourceTypeMap[acl.resourceType]}
+ {acl.principal}
+ {permissionMap[acl.permissionType]}
+ {operationMap[acl.operation]}
+ Create Kafka ACLs
-
- >
- )
-}