diff --git a/frontend/public/integration-v3.cfn.yaml b/frontend/public/integration-v3.cfn.yaml index 77071b1..2b13c7f 100644 --- a/frontend/public/integration-v3.cfn.yaml +++ b/frontend/public/integration-v3.cfn.yaml @@ -134,20 +134,22 @@ Resources: Statement: # These actions allow us to get general information about the organization. - Action: + - organizations:DescribePolicy - organizations:ListAccounts + - organizations:ListChildren - organizations:ListParents + - organizations:ListPoliciesForTarget - organizations:ListRoots - iam:GenerateOrganizationsAccessReport - iam:GetOrganizationsAccessReport Effect: Allow Resource: '*' - # Allow enumerating and attaching service control policies to accounts. Note - # that attaching and detaching policies also requires permissions on the policy - # resource, which are granted in the next statement. + # Allow attaching service control policies to accounts. Note that attaching and + # detaching policies also requires permissions on the policy resource, which are + # granted in the next statement. - Action: - organizations:AttachPolicy - organizations:DetachPolicy - - organizations:ListPoliciesForTarget Effect: Allow Resource: !Sub 'arn:aws:organizations::${AWS::AccountId}:account/*' Condition: @@ -159,7 +161,6 @@ Resources: - organizations:AttachPolicy - organizations:DetachPolicy - organizations:CreatePolicy - - organizations:DescribePolicy - organizations:DeletePolicy - organizations:UpdatePolicy Effect: Allow diff --git a/frontend/src/app/(user-area)/teams/[teamId]/Rules.tsx b/frontend/src/app/(user-area)/teams/[teamId]/Rules.tsx index c4e739e..9b748c4 100644 --- a/frontend/src/app/(user-area)/teams/[teamId]/Rules.tsx +++ b/frontend/src/app/(user-area)/teams/[teamId]/Rules.tsx @@ -1,11 +1,11 @@ import { clsx } from 'clsx'; -import { useEffect, useMemo, useState } from 'react'; -import { ChevronRightIcon } from '@heroicons/react/24/outline'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { ChevronRightIcon, SparklesIcon } from '@heroicons/react/24/outline'; import Link from 'next/link'; import { Transition } from '@headlessui/react'; import { awsServices } from '@/aws'; -import { Button, ChipEditor, Dialog, ErrorMessage } from '@/components'; +import { Button, ChipEditor, Dialog, ErrorMessage, SuccessMessage } from '@/components'; import { AWSAccount } from '@/generated/api'; import { useAwsRegions, useCurrentTeamId, useManagedAwsScp, useTeamAwsAccountsMap } from '@/hooks'; import { RuleSet } from '@/rules'; @@ -70,7 +70,11 @@ interface AccountPageProps { } const AccountPage = ({ account, onBack }: AccountPageProps) => { + const [errorMessage, setErrorMessage] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); + const [isLoadingSuggestions, setIsLoadingSuggestions] = useState(false); const [isPreviewing, setIsPreviewing] = useState(false); + const dispatch = useDispatch(); const awsRegions = useAwsRegions(); const teamId = useCurrentTeamId(); const scp = useManagedAwsScp(teamId, account.id); @@ -95,13 +99,85 @@ const AccountPage = ({ account, onBack }: AccountPageProps) => { } }, [scpRuleSet]); + const suggest = useCallback(() => { + const impl = async () => { + if (isLoadingSuggestions) { + return; + } + setIsLoadingSuggestions(true); + setErrorMessage(''); + setSuccessMessage(''); + + try { + const [reports, awsAccessReport] = await Promise.all([ + dispatch.reports.fetchTeamReports(teamId), + dispatch.aws.fetchAccessReportByTeamAndAccountId({ + teamId, + accountId: account.id, + }), + ]); + + const ruleSet = new RuleSet(); + + for (const report of reports) { + if (report.size > 0 && report.scope.aws.accountId === account.id) { + ruleSet.addRegionToAllowlist(report.scope.aws.region); + } + } + + const cutoffDate = new Date(); + cutoffDate.setMonth(cutoffDate.getMonth() - 2); + + for (const service of awsAccessReport.services) { + if (service.lastAuthenticationTime && service.lastAuthenticationTime > cutoffDate) { + ruleSet.addServiceToAllowlist(service.namespace); + } + } + + setRuleSet(ruleSet); + setSuccessMessage( + 'Based on your recent account activity, we recommend the following rules. Please review them carefully and add or remove items as needed.', + ); + } catch (err) { + setErrorMessage(err instanceof Error ? err.message : 'An unknown error occurred.'); + } finally { + setIsLoadingSuggestions(false); + } + }; + impl(); + }, [ + isLoadingSuggestions, + setIsLoadingSuggestions, + dispatch, + teamId, + account, + setErrorMessage, + setSuccessMessage, + setRuleSet, + ]); + return (
Loading...
) : ( <> -{account.name ? `${account.name} (${account.id})` : account.id}
+