Skip to content

Commit

Permalink
Add --resource-type flag to policy_sentry query action-table comm…
Browse files Browse the repository at this point in the history
…and (#261)

* add resource type flag in query action-table

* add unit test

* updating doc for resource-type param
  • Loading branch information
reetasingh committed Oct 20, 2020
1 parent ca3893a commit 8bb7a70
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 58 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* write-policy:
* Minimization is improved by grouping results based on ARNs (#252)
* query:
* `--resource-type` flag is added to `policy_sentry query action-table` command
* `--resource-type` flag added to `policy_sentry query action-table` command (Fixes #255)
* Backend methods:
* Added some utility functions (`get_statement_from_policy_using_sid`, `get_sid_names_from_policy` to make it easier to future-proof unit tests that rely on the ever-changing AWS IAM data.
* Bug fix for `get_actions_for_service` (Fixes #245)
Expand Down
18 changes: 10 additions & 8 deletions docs/querying/action-table.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ Action table
# NOTE: Use --fmt yaml or --fmt json to change the output format. Defaults to json for querying

# Get a list of actions that do not support resource constraints
policy_sentry query action-table --service s3 --wildcard-only --fmt yaml
policy_sentry query action-table --service s3 --resource-type '*' --fmt yaml

# Get a list of actions at the "Read" level in S3 that do not support resource constraints
policy_sentry query action-table --service s3 --access-level read --wildcard-only --fmt yaml
policy_sentry query action-table --service s3 --access-level read --resource-type '*' --fmt yaml

# Get a list of actions at the "Write" level in SSM service for resource type "parameter"
policy_sentry query action-table --service ssm --access-level write --resource-type parameter

# Get a list of all IAM Actions available to the RAM service
policy_sentry query action-table --service ram
Expand All @@ -21,6 +24,7 @@ policy_sentry query action-table --service ram --access-level permissions-manage

# Get a list of all IAM actions under the SES service that support the `ses:FeedbackAddress` condition key.
policy_sentry query action-table --service ses --condition ses:FeedbackAddress

```

Options
Expand All @@ -42,10 +46,8 @@ Options:
--condition TEXT If action table is chosen, you can supply a
condition key to show a list of all IAM
actions that support the condition key.
--wildcard-only If action table is chosen, show the IAM
actions that only support wildcard resources
- i.e., cannot support ARNs in the resource
block.
--resource-type TEXT Supply a resource type to show a list of all
IAM actions that support the resource type.
--fmt [yaml|json] Format output as YAML or JSON. Defaults to
"yaml"
--v Set the logging level. Choices are CRITICAL, ERROR, WARNING, INFO, or DEBUG. Defaults to INFO
Expand Down Expand Up @@ -624,7 +626,7 @@ All IAM actions under the s3 service that have the access level permissions-mana
### Actions for S3 service at Write access level that only support wildcard resources (*)

<details open>
<summary>policy_sentry query action-table --service s3 --access-level read --wildcard-only --fmt yaml</summary>
<summary>policy_sentry query action-table --service s3 --access-level read --resource-type '*' --fmt yaml</summary>
<br>
<pre>
<code>
Expand All @@ -640,7 +642,7 @@ s3 READ actions that must use wildcards in the resources block:
### Actions for S3 service at any access level that only support wildcard resources (*)

<details open>
<summary>policy_sentry query action-table --service s3 --wildcard-only --fmt yaml</summary>
<summary>policy_sentry query action-table --service s3 --resource-type '*' --fmt yaml</summary>
<br>
<pre>
<code>
Expand Down
33 changes: 15 additions & 18 deletions policy_sentry/command/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
get_actions_for_service,
get_actions_with_access_level,
get_action_data,
get_actions_that_support_wildcard_arns_only,
get_actions_matching_condition_key,
get_actions_at_access_level_that_support_wildcard_arns_only,
get_actions_with_arn_type_and_access_level,
get_actions_matching_arn_type
)
from policy_sentry.querying.conditions import (
get_condition_keys_for_service,
Expand Down Expand Up @@ -65,11 +65,10 @@ def query():
help="Supply a condition key to show a list of all IAM actions that support the condition key.",
)
@click.option(
"--wildcard-only",
is_flag=True,
"--resource-type",
type=str,
required=False,
help="Show the IAM actions that only support "
"wildcard resources - i.e., cannot support ARNs in the resource block.",
help="Supply a resource type to show a list of all IAM actions that support the resource type.",
)
@click.option(
"--fmt",
Expand All @@ -79,13 +78,13 @@ def query():
help='Format output as YAML or JSON. Defaults to "yaml"',
)
@click_log.simple_verbosity_option(logger)
def action_table(name, service, access_level, condition, wildcard_only, fmt):
def action_table(name, service, access_level, condition, resource_type, fmt):
"""Query the Action Table from the Policy Sentry database"""
query_action_table(name, service, access_level, condition, wildcard_only, fmt)
query_action_table(name, service, access_level, condition, resource_type, fmt)


def query_action_table(
name, service, access_level, condition, wildcard_only, fmt="json"
name, service, access_level, condition, resource_type, fmt="json"
):
"""Query the Action Table from the Policy Sentry database.
Use this one when leveraging Policy Sentry as a library."""
Expand Down Expand Up @@ -114,7 +113,7 @@ def query_action_table(
print(yaml.dump(output)) if fmt == "yaml" else [
print(item) for item in output
]
elif name is None and access_level and not wildcard_only:
elif name is None and access_level and not resource_type:
print(
f"All IAM actions under the {service} service that have the access level {access_level}:"
)
Expand All @@ -123,14 +122,12 @@ def query_action_table(
print(yaml.dump(output)) if fmt == "yaml" else [
print(json.dumps(output, indent=4))
]
elif name is None and access_level and wildcard_only:
elif name is None and access_level and resource_type:
print(
f"{service} {access_level.upper()} actions that must use wildcards in the resources block:"
f"{service} {access_level.upper()} actions that have the resource type {resource_type.upper()}:"
)
access_level = transform_access_level_text(access_level)
output = get_actions_at_access_level_that_support_wildcard_arns_only(
service, access_level
)
output = get_actions_with_arn_type_and_access_level(service, resource_type, access_level)
print(yaml.dump(output)) if fmt == "yaml" else [
print(json.dumps(output, indent=4))
]
Expand All @@ -145,11 +142,11 @@ def query_action_table(
]
# Get a list of IAM Actions under the service that only support resources = "*"
# (i.e., you cannot restrict it according to ARN)
elif wildcard_only:
elif resource_type:
print(
f"IAM actions under {service} service that support wildcard resource values only:"
f"IAM actions under {service} service that have the resource type {resource_type}:"
)
output = get_actions_that_support_wildcard_arns_only(service)
output = get_actions_matching_arn_type(service, resource_type)
print(yaml.dump(output)) if fmt == "yaml" else [
print(json.dumps(output, indent=4))
]
Expand Down
105 changes: 77 additions & 28 deletions policy_sentry/querying/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,33 +95,29 @@ def get_action_data(service, action_name):
# raise Exception("Unknown action {}:{}".format(service, action_name))


def get_actions_that_support_wildcard_arns_only(service_prefix):
def get_actions_with_access_level(service_prefix, access_level):
"""
Get a list of actions that do not support restricting the action to resource ARNs.
Set service to "all" to get a list of actions across all services.
Get a list of actions in a service under different access levels.
Arguments:
service_prefix: A single AWS service prefix, like `s3` or `kms`
access_level: An access level as it is written in the database, such as 'Read', 'Write', 'List', 'Permisssions management', or 'Tagging'
Returns:
List: A list of actions that do not support resource ARN constraints
List: A list of actions with that access level and service prefix
"""
results = []
if service_prefix == "all":
for some_prefix in all_service_prefixes:
service_prefix_data = get_service_prefix_data(some_prefix)
for action_name, action_data in service_prefix_data["privileges"].items():
if len(action_data["resource_types"].keys()) == 1:
for resource_type in action_data["resource_types"]:
if resource_type == '':
results.append(f"{service_prefix}:{action_name}")
if action_data["access_level"] == access_level:
results.append(f"{some_prefix}:{action_data['privilege']}")
else:
service_prefix_data = get_service_prefix_data(service_prefix)
for action_name, action_data in service_prefix_data["privileges"].items():
if len(action_data["resource_types"].keys()) == 1:
for resource_type in action_data["resource_types"]:
if resource_type == '':
results.append(f"{service_prefix}:{action_name}")
if action_data["access_level"] == access_level:
results.append(f"{service_prefix}:{action_data['privilege']}")
return results


Expand Down Expand Up @@ -161,50 +157,103 @@ def get_actions_at_access_level_that_support_wildcard_arns_only(
return results


def get_actions_with_access_level(service_prefix, access_level):
def get_actions_with_arn_type_and_access_level(
service_prefix, resource_type_name, access_level
):
"""
Get a list of actions in a service under different access levels.
Get a list of actions in a service under different access levels, specific to an ARN format.
Arguments:
service_prefix: A single AWS service prefix, like `s3` or `kms`
resource_type_name: The ARN type name, like `bucket` or `key`
access_level: Access level like "Read" or "List" or "Permissions management"
Return:
List: A list of actions that have that ARN type and Access level
"""
service_prefix_data = get_service_prefix_data(service_prefix)
results = []

if resource_type_name == '*':
return get_actions_at_access_level_that_support_wildcard_arns_only(service_prefix, access_level)

if service_prefix == "all":
for some_prefix in all_service_prefixes:
service_prefix_data = get_service_prefix_data(some_prefix)
for action_name, action_data in service_prefix_data["privileges"].items():
if action_data["access_level"] == access_level:
for resource_name, resource_data in action_data["resource_types"].items():
this_resource_type = resource_data["resource_type"].strip("*")
if this_resource_type.lower() == resource_type_name.lower():
results.append(f"{service_prefix}:{action_data['privilege']}")
break
else:
for action_name, action_data in service_prefix_data["privileges"].items():
if action_data["access_level"] == access_level:
for resource_name, resource_data in action_data["resource_types"].items():
this_resource_type = resource_data["resource_type"].strip("*")
if this_resource_type.lower() == resource_type_name.lower():
results.append(f"{service_prefix}:{action_data['privilege']}")
break
return results


def get_actions_that_support_wildcard_arns_only(service_prefix):
"""
Get a list of actions that do not support restricting the action to resource ARNs.
Set service to "all" to get a list of actions across all services.
Arguments:
service_prefix: A single AWS service prefix, like `s3` or `kms`
access_level: An access level as it is written in the database, such as 'Read', 'Write', 'List', 'Permisssions management', or 'Tagging'
Returns:
List: A list of actions with that access level and service prefix
List: A list of actions that do not support resource ARN constraints
"""
results = []
if service_prefix == "all":
for some_prefix in all_service_prefixes:
service_prefix_data = get_service_prefix_data(some_prefix)
for action_name, action_data in service_prefix_data["privileges"].items():
if action_data["access_level"] == access_level:
results.append(f"{some_prefix}:{action_data['privilege']}")
if len(action_data["resource_types"].keys()) == 1:
for resource_type in action_data["resource_types"]:
if resource_type == '':
results.append(f"{service_prefix}:{action_name}")
else:
service_prefix_data = get_service_prefix_data(service_prefix)
for action_name, action_data in service_prefix_data["privileges"].items():
if action_data["access_level"] == access_level:
results.append(f"{service_prefix}:{action_data['privilege']}")
if len(action_data["resource_types"].keys()) == 1:
for resource_type in action_data["resource_types"]:
if resource_type == '':
results.append(f"{service_prefix}:{action_name}")
return results


def get_actions_with_arn_type_and_access_level(
service_prefix, resource_type_name, access_level
):
def get_actions_matching_arn_type(service_prefix, resource_type_name):
"""
Get a list of actions in a service under different access levels, specific to an ARN format.
Get a list of actions in a service specific to ARN type.
Arguments:
service_prefix: A single AWS service prefix, like `s3` or `kms`
resource_type_name: The ARN type name, like `bucket` or `key`
access_level: Access level like "Read" or "List" or "Permissions management"
Return:
List: A list of actions that have that ARN type and Access level
List: A list of actions that have that ARN type
"""
if resource_type_name == '*':
return get_actions_that_support_wildcard_arns_only(service_prefix)

service_prefix_data = get_service_prefix_data(service_prefix)
results = []

for action_name, action_data in service_prefix_data["privileges"].items():
if action_data["access_level"] == access_level:
if service_prefix == "all":
for some_prefix in all_service_prefixes:
service_prefix_data = get_service_prefix_data(some_prefix)
for action_name, action_data in service_prefix_data["privileges"].items():
for resource_name, resource_data in action_data["resource_types"].items():
this_resource_type = resource_data["resource_type"].strip("*")
if this_resource_type.lower() == resource_type_name.lower():
results.append(f"{service_prefix}:{action_data['privilege']}")
break
else:
for action_name, action_data in service_prefix_data["privileges"].items():
for resource_name, resource_data in action_data["resource_types"].items():
this_resource_type = resource_data["resource_type"].strip("*")
if this_resource_type.lower() == resource_type_name.lower():
Expand Down
4 changes: 4 additions & 0 deletions tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ def query(c):
c.run('./policy_sentry/bin/cli.py query action-table --service ram --name tagresource', pty=True)
c.run('./policy_sentry/bin/cli.py query action-table '
'--service ram --access-level permissions-management', pty=True)
c.run('./policy_sentry/bin/cli.py query action-table --service ssm --resource-type parameter', pty=True)
c.run('./policy_sentry/bin/cli.py query action-table --service ssm --access-level write '
'--resource-type parameter', pty=True)
c.run('policy_sentry query action-table --service ssm --resource-type parameter', pty=True)
c.run('./policy_sentry/bin/cli.py query action-table --service ses --condition ses:FeedbackAddress', pty=True)
c.run('echo "Querying the ARN table"', pty=True)
c.run('./policy_sentry/bin/cli.py query arn-table --service ssm', pty=True)
Expand Down
Loading

0 comments on commit 8bb7a70

Please sign in to comment.