dmint-cli 0.1.0__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dmint_cli-0.2.0/MANIFEST.in +1 -0
- dmint_cli-0.2.0/PKG-INFO +97 -0
- dmint_cli-0.2.0/README.md +86 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/pyproject.toml +5 -2
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli/__main__.py +20 -8
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli/api.py +50 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli/compile_policy.py +14 -31
- dmint_cli-0.2.0/src/dmint_cli/create_policy.py +379 -0
- dmint_cli-0.2.0/src/dmint_cli/prompts/__init__.py +1 -0
- dmint_cli-0.2.0/src/dmint_cli/prompts/policy_skill.md +49 -0
- dmint_cli-0.2.0/src/dmint_cli/verify_policy.py +92 -0
- dmint_cli-0.2.0/src/dmint_cli.egg-info/PKG-INFO +97 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli.egg-info/SOURCES.txt +6 -1
- dmint_cli-0.2.0/src/dmint_cli.egg-info/requires.txt +1 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/tests/test_cli_api.py +32 -0
- dmint_cli-0.2.0/tests/test_create_policy.py +214 -0
- dmint_cli-0.2.0/tests/test_verify_policy.py +68 -0
- dmint_cli-0.1.0/PKG-INFO +0 -79
- dmint_cli-0.1.0/README.md +0 -67
- dmint_cli-0.1.0/src/dmint_cli/create_policy.py +0 -230
- dmint_cli-0.1.0/src/dmint_cli.egg-info/PKG-INFO +0 -79
- dmint_cli-0.1.0/src/dmint_cli.egg-info/requires.txt +0 -2
- dmint_cli-0.1.0/tests/test_create_policy.py +0 -95
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/LICENSE +0 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/setup.cfg +0 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli/__init__.py +0 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli.egg-info/dependency_links.txt +0 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli.egg-info/entry_points.txt +0 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/src/dmint_cli.egg-info/top_level.txt +0 -0
- {dmint_cli-0.1.0 → dmint_cli-0.2.0}/tests/test_compile_policy.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
recursive-include src/dmint_cli/prompts *.md
|
dmint_cli-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dmint-cli
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: CLI tooling for Dmint policy creation, compilation, and management
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: dmint<0.2.0,>=0.1.0
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# dmint-cli (v0.2.0)
|
|
13
|
+
|
|
14
|
+
> **Developer tooling for interactive creation, compilation, and validation of Dmint security policies.**
|
|
15
|
+
|
|
16
|
+
`dmint-cli` provides command-line utilities for discovering tools via static AST analysis, interactive multi-turn policy authoring via LLM model selection and clarification loops, and deterministic schema/semantic policy verification (`policy.json`).
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
access.md (Human Security Intent)
|
|
20
|
+
↓
|
|
21
|
+
dmint create-policy (Multi-turn Wizard & Model Discovery)
|
|
22
|
+
↓
|
|
23
|
+
Self-Correcting LLM Loop (clarification_needed / policy_ready)
|
|
24
|
+
↓
|
|
25
|
+
Dmint Core Validation (Policy.from_mapping)
|
|
26
|
+
↓
|
|
27
|
+
dmint verify-policy (Pure Deterministic Verification)
|
|
28
|
+
↓
|
|
29
|
+
policy.json (Authoritative Policy)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
> **Note:** The CLI helps developers *author* and *validate* policies during development. `dmint` core remains the sole trusted runtime authorization engine.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install dmint-cli
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## CLI Commands
|
|
45
|
+
|
|
46
|
+
### 1. Interactive Policy Wizard (`dmint create-policy`)
|
|
47
|
+
|
|
48
|
+
Interactive, multi-turn wizard to create and validate a Dmint policy:
|
|
49
|
+
- Interactive provider and model discovery (`list_models`).
|
|
50
|
+
- AST-based static tool discovery via `--tools` parameter.
|
|
51
|
+
- Multi-turn envelope output contract (`clarification_needed` & `policy_ready`).
|
|
52
|
+
- Self-correcting validation retry loop on `PolicyError`.
|
|
53
|
+
- Atomic file write and immediate verification.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
dmint create-policy -f access.md -o policy.json --tools my_tools.py
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Standalone Policy Verification (`dmint verify-policy`)
|
|
60
|
+
|
|
61
|
+
Pure, deterministic schema and semantic validation of `policy.json` without LLM involvement. Returns exit code `0` on success or non-zero on validation failure:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
dmint verify-policy policy.json
|
|
65
|
+
# or
|
|
66
|
+
dmint verify-policy -f policy.json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Environment Configuration
|
|
72
|
+
|
|
73
|
+
Set API credentials via environment variables for LLM policy compilation:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
export OPENAI_API_KEY="sk-..."
|
|
77
|
+
# Optional custom providers (openai, gemini, groq, openrouter, ollama)
|
|
78
|
+
export GEMINI_API_KEY="..."
|
|
79
|
+
export GROQ_API_KEY="..."
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Testing
|
|
85
|
+
|
|
86
|
+
Run the CLI test suite:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
python3 -m unittest discover -s tests -p "test_*.py"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
Licensed under the [Apache License, Version 2.0](LICENSE).
|
|
97
|
+
See the [`LICENSE`](LICENSE) file for the complete license text.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# dmint-cli (v0.2.0)
|
|
2
|
+
|
|
3
|
+
> **Developer tooling for interactive creation, compilation, and validation of Dmint security policies.**
|
|
4
|
+
|
|
5
|
+
`dmint-cli` provides command-line utilities for discovering tools via static AST analysis, interactive multi-turn policy authoring via LLM model selection and clarification loops, and deterministic schema/semantic policy verification (`policy.json`).
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
access.md (Human Security Intent)
|
|
9
|
+
↓
|
|
10
|
+
dmint create-policy (Multi-turn Wizard & Model Discovery)
|
|
11
|
+
↓
|
|
12
|
+
Self-Correcting LLM Loop (clarification_needed / policy_ready)
|
|
13
|
+
↓
|
|
14
|
+
Dmint Core Validation (Policy.from_mapping)
|
|
15
|
+
↓
|
|
16
|
+
dmint verify-policy (Pure Deterministic Verification)
|
|
17
|
+
↓
|
|
18
|
+
policy.json (Authoritative Policy)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
> **Note:** The CLI helps developers *author* and *validate* policies during development. `dmint` core remains the sole trusted runtime authorization engine.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install dmint-cli
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## CLI Commands
|
|
34
|
+
|
|
35
|
+
### 1. Interactive Policy Wizard (`dmint create-policy`)
|
|
36
|
+
|
|
37
|
+
Interactive, multi-turn wizard to create and validate a Dmint policy:
|
|
38
|
+
- Interactive provider and model discovery (`list_models`).
|
|
39
|
+
- AST-based static tool discovery via `--tools` parameter.
|
|
40
|
+
- Multi-turn envelope output contract (`clarification_needed` & `policy_ready`).
|
|
41
|
+
- Self-correcting validation retry loop on `PolicyError`.
|
|
42
|
+
- Atomic file write and immediate verification.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
dmint create-policy -f access.md -o policy.json --tools my_tools.py
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Standalone Policy Verification (`dmint verify-policy`)
|
|
49
|
+
|
|
50
|
+
Pure, deterministic schema and semantic validation of `policy.json` without LLM involvement. Returns exit code `0` on success or non-zero on validation failure:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
dmint verify-policy policy.json
|
|
54
|
+
# or
|
|
55
|
+
dmint verify-policy -f policy.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Environment Configuration
|
|
61
|
+
|
|
62
|
+
Set API credentials via environment variables for LLM policy compilation:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export OPENAI_API_KEY="sk-..."
|
|
66
|
+
# Optional custom providers (openai, gemini, groq, openrouter, ollama)
|
|
67
|
+
export GEMINI_API_KEY="..."
|
|
68
|
+
export GROQ_API_KEY="..."
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Testing
|
|
74
|
+
|
|
75
|
+
Run the CLI test suite:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python3 -m unittest discover -s tests -p "test_*.py"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
Licensed under the [Apache License, Version 2.0](LICENSE).
|
|
86
|
+
See the [`LICENSE`](LICENSE) file for the complete license text.
|
|
@@ -4,16 +4,19 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dmint-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "CLI tooling for Dmint policy creation, compilation, and management"
|
|
9
9
|
license = "Apache-2.0"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
readme = "README.md"
|
|
12
12
|
dependencies = [
|
|
13
13
|
"dmint>=0.1.0,<0.2.0",
|
|
14
|
-
"dmint-skills>=0.1.0,<0.2.0",
|
|
15
14
|
]
|
|
16
15
|
|
|
16
|
+
[tool.setuptools.package-data]
|
|
17
|
+
"dmint_cli" = ["prompts/*.md"]
|
|
18
|
+
"dmint_cli.prompts" = ["*.md"]
|
|
19
|
+
|
|
17
20
|
[project.scripts]
|
|
18
21
|
dmint = "dmint_cli.__main__:main"
|
|
19
22
|
|
|
@@ -7,13 +7,14 @@ import sys
|
|
|
7
7
|
|
|
8
8
|
from dmint_cli.compile_policy import main_compile
|
|
9
9
|
from dmint_cli.create_policy import main_create
|
|
10
|
+
from dmint_cli.verify_policy import main_verify
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def main(args: list[str] | None = None) -> int:
|
|
13
14
|
if args is None:
|
|
14
15
|
args = sys.argv[1:]
|
|
15
16
|
|
|
16
|
-
# Normalize subcommands
|
|
17
|
+
# Normalize two-word subcommands: "create policy" -> "create-policy", "verify policy" -> "verify-policy", etc.
|
|
17
18
|
normalized_args: list[str] = []
|
|
18
19
|
i = 0
|
|
19
20
|
while i < len(args):
|
|
@@ -22,6 +23,10 @@ def main(args: list[str] | None = None) -> int:
|
|
|
22
23
|
normalized_args.append("create-policy")
|
|
23
24
|
i += 2
|
|
24
25
|
continue
|
|
26
|
+
if arg == "verify" and i + 1 < len(args) and args[i + 1] == "policy":
|
|
27
|
+
normalized_args.append("verify-policy")
|
|
28
|
+
i += 2
|
|
29
|
+
continue
|
|
25
30
|
if arg == "compile" and i + 1 < len(args) and args[i + 1] == "policy":
|
|
26
31
|
normalized_args.append("compile-policy")
|
|
27
32
|
i += 2
|
|
@@ -36,13 +41,18 @@ def main(args: list[str] | None = None) -> int:
|
|
|
36
41
|
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
37
42
|
|
|
38
43
|
subparsers.add_parser(
|
|
39
|
-
"
|
|
40
|
-
help="
|
|
44
|
+
"create-policy",
|
|
45
|
+
help="Interactive wizard to create and validate Dmint policy files",
|
|
41
46
|
add_help=False,
|
|
42
47
|
)
|
|
43
48
|
subparsers.add_parser(
|
|
44
|
-
"
|
|
45
|
-
help="
|
|
49
|
+
"verify-policy",
|
|
50
|
+
help="Verify a policy.json file against Dmint schema and semantic invariants",
|
|
51
|
+
add_help=False,
|
|
52
|
+
)
|
|
53
|
+
subparsers.add_parser(
|
|
54
|
+
"compile-policy",
|
|
55
|
+
help="Compile natural language policy requirements into a validated policy.json file",
|
|
46
56
|
add_help=False,
|
|
47
57
|
)
|
|
48
58
|
|
|
@@ -53,10 +63,12 @@ def main(args: list[str] | None = None) -> int:
|
|
|
53
63
|
cmd = normalized_args[0]
|
|
54
64
|
cmd_args = normalized_args[1:]
|
|
55
65
|
|
|
56
|
-
if cmd == "
|
|
57
|
-
return main_compile(cmd_args)
|
|
58
|
-
elif cmd == "create-policy":
|
|
66
|
+
if cmd == "create-policy":
|
|
59
67
|
return main_create(cmd_args)
|
|
68
|
+
elif cmd == "verify-policy":
|
|
69
|
+
return main_verify(cmd_args)
|
|
70
|
+
elif cmd == "compile-policy":
|
|
71
|
+
return main_compile(cmd_args)
|
|
60
72
|
else:
|
|
61
73
|
parser.print_help()
|
|
62
74
|
return 2
|
|
@@ -154,6 +154,56 @@ class OpenAICompatClient:
|
|
|
154
154
|
|
|
155
155
|
return extract_json_text(content)
|
|
156
156
|
|
|
157
|
+
def list_models(self) -> list[str]:
|
|
158
|
+
"""Call GET {base_url}/models and return a list of available model IDs."""
|
|
159
|
+
endpoint = f"{self.base_url}/models"
|
|
160
|
+
headers = {
|
|
161
|
+
"User-Agent": "dmint-policy-author/0.1.0",
|
|
162
|
+
}
|
|
163
|
+
if self.api_key:
|
|
164
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
165
|
+
|
|
166
|
+
req = urllib.request.Request(
|
|
167
|
+
endpoint,
|
|
168
|
+
headers=headers,
|
|
169
|
+
method="GET",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
174
|
+
resp_bytes = resp.read()
|
|
175
|
+
try:
|
|
176
|
+
resp_data = json.loads(resp_bytes.decode("utf-8"))
|
|
177
|
+
except Exception as exc:
|
|
178
|
+
raise RuntimeError("API response is not valid JSON") from exc
|
|
179
|
+
except urllib.error.HTTPError as exc:
|
|
180
|
+
try:
|
|
181
|
+
error_body = exc.read().decode("utf-8", errors="ignore")
|
|
182
|
+
error_json = json.loads(error_body)
|
|
183
|
+
msg = error_json.get("error", {}).get("message", error_body)
|
|
184
|
+
except Exception:
|
|
185
|
+
msg = f"HTTP Error {exc.code}"
|
|
186
|
+
masked_key = mask_secret(self.api_key)
|
|
187
|
+
raise RuntimeError(f"OpenAI-compatible API HTTP {exc.code}: {msg} (key: {masked_key})") from exc
|
|
188
|
+
except urllib.error.URLError as exc:
|
|
189
|
+
raise RuntimeError(f"API connection failed ({self.base_url}): {exc.reason}") from exc
|
|
190
|
+
except Exception as exc:
|
|
191
|
+
raise RuntimeError(f"API request failed: {exc}") from exc
|
|
192
|
+
|
|
193
|
+
data = resp_data.get("data")
|
|
194
|
+
if not isinstance(data, list):
|
|
195
|
+
raise RuntimeError("API response missing 'data' array in models response")
|
|
196
|
+
|
|
197
|
+
models: list[str] = []
|
|
198
|
+
for item in data:
|
|
199
|
+
if isinstance(item, dict) and "id" in item and isinstance(item["id"], str):
|
|
200
|
+
models.append(item["id"])
|
|
201
|
+
|
|
202
|
+
if not models:
|
|
203
|
+
raise RuntimeError("No model IDs found in /models response")
|
|
204
|
+
|
|
205
|
+
return models
|
|
206
|
+
|
|
157
207
|
|
|
158
208
|
def extract_json_text(text: str) -> str:
|
|
159
209
|
"""Extract clean JSON text, removing markdown code fences if present."""
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import importlib.resources
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
8
9
|
import sys
|
|
@@ -11,7 +12,6 @@ from pathlib import Path
|
|
|
11
12
|
|
|
12
13
|
from dmint.policy import Policy, PolicyError
|
|
13
14
|
from dmint_cli.api import OpenAICompatClient, mask_secret
|
|
14
|
-
from dmint_skills.skill import DMINT_POLICY_SYSTEM_PROMPT
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class CLIError(Exception):
|
|
@@ -47,6 +47,15 @@ class OutputWriteError(CLIError):
|
|
|
47
47
|
super().__init__(message, exit_code=7)
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
def load_system_prompt() -> str:
|
|
51
|
+
"""Load system prompt from bundled resource file."""
|
|
52
|
+
try:
|
|
53
|
+
return importlib.resources.files("dmint_cli.prompts").joinpath("policy_skill.md").read_text(encoding="utf-8")
|
|
54
|
+
except Exception:
|
|
55
|
+
prompt_path = Path(__file__).parent / "prompts" / "policy_skill.md"
|
|
56
|
+
return prompt_path.read_text(encoding="utf-8")
|
|
57
|
+
|
|
58
|
+
|
|
50
59
|
def atomic_write_json(output_path: Path, data: dict) -> None:
|
|
51
60
|
"""Atomically write JSON to destination using temp file replace to prevent partial writes."""
|
|
52
61
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -75,7 +84,6 @@ def compile_policy_file(
|
|
|
75
84
|
model: str | None = None,
|
|
76
85
|
provider: str | None = None,
|
|
77
86
|
timeout: float = 60.0,
|
|
78
|
-
offline_fallback: bool = True,
|
|
79
87
|
) -> Policy:
|
|
80
88
|
"""Compile a natural language policy requirement file into a verified Dmint policy JSON file."""
|
|
81
89
|
input_path = Path(input_file).resolve()
|
|
@@ -105,8 +113,10 @@ def compile_policy_file(
|
|
|
105
113
|
timeout=timeout,
|
|
106
114
|
)
|
|
107
115
|
|
|
116
|
+
system_prompt = load_system_prompt()
|
|
117
|
+
|
|
108
118
|
messages = [
|
|
109
|
-
{"role": "system", "content":
|
|
119
|
+
{"role": "system", "content": system_prompt},
|
|
110
120
|
{
|
|
111
121
|
"role": "user",
|
|
112
122
|
"content": f"Convert the following natural language policy requirements into Dmint JSON policy format:\n\n{policy_text}",
|
|
@@ -116,34 +126,7 @@ def compile_policy_file(
|
|
|
116
126
|
try:
|
|
117
127
|
raw_json = client.chat_completion(messages, temperature=0.0)
|
|
118
128
|
except Exception as exc:
|
|
119
|
-
|
|
120
|
-
print(f"[!] API unavailable or key unconfigured ({exc}). Using deterministic policy compilation fallback...\n")
|
|
121
|
-
rules = []
|
|
122
|
-
seen_rules = set()
|
|
123
|
-
for line in policy_text.splitlines():
|
|
124
|
-
l = line.lower()
|
|
125
|
-
r = None
|
|
126
|
-
if "list_services" in l or "list services" in l:
|
|
127
|
-
r = ("allow", "deployment", "list_services", "*")
|
|
128
|
-
elif "get_service_status" in l or "read service status" in l:
|
|
129
|
-
r = ("allow", "deployment", "get_service_status", "*")
|
|
130
|
-
elif "deploy_service" in l or "deploy" in l:
|
|
131
|
-
eff = "approval_required" if "approval" in l or "production" in l else "allow"
|
|
132
|
-
r = (eff, "deployment", "deploy_service", "*")
|
|
133
|
-
elif "rollback_service" in l or "rollback" in l:
|
|
134
|
-
r = ("approval_required", "deployment", "rollback_service", "*")
|
|
135
|
-
elif "scale_service" in l or "scale" in l:
|
|
136
|
-
r = ("approval_required", "deployment", "scale_service", "*")
|
|
137
|
-
elif "delete_service" in l or "delete" in l:
|
|
138
|
-
r = ("deny", "deployment", "delete_service", "*")
|
|
139
|
-
|
|
140
|
-
if r and r not in seen_rules:
|
|
141
|
-
seen_rules.add(r)
|
|
142
|
-
rules.append({"effect": r[0], "tool": r[1], "action": r[2], "resource": r[3]})
|
|
143
|
-
|
|
144
|
-
raw_json = json.dumps({"rules": rules})
|
|
145
|
-
else:
|
|
146
|
-
raise APIError(str(exc)) from exc
|
|
129
|
+
raise APIError(str(exc)) from exc
|
|
147
130
|
|
|
148
131
|
try:
|
|
149
132
|
policy_data = json.loads(raw_json)
|