Skip to content

Commit

Permalink
Adding a basic panel to show which policies have a given action
Browse files Browse the repository at this point in the history
  • Loading branch information
hotnops committed Sep 16, 2024
1 parent fe4a509 commit b681cf0
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 32 deletions.
24 changes: 24 additions & 0 deletions cmd/api/src/actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package api

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/hotnops/apeman/src/api/src/queries"
)

func (s *Server) GetActionPolicies(c *gin.Context) {
actionName := "actionname"
action := c.Param(actionName)

statements, err := queries.GetActionPolicies(s.ctx, s.db, action)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
}

c.IndentedJSON(http.StatusOK, statements)
}

func (s *Server) addActionsEndpoints(router *gin.RouterGroup) {
router.GET("policies", s.GetActionPolicies)
}
8 changes: 8 additions & 0 deletions cmd/api/src/queries/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ type PermissionMapping struct {
Actions map[string][]graph.ID `json:"actions"`
}

// Get all the policies attached to a particular action node
func GetActionPolicies(ctx context.Context, db graph.Database, action string) (graph.PathSet, error) {
query := "MATCH p=(a:AWSAction) <- [:ExpandsTo|Action*1..2] - (s:AWSStatement) - [:AttachedTo*2..3] - (pol:AWSManagedPolicy|AWSInlinePolicy) WHERE a.name = '%s' RETURN p"
query = fmt.Sprintf(query, action)
paths, err := CypherQueryPaths(ctx, db, query)
return paths, err
}

func GetInboundRolePaths(ctx context.Context, db graph.Database, roleId string) (graph.PathSet, error) {
query := "MATCH p=(a:UniqueArn) - [:IdentityTransform* {name: 'sts:assumerole'}] -> (b:AWSRole) WHERE b.roleid = '%s' AND ALL(n IN nodes(p) WHERE SINGLE(x IN nodes(p) WHERE x = n)) RETURN p"
query = fmt.Sprintf(query, roleId)
Expand Down
1 change: 1 addition & 0 deletions cmd/api/src/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ func (s *Server) handleRequests() {
s.addNodeEndpoints(router.Group("/nodes/:nodeid"))
s.addGroupsEndpoints(router.Group("/groups/:groupid"))
s.addAccountsEndpoints(router.Group("/accounts/:accountid"))
s.addActionsEndpoints(router.Group("/actions/:actionname"))

router.GET("/nodes", s.GetAWSNodes)
router.GET("/accounts", s.GetAWSAccountIDs)
Expand Down
47 changes: 45 additions & 2 deletions ui/apeman-ui/src/components/ActionOverviewPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
const ActionOverviewPanel = () => {
return <div>ActionOverviewPanel</div>;
import { Accordion, Box } from "@chakra-ui/react";
import PathAccordionList from "./PathAccordionList";
import { addPathToGraph, Path } from "../services/pathService";
import { useApemanGraph } from "../hooks/useApemanGraph";
import { useEffect, useState } from "react";
import { GetActionPolicies } from "../services/actions";
import { getNodeLabel, Node } from "../services/nodeService";

interface Props {
node: Node;
}

const ActionOverviewPanel = ({ node }: Props) => {
const [inboundPaths, setInboundPaths] = useState<Path[]>([]);
const { addNode, addEdge } = useApemanGraph();

useEffect(() => {
const { request, cancel } = GetActionPolicies(node.properties.map.name);
request
.then((res) => {
setInboundPaths(res.data.map((path) => path));
})
.catch((error) => {
if (error.code !== "ERR_CANCELED") {
console.error("Error fetching inbound roles:", error);
}
});

return cancel;
}, [node.properties.map.roleid]);

return (
<Box>
<Accordion allowMultiple={true} width="100%">
<PathAccordionList
paths={inboundPaths}
name="Policies with Action"
pathFunction={(n) => {
addPathToGraph(n, addNode, addEdge);
}}
pathLabelFunction={(n) => getNodeLabel(n.Nodes[n.Nodes.length - 1])}
></PathAccordionList>
</Accordion>
</Box>
);
};

export default ActionOverviewPanel;
71 changes: 41 additions & 30 deletions ui/apeman-ui/src/components/NodeOverviewPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { kinds } from "../services/nodeService";
import PolicyOverview from "./PolicyOverview";
import UserOverviewPanel from "./UserOverviewPanel";
import GroupOverviewPanel from "./GroupOverviewPanel";
import ActionOverviewPanel from "./ActionOverviewPanel";

interface Props {
node: Node;
Expand All @@ -27,6 +28,7 @@ const NodeOverviewPanel = ({ node }: Props) => {
[kinds.AWSGroup, "Group Overview"],
[kinds.UniqueArn, "Resource Overview"],
[kinds.AWSStatement, "Statement Overview"],
[kinds.AWSAction, "Action Overview"],
]);

return (
Expand All @@ -39,41 +41,50 @@ const NodeOverviewPanel = ({ node }: Props) => {
size="sm"
>
<TabList>
{nodeKinds.map((kind) => (
<Tab fontSize="xs" key={kind}>
{tabTitleMap.get(kind)}
</Tab>
))}
{nodeKinds.map(
(kind) =>
tabTitleMap.get(kind) && (
<Tab fontSize="xs" key={kind}>
{tabTitleMap.get(kind)}
</Tab>
)
)}
<Tab fontSize="xs" key="nodeOverview">
Node Overview
</Tab>
</TabList>
<TabPanels>
{nodeKinds.map((kind) => (
<TabPanel key={kind}>
{kind === kinds.AWSAccount ? (
<AccountOverviewPanel node={node}></AccountOverviewPanel>
) : null}
{kind === kinds.AWSRole ? (
<RoleOverviewPanel node={node}></RoleOverviewPanel>
) : null}
{kind === kinds.AWSUser ? (
<UserOverviewPanel node={node}></UserOverviewPanel>
) : null}
{kind === kinds.AWSGroup ? (
<GroupOverviewPanel node={node}></GroupOverviewPanel>
) : null}
{kind === kinds.UniqueArn ? (
<ResourceOverview node={node} />
) : null}
{kind === kinds.AWSStatement ? (
<StatementOverview node={node}></StatementOverview>
) : null}
{kind === kinds.AWSManagedPolicy ? (
<PolicyOverview node={node}></PolicyOverview>
) : null}
</TabPanel>
))}
{nodeKinds.map(
(kind) =>
tabTitleMap.get(kind) && (
<TabPanel key={kind}>
{kind === kinds.AWSAccount ? (
<AccountOverviewPanel node={node}></AccountOverviewPanel>
) : null}
{kind === kinds.AWSRole ? (
<RoleOverviewPanel node={node}></RoleOverviewPanel>
) : null}
{kind === kinds.AWSUser ? (
<UserOverviewPanel node={node}></UserOverviewPanel>
) : null}
{kind === kinds.AWSGroup ? (
<GroupOverviewPanel node={node}></GroupOverviewPanel>
) : null}
{kind === kinds.UniqueArn ? (
<ResourceOverview node={node} />
) : null}
{kind === kinds.AWSStatement ? (
<StatementOverview node={node}></StatementOverview>
) : null}
{kind === kinds.AWSManagedPolicy ? (
<PolicyOverview node={node}></PolicyOverview>
) : null}
{kind === kinds.AWSAction ? (
<ActionOverviewPanel node={node}></ActionOverviewPanel>
) : null}
</TabPanel>
)
)}
<TabPanel>
<NodeOverview node={node}></NodeOverview>
</TabPanel>
Expand Down
16 changes: 16 additions & 0 deletions ui/apeman-ui/src/services/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import apiClient from "./api-client";
import { Path } from "./pathService";

export function GetActionPolicies(actionName: string) {
const controller = new AbortController();
const request = apiClient.get<Path[]>(`/actions/${actionName}/policies`, {
signal: controller.signal,
});

return {
request,
cancel: () => {
controller.abort();
},
};
}

0 comments on commit b681cf0

Please sign in to comment.