-
Notifications
You must be signed in to change notification settings - Fork 175
Library Reference
The principalmapper
module exposes several different classes and functions and can be used for your own purposes. This page details the different submodules, classes, and functions to enable you to use Principal Mapper as a library.
The common
submodule has all of the different main classes for working with Graphs and OrganizationTree objects. The classes are exposed through __all__
, meaning you can do:
from principalmapper.common import Graph, Node, Edge
The Graph is an object containing Nodes, Edges, Groups, and Policies along with some metadata (AWS account ID, PMapper version). You can create a Graph a few ways:
from principalmapper.common import Graph, Node, Edge, Policy, Group
# to construct Graph you need all the nodes/edges/policies/groups/metadata ready to go
# nodes: List[Node]
# edges: List[Edge]
# policies: List[Policy]
# groups: List[Group]
# metadata: dict, must have keys 'account_id' and 'pmapper_version' or ValueError is raised
g1 = Graph(nodes, edges, policies, groups, metadata)
# if you have a Graph stored on-disk, there's a classmethod to load it
g2 = Graph.create_graph_from_local_disk('graph_dir')
# And with a Graph object you can drop it to disk with a method
g1.store_graph_as_json('other_graph_dir')
Nodes represent IAM Users and Roles. Here's the signature of the constructor:
def __init__(self, arn: str, id_value: str, attached_policies: Optional[List[Policy]], group_memberships: Optional[List[Group]], trust_policy: Optional[dict], instance_profile: Optional[List[str]], num_access_keys: int, active_password: bool, is_admin: bool, permissions_boundary: Optional[Union[str, Policy]], has_mfa: bool, tags: Optional[dict]):
These are fairly self-explanatory. The constructor itself has a bunch of checks to make sure that all the values are filled and reasonable (i.e. you can't set a trust document for an IAM User). All of those input parameters can be read and updated after construction, such as is_admin
.
Additionally, Node objects contain a cache
dictionary. The cache is used for preventing recomputations, such as finding Edges where the Node object is the source of the Edge (get_outbound_edges(graph: Graph)
method) or giving the "searchable name" of the Node (searchable_name()
method).
Edges represent how one Node can authenticate as another Node, thus gaining the ability to make AWS API calls with that other Node's permissions. They are constructed with:
def __init__(self, source: Node, destination: Node, reason: str, short_reason: str)
The reason
/short_reason
parameters describe why the source node can access the destination node. PMapper's current code just lists a service name for the short_reason
field. There is also a method, describe_edge()
that returns a string that chains the source/reason/destination in a way that usually formulates a complete sentence. This is seen when running query
where a principal has to access another principal to make an API call.
Groups correspond to IAM Groups. IAM Users can belong to one or more IAM Groups, IAM Groups can have multiple IAM Users as members. Groups can have attached policies, which filter down to its member IAM Users. Group objects in PMapper are constructed like so:
def __init__(self, arn: str, attached_policies: Optional[List[Policy]]):
Policies define the permissions that a given principal has, and are checked during the authorization process. These policies are defined using JSON objects. PMapper needs them serialized as dictionaries before passing it as a constructor:
def __init__(self, arn: str, name: str, policy_doc: dict):
The arn
parameter should either be the ARN of the managed policy being represented, or the ARN of the resource in the case of resource policies.
OrganizationTree objects represent an organization from AWS Organizations, as well as its hierarchy of OUs (represented with OrganizationNode objects). These can be constructed as well as loaded and saved to disk as Graphs are:
def __init__(self, org_id: str, management_account_id: str, root_ous: List[OrganizationNode], all_scps: List[Policy], accounts: List[str], edge_list: List[Edge], metadata: dict):
from principalmapper.common import OrganizationTree
org1 = OrganizationTree.create_from_dir('org-dir')
org1.save_organization_to_disk('alternate-org-dir')
For manual construction, see principalmapper.gathering.get_organizations_data
for an example of the traversals needed to build the OU hierarchy.