From bc8e81f751d9cf9e80389f675bf1b26bd778bfc3 Mon Sep 17 00:00:00 2001
From: dakota002
Date: Mon, 19 Aug 2024 14:58:08 -0400
Subject: [PATCH] Adds some checks to confirm actions, and include Update
Broker from DB action
---
app/lib/kafka.server.ts | 26 ++----
app/routes/admin.kafka._index.tsx | 147 ++++++++++++++++++++++--------
app/routes/admin.kafka.edit.tsx | 11 ++-
sandbox-seed.json | 16 +++-
4 files changed, 138 insertions(+), 62 deletions(-)
diff --git a/app/lib/kafka.server.ts b/app/lib/kafka.server.ts
index 831aa0701..dbaa4c5b5 100644
--- a/app/lib/kafka.server.ts
+++ b/app/lib/kafka.server.ts
@@ -9,6 +9,7 @@ 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 {
@@ -81,17 +82,6 @@ if (process.env.ARC_SANDBOX) {
}
}
-/**
- * AclEntry already contains definitions for the following:
- *
- * principal: string --> 'User:{cognito_group_name}'
- * host: string --> '*'
- * operation: AclOperationTypes --> Read,Write, etc from enum
- * permissionType: AclPermissionTypes --> Allow, Deny, etc from enum
- * resourceType: AclResourceTypes --> TOPIC, etc
- * resourceName: string --> name of topic: 'gcn.notices.burstcube'
- * resourcePatternType: ResourcePatternTypes --> PREFIXED or LITERAL
- */
export type KafkaACL = AclEntry & {
aclId?: string
}
@@ -139,9 +129,9 @@ export async function createKafkaACL(
resourceName,
principal: `User:${group}`,
host: '*',
- operation, // Read, write, etc
- permissionType, // Allow, deny etc
- resourcePatternType: 3, // LITERAL | PREFIXED
+ operation,
+ permissionType,
+ resourcePatternType: 3,
resourceType,
}
})
@@ -150,9 +140,9 @@ export async function createKafkaACL(
resourceName,
principal: `User:${group}`,
host: '*',
- operation, // Read, write, etc
+ operation,
permissionType,
- resourcePatternType: 3, // LITERAL | PREFIX
+ resourcePatternType: 3,
resourceType,
}
})
@@ -280,13 +270,13 @@ export async function getAclsFromBrokers() {
export async function deleteKafkaACL(user: User, aclIds: string[]) {
validateUser(user)
const db = await tables()
- const acls = await Promise.all(
+ 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 })
+ await adminClient.deleteAcls({ filters: acls as AclFilter[] })
await adminClient.disconnect()
await Promise.all(
diff --git a/app/routes/admin.kafka._index.tsx b/app/routes/admin.kafka._index.tsx
index b2b877679..0685fcd23 100644
--- a/app/routes/admin.kafka._index.tsx
+++ b/app/routes/admin.kafka._index.tsx
@@ -19,6 +19,7 @@ import {
ModalToggleButton,
TextInput,
} from '@trussworks/react-uswds'
+import { groupBy, sortBy } from 'lodash'
import { useEffect, useRef, useState } from 'react'
import { getUser } from './_auth/user.server'
@@ -43,7 +44,13 @@ export async function loader({ request }: LoaderFunctionArgs) {
if (!user || !user.groups.includes(adminGroup))
throw new Response(null, { status: 403 })
const { aclFilter } = Object.fromEntries(new URL(request.url).searchParams)
- const dynamoDbAclData = await getKafkaACLsFromDynamoDB(user, aclFilter)
+ const dynamoDbAclData = groupBy(
+ sortBy(await getKafkaACLsFromDynamoDB(user, aclFilter), [
+ 'resourceName',
+ 'principal',
+ ]),
+ 'resourceName'
+ )
const latestSync = await getLastSyncDate()
return { dynamoDbAclData, latestSync }
}
@@ -54,6 +61,7 @@ export async function action({ request }: ActionFunctionArgs) {
throw new Response(null, { status: 403 })
const data = await request.formData()
const intent = getFormDataString(data, 'intent')
+
if (intent === 'migrateFromBroker') {
await updateDbFromBrokers(user)
return null
@@ -64,11 +72,11 @@ export async function action({ request }: ActionFunctionArgs) {
return null
}
- const aclId = getFormDataString(data, 'aclId')
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
@@ -78,8 +86,8 @@ export async function action({ request }: ActionFunctionArgs) {
data,
'userClientType'
) as UserClientType
- const permissionTypeString = getFormDataString(data, 'permissionType')
const group = getFormDataString(data, 'group')
+ const permissionTypeString = getFormDataString(data, 'permissionType')
const includePrefixed = getFormDataString(data, 'includePrefixed')
const resourceTypeString = getFormDataString(data, 'resourceType')
@@ -122,6 +130,7 @@ export default function Index() {
const updateFetcher = useFetcher()
const aclFetcher = useFetcher()
const brokerFromDbFetcher = useFetcher()
+ const ref = useRef(null)
useEffect(() => {
setAclData(aclFetcher.data?.dynamoDbAclData ?? aclData)
@@ -140,7 +149,6 @@ export default function Index() {
data from topics, create or delete topics, manage consumer groups, and
perform administrative tasks.
-
- {latestSync && (
+ {latestSync ? (
Last synced by {latestSync.syncedBy}{' '}
+ ) : (
+
+ )}
+
+ Update Broker from DB
+
+ {brokerFromDbFetcher.state !== 'idle' && (
+
+ Updating...
+
)}
-
-
-
- {brokerFromDbFetcher.state !== 'idle' && (
-
- Updating...
-
- )}
-
-
{aclData && (
<>
-
+