# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['wonk']

package_data = \
{'': ['*']}

install_requires = \
['PyYAML>=5.4.1,<6.0.0',
 'boto3>=1.17.25,<2.0.0',
 'ortools>=8.2.8710,<9.0.0',
 'pydantic>=1.8.2,<2.0.0',
 'toposort>=1.6,<2.0',
 'xdg>=5.0.1,<6.0.0']

entry_points = \
{'console_scripts': ['wonk = wonk.cli:handle_command_line']}

setup_kwargs = {
    'name': 'wonk',
    'version': '0.5.2',
    'description': 'Wonk is a tool for combining a set of AWS policy files into smaller compiled policy sets.',
    'long_description': 'The Policy Wonk\n===============\n\nWonk is a tool for combining a set of AWS policy files into smaller compiled `policy sets`_.\n\nTable of Contents\n-----------------\n\n.. contents:: Contents:\n\nRationale\n=========\n\nWonk can help you in several situations.\n\nPolicies are limited resources\n------------------------------\n\nYou want to give the people in your organization the AWS permissions needed to do their job. Amazon has helpfully created several hundred policies like ``AmazonRDSReadOnlyAccess`` and ``AmazonS3FullAccess`` that you can assign to users, groups, or roles. This is super convenient... up until it\'s not.\n\nAWS has `IAM quotas`_ like:\n\n* Policies may not be more than 6,144 characters long.\n* You can\'t attach more than 10 groups or roles to a user.\n* You can\'t attach more than 10 policies to a single group, role, or user.\n* If you\'re logging into AWS with SSO, each user like gets exactly 1 role assigned to them.\n\nWhat if your backend engineers log in with Okta and they need **11** policies to do their job?\n\nWonk to the rescue! You can combine them into one big policy with a command line like:\n\n::\n\n    $ wonk combine -p MyPolicy AWSPolicy1.json AWSPolicy2.json\n\nwhich reads the contents of ``AWSPolicy1.json`` and ``AWSPolicy2.json`` and merges them into a ``MyPolicy.json`` file.\n\nYour roles share a lot of common permissions\n--------------------------------------------\n\nPerhaps you have one role for backend engineers, and another role for backend engineers who are on call this week and need some additional permissions. You really don\'t want to maintain two policies that are nearly identical, though.\n\nIn this case, you could put all of the standard permissions in one policy, all of the additional on-call permissions in another, then combine them:\n\n::\n\n    $ wonk combine -p BackendOnCall Backend.json OnCall.json\n\nThings got really complicated when you weren\'t looking\n------------------------------------------------------\n\nBeyond just combining a file or 2 as needed, you want some help managing multiple roles with lots of policies. Say you\'re setting up policies for both frontend and backend engineers and each of them have special on-call roles that share some extra debugging permissions. Each role uses a combination of some AWS-managed policies with some that you\'ve written yourself.\n\nWonk loves you and wants you to be happy.\n\nFirst, it assumes a directory layout like this:\n\n::\n\n    ├── wonk.yaml\n    ├── managed\n    │   ├── AWSPolicy1.json\n    │   └── AWSPolicy2.json\n    ├── local\n    │   ├── BackendECSReadOnly.json\n    │   ├── FrontendCloudWatchReadOnly.json\n    │   └── OnCall.json\n    └── combined\n        ├── Backend_1.json\n        ├── Frontend_1.json\n        ├── BackendOnCall_1.json\n        └── FrontendOnCall_1.json\n\nwhere the ``managed`` directory is full of policy files that you\'ve downloaded from AWS (maybe using the ``wonk fetch`` command), the ``local`` directory has policies you\'ve written yourself, and ``combined`` has the output files that Wonk creates for you.\n\nYou **could** write a bunch of ``wonk combine`` command lines, maybe in a shell script or a Makefile. Alternatively, you could write a ``wonk.yaml`` file like this:\n\n::\n\n    policy_sets:\n      Backend:\n        managed:\n          - AWSPolicy1\n          - arn:path:to:your:policy/AWSPolicy2\n        local:\n          - BackendECSReadOnly\n\n      BackendOnCall:\n        inherits:\n          - Backend\n        local:\n          - OnCall\n\n      Frontend:\n        managed:\n          - AWSPolicy3\n        local:\n          - FrontendCloudWatchReadOnly\n\n      FrontendOnCall:\n        inherits:\n          - Frontend\n        local:\n          - OnCall\n\nand then tell Wonk to build them all for you:\n\n::\n\n    $ wonk build --all\n\nwhich fetches any missing managed policies, then creates a set of combined policies named after their YAML configurations.\n\nA managed policy ``Foo`` is fetched by the ARN ``arn:aws:iam::aws:policy/Foo``. However, some Amazon policies don\'t follow that convention. In that case, you can give an ARN instead of a policy name and that ARN will be fetched instead (and the policy\'s name will be derived from the ARN). You could also do that if you want to fetch your own policy from Amazon instead of maintaining it locally.\n\nInstallation\n============\n\nToday: clone this repo and run ``poetry install``.\n\nSoon: ``pip install wonk``.\n\nUsage\n=====\n\nFetching policies\n-----------------\n\nUse ``wonk fetch`` to retrieve a policy from AWS by name or by ARN and write it to stdout. Each of these commands emit the same output:\n\n::\n\n    $ wonk fetch --arn "arn:aws:iam::aws:policy/AWSLambdaFullAccess"\n    $ wonk fetch --name AWSLambdaFullAccess\n    $ wonk fetch --profile my_aws_profile_name --name AWSLambdaFullAccess\n\nCombining policies\n------------------\n\nUse ``wonk combine`` to combine multiple policies into a policy set:\n\n::\n\n    $ wonk combine -p Foo policy1.json policy2.json\n\nBuilding configured policy statements\n-------------------------------------\n\nThe ``wonk build`` command interprets a ``wonk.yaml`` file as described in the example above and builds the requested policy set(s).\n\nTo build one named policy set:\n\n::\n\n    $ wonk build --policy-set BackendOnCall\n\nTo build all defined policy sets:\n\n::\n\n    $ wonk build --all\n\nThe details\n===========\n\nSounds simple, right? Well, not quite. Remember, IAM quotas limit managed policies to 6,144 characters. You can put a few more characters on an inline policy directly on a role, but that\'s not best practice and you don\'t really want to go down that path. Instead, Wonk uses a few tricks to try to make policies fit inside their size limit:\n\n* It strips all ``Sid`` keys from statements, per Amazon\'s recommendations.\n* It discard duplicate actions.\n* It removes all "shadowed" actions. For instance, if a statement has actions ``Foo:SomeAction`` and ``Foo:*``, it discards ``Foo:SomeAction`` because ``Foo:*`` already has it covered. Similarly, ``Foo:Get*`` will shadow ``Foo:GetSomething``, so ``Foo:GetSomething`` will be removed.\n* Wonk tries to make the generated policies as human-readable as possible, but will format them very tersely if necessary. You can always use jq_ to reformat its outputs for viewing.\n\nNote: actions are always grouped by similar principals, resources, conditions, etc. If two statements have different conditions, say, they are processed separately.\n\nBreaking up is hard to do\n-------------------------\n\nWonk does whatever it can to make a policy fit within that magic 6,144 character limit, but somethings that just can\'t be done. If you try to combine 30 different non-overlapping policies, there\'s a decent chance that the end result simply can\'t be shrunk enough. A careful reader might have noticed that all of the command examples specify an output "base" instead of a specific filename, and an output ``Foo`` ends up creating a file named ``Foo.json``. This is because in the case that Wonk can\'t pack everything into a separate file, it creates a **set** of as few output policies as possible to include all of the actions. The general process is this:\n\n* Try to make everything fit.\n* If there are any statements with so many actions that they can\'t be shrunk into the size limit, split them up into equal-size chunks that do fit.\n* Now we have the case of fitting **M** statements into **N** policies, of which there can\'t be more than 10 because of the AWS limits. That looks a lot like the `knapsack problem`_, and indeed it is. Wonk uses Google\'s `SCIP constraint solver`_ to pack all of the statements into as few policies as possible.\n* If **none** of this is sufficient, Wonk raises an exception and quits.\n\nPolicy sets\n-----------\n\nThe end result of many Wonk operations is a collection of files, a **policy set**, named ``<base>_1.json`` through ``<base>_N.json`` where N <= 10. This is different from most utilities which operate on individual files, but Wonk can\'t know how many files it will be creating in advance.\n\nWhy 10? Because AWS usually won\'t allow you to attach more than 10 policies to a user, group, or role. Since policy sets work together like one giant policy and can\'t be split up, Wonk won\'t create a policy set that can\'t actually be attached to anything. If you\'re bumping up against this limit, consider creating 2 policy sets and applying them to 2 distinct but groups (like ``Backend_1`` and ``Backend_2``), then putting each relevant user into both groups. Alternatively, if your policies cover 99 actions like ``Service:OnePermission`` and ``Service:Another`` on a service that only has 100 possible actions, and you\'ve done your due diligence and don\'t mind giving your users access to that 100th action, consider adding a ``Service:*`` action to a local policy. That will replace all those individual actions with the single wildcard. Likewise, if you mean to give your users access to all of the various ``Service:GetThis`` and ``Service:GetThat`` actions, you can cover them all at once with ``Service:Get*``. This also has the nice side effect of documenting that you actually intend to allow access to all of the ``Get*`` actions.\n\nTerraforming combined policies\n==============================\n\nReasonably recent modern versions of Terraform support ``fileset`` and ``for_each`` syntax. You can define a single policy resource that exactly expands out to a whole set of policies, then attach them all at once to a group or role:\n\n::\n\n    resource "aws_iam_policy" "Frontend" {\n      for_each    = fileset(path.module, "combined/Frontend_*.json")\n      name        = split(".", basename(each.value))[0]\n      description = "Frontend users need to do stuff"\n      policy      = file(each.value)\n    }\n\n    resource "aws_iam_group_policy_attachment" "Frontenders__Frontend" {\n      for_each   = aws_iam_policy.Frontend\n      group      = data.aws_iam_group.frontenders.group_name\n      policy_arn = each.value.arn\n    }\n\nLimitations\n===========\n\nAs of this writing, Wonk is usable but not finished. It\'s missing a few nice features:\n\n* Wonk doesn\'t consider action shadowing when one statement has restrictions but another doesn\'t. For example, given two statement blocks:\n\n::\n\n    {\n        "Statement": [\n            {\n                "Effect": "Allow",\n                "Action": "Foo:Something",\n                "Resource": "arn:aws:service::my_resource"\n            },\n            {\n                "Effect": "Allow",\n                "Action": "Foo:Something"\n            }\n        ]\n    }\n\nthe second statement is broader than the first, so the first could be safely removed. Right now it isn\'t.\n\nCopyright\n=========\n\nThe Policy Wonk is copyright 2021 Amino, Inc. and distributed under the terms of the Apache-2.0 License.\n\n.. _IAM quotas: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html\n.. _jq: https://stedolan.github.io/jq/\n.. _knapsack problem: https://en.wikipedia.org/wiki/Knapsack_problem\n.. _SCIP constraint solver: https://developers.google.com/optimization/mip/integer_opt\n',
    'author': 'Kirk Strauser',
    'author_email': 'kirk@amino.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/aminohealth/wonk',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
