Skip to content

Commit c0e90b5

Browse files
committed
add query command for service data
1 parent 36dbd37 commit c0e90b5

File tree

3 files changed

+90
-5
lines changed

3 files changed

+90
-5
lines changed

policy_sentry/command/query.py

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import click
1313
import yaml
14+
15+
from policy_sentry.querying.services import get_services_data
1416
from policy_sentry.util.access_levels import transform_access_level_text
1517
from policy_sentry.querying.all import get_all_service_prefixes
1618
from policy_sentry.querying.arns import (
@@ -47,8 +49,17 @@ def print_list(output: Any, fmt: str = "json") -> None:
4749

4850

4951
def print_dict(output: Any, fmt: str = "json") -> None:
50-
"""Common method on how to print a dict, depending on whether the user requests JSON or YAML output"""
51-
print(yaml.dump(output)) if fmt == "yaml" else [print(json.dumps(output, indent=4))]
52+
"""Common method on how to print a dict, depending on whether the user requests JSON, YAML or CSV output"""
53+
if fmt == "csv":
54+
if not output:
55+
return None
56+
print(",".join(output[0].keys()))
57+
for entry in output:
58+
print(",".join(entry.values()))
59+
elif fmt == "json":
60+
print(json.dumps(output, indent=4))
61+
elif fmt == "yaml":
62+
print(yaml.dump(output))
5263

5364

5465
@click.group()
@@ -97,7 +108,7 @@ def query() -> None:
97108
type=click.Choice(["yaml", "json"]),
98109
default="json",
99110
required=False,
100-
help='Format output as YAML or JSON. Defaults to "yaml"',
111+
help='Format output as YAML or JSON. Defaults to "json"',
101112
)
102113
@click.option(
103114
"--verbose",
@@ -229,7 +240,7 @@ def query_action_table(
229240
type=click.Choice(["yaml", "json"]),
230241
default="json",
231242
required=False,
232-
help='Format output as YAML or JSON. Defaults to "yaml"',
243+
help='Format output as YAML or JSON. Defaults to "json"',
233244
)
234245
@click.option(
235246
"--verbose",
@@ -296,7 +307,7 @@ def query_arn_table(
296307
type=click.Choice(["yaml", "json"]),
297308
default="json",
298309
required=False,
299-
help='Format output as YAML or JSON. Defaults to "yaml"',
310+
help='Format output as YAML or JSON. Defaults to "json"',
300311
)
301312
@click.option(
302313
"--verbose",
@@ -334,3 +345,42 @@ def query_condition_table(
334345
output = get_condition_key_details(service, name)
335346
print_dict(output=output, fmt=fmt)
336347
return output
348+
349+
350+
@query.command(short_help="Query the service table.")
351+
@click.option(
352+
"--fmt",
353+
type=click.Choice(["yaml", "json", "csv"]),
354+
default="json",
355+
required=False,
356+
help='Format output as YAML, JSON or CSV. Defaults to "json"',
357+
)
358+
@click.option(
359+
"--verbose",
360+
"-v",
361+
type=click.Choice(
362+
["critical", "error", "warning", "info", "debug"], case_sensitive=False
363+
),
364+
)
365+
def service_table(fmt: str, verbose: str | None) -> None:
366+
"""Query the service table from the Policy Sentry database"""
367+
if verbose:
368+
log_level = getattr(logging, verbose.upper())
369+
set_stream_logger(level=log_level)
370+
query_service_table(fmt)
371+
372+
373+
def query_service_table(fmt: str = "json") -> list[dict[str, str]]:
374+
"""Query the service table from the Policy Sentry database.
375+
Use this one when leveraging Policy Sentry as a library."""
376+
if os.path.exists(LOCAL_DATASTORE_FILE_PATH):
377+
logger.info(
378+
f"Using the Local IAM definition: {LOCAL_DATASTORE_FILE_PATH}. To leverage the bundled definition instead, remove the folder $HOME/.policy_sentry/"
379+
)
380+
else:
381+
# Otherwise, leverage the datastore inside the python package
382+
logger.debug("Leveraging the bundled IAM Definition.")
383+
384+
output = get_services_data()
385+
print_dict(output=output, fmt=fmt)
386+
return output

policy_sentry/querying/services.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
"""
2+
Methods that execute specific queries against the SQLite database for the SERIVCES table.
3+
This supports the policy_sentry query functionality
4+
"""
5+
6+
from __future__ import annotations
7+
8+
from policy_sentry.shared.iam_data import iam_definition
9+
10+
11+
def get_services_data() -> list[dict[str, str]]:
12+
return [
13+
{
14+
"prefix": service_prefix,
15+
"service_name": data["service_name"],
16+
}
17+
for service_prefix, data in iam_definition.items()
18+
if isinstance(data, dict)
19+
]

test/querying/test_query_services.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import unittest
2+
3+
from policy_sentry.querying.services import get_services_data
4+
5+
6+
class QueryServicesTestCase(unittest.TestCase):
7+
def test_get_services_data(self):
8+
# when
9+
results = get_services_data()
10+
11+
# then
12+
self.assertGreater(len(results), 400) # in 07/24 it was 405
13+
14+
# both should be a non-empty string
15+
self.assertTrue(results[0]["prefix"])
16+
self.assertTrue(results[0]["service_name"])

0 commit comments

Comments
 (0)