Example #1
0
    def _get_resource_arn(self, resource, base_resource):

        resource_type = resource.__class__.__name__.split(".")[-1]
        properties = resource.meta.data
        keys = properties.keys()
        label = self._get_resource_type_label(resource_type)
        arn = None

        if "Arn" in keys:
            arn = properties["Arn"]
        elif f"{resource_type}Arn" in keys:
            arn = properties[f"{resource_type}Arn"]
        elif f"{resource_type}Id" in keys and properties[
                f"{resource_type}Id"].startswith("arn:aws"):
            arn = properties[f"{resource_type}Id"]
        elif label in RESOURCES.keys():
            parent = base_resource.meta.data if base_resource.meta.data is not None else {}
            combined = {**parent, **properties}
            arn = RESOURCES.definition(label).format(
                Region=self.session.region_name,
                Account=self.account_id,
                **combined)

        if isinstance(arn, str) \
                and re.compile("arn:aws:([a-zA-Z0-9]+):([a-z0-9-]*):(\d{12}|aws)?:(.*)"
                               ).match(arn) is not None:
            return arn

        return None
Example #2
0
    def load_generics(self, types=None):

        for k in self.console.tasklist(f"Adding Generic resources",
                                       self.types,
                                       done=f"Added Generic resources"):
            self.add(
                Generic(properties={
                    "Name": f"${k.split(':')[-1]}",
                    "Arn": RESOURCES.definition(k),
                },
                        labels=[k]))
Example #3
0
    def _load_generics(self, types=None):

        labels = [t for t in types if t in RESOURCES]  \
            if types is not None else \
            [t for t in RESOURCES if t.startswith(
                "AWS::%s::" % self.__class__.__name__.capitalize())]

        for k in labels:

            self.add(Generic(properties={
                "Name": "$%s" % k.split(':')[-1],
                "Arn":  RESOURCES.definition(k)
            }, labels=[k]))
Example #4
0
    def _load_associations(self):

        if len(self.associates) == 0:
            return

        self._print(
            f"[*] Adding {self.__class__.__name__} associative relationships")

        edges = Elements()

        for resource in self.get("Resource"):

            references = {}
            label = [l for l in resource.labels() if l != "Resource"][0]

            # Find references to other resources in the form of a dictionary (refs)

            self._references(resource.properties(), references)

            # Create an edge, for all known associations (as defined by self.rels).

            for rel in [
                    r for r in self.associates
                    if r[0] == label or r[1] == label
            ]:

                i = 1 if label == rel[0] else 0

                # Get a list of foreign keys that we must be capable of referencing
                # in order to create an association

                fk = [
                    a for a in re.compile("{([A-Za-z]+)}").findall(
                        RESOURCES.definition(rel[i]))
                    if a not in ["Account", "Region"]
                ]

                if not all([k in references.keys() for k in fk]):
                    continue

                # TODO: Handle Types that make use of more than one
                # variable identifier

                if len(fk) != 1:
                    raise NotImplementedError

                fk = fk[0]

                for v in list(references[fk]):

                    # Find the first resource matching the reference

                    r = next((r
                              for r in self if re.compile(
                                  RESOURCES.definition(rel[i]).
                                  format(Account=self.account_id,
                                         Region=self.session.region_name,
                                         **{
                                             **{
                                                 x: list(y)[0]
                                                 for x, y in references.items(
                                                 ) if len(y) == 1
                                             },
                                             **{
                                                 fk: v
                                             }
                                         })).match(r.id()) is not None), None)

                    if r is None:
                        # print("Failed to match (%s: %s) against any resources" % (k, v))
                        # print("Its likely that the resource was missed during ingestion")
                        continue

                    # Delete the properties that are responsible for the edge's existence.

                    properties = self._extract_property_value(
                        resource.properties(), fk)

                    # Even though direction is irrelavent when dealing with Associative
                    # edges, neo4j is directed. We need to ensure the direction is kept
                    # in order to eliminate duplicate edges.

                    (source, target) = (resource, r) if i == 1 else (r,
                                                                     resource)

                    edge = Associative(properties={"Name": "Attached"},
                                       source=source,
                                       target=target)
                    opposite_edge = Associative(
                        properties={"Name": "Attached"},
                        source=target,
                        target=source)

                    if (edge not in self and opposite_edge
                            not in self) and edge not in edges:
                        edges.append(edge)

        self.extend(edges)
Example #5
0
        def run_ingestor(collections, model):

            if not len(collections) > 0:
                return

            for attr, v in model.items():

                label = list(v.keys())[0]
                collection_managers = []

                if len(self.types) > 0 and label not in self.types:

                    collateral = [
                        rt for rt in [
                            list(k.keys())[0]
                            for k in list(v.values())[0].values()
                        ] if rt in self.types and rt not in
                        [list(k.keys())[0] for k in model.values()]
                    ]

                    self.console.debug(''.join(
                        (f"Skipped {label} ingestion ",
                         f"({', '.join(collateral)} will also be skipped)."
                         if len(collateral) > 0 else "")))

                    continue

                rt = ''.join(''.join([
                    f" {c}" if c.isupper() else c for c in getattr(
                        collections[0], attr)._model.request.operation
                ]).split()[1:])

                for operation, collection in self.console.tasklist(
                        f"Adding {rt}",
                        iterables=map(lambda c: (getattr(c, attr).all, c),
                                      collections),
                        wait=
                        f"Awaiting response to {self.__class__.__name__.lower()}:"
                        f"{getattr(collections[0], attr)._model.request.operation}",
                        done=f"Added {rt}"):

                    for cm in SessionClientWrapper(operation(),
                                                   console=self.console):

                        collection_managers.append(cm)

                        if 'meta' not in dir(cm) or cm.meta.data is None:

                            self.console.warn(
                                f"Skipping ServiceResource {cm}: "
                                "it has no properties")
                            continue

                        cm.meta.data["Name"] = [getattr(cm, i)
                                                for i in cm.meta.identifiers
                                                ][-1] if "Name" not in cm.meta.data.keys() \
                            else cm.meta.data["Name"]

                        properties = {
                            **cm.meta.data,
                            **dict(collection.meta.data
                                   if collection is not None
                                   and not collection.__class__.__name__.endswith("ServiceResource")
                                   and collection.meta.data is not None
                                   else {}),
                        }

                        try:
                            cm.meta.data["Arn"] = RESOURCES.definition(
                                label).format(Region=self.session.region_name,
                                              Account=self.account,
                                              **properties)

                        except KeyError as p:

                            self.console.warn(
                                f"Failed to construct resource ARN: defintion for type '{label}' is malformed - "
                                f"boto collection '{cm.__class__.__name__}' does not have property {p}, "
                                f"maybe you meant one of the following ({', '.join(properties.keys())}) instead?"
                            )
                            continue

                        # Add Resource
                        resource = Resource(labels=[label],
                                            properties=cm.meta.data)
                        self.add(resource)

                for _, attrs in v.items():
                    run_ingestor(collection_managers, attrs)