iam-floyd 0.847.0 → 0.848.0
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.
- package/.markdownlint-cli2.yaml +12 -0
- package/.prettierignore +4 -0
- package/.yamllint.yaml +13 -0
- package/AGENTS.md +44 -14
- package/README.md +8 -7
- package/doc8.ini +3 -0
- package/eslint.config.mjs +122 -0
- package/lib/collection/allowEc2InstanceDeleteByOwner.d.ts +2 -2
- package/lib/collection/allowEc2InstanceDeleteByOwner.js +3 -3
- package/lib/collection/index.d.ts +2 -1
- package/lib/shared/compact.d.ts +12 -0
- package/lib/shared/compact.js +121 -0
- package/lib/shared/policy-statement/1-base.d.ts +0 -6
- package/lib/shared/policy-statement/1-base.js +0 -7
- package/lib/shared/policy-statement/10-final.d.ts +0 -1
- package/lib/shared/policy-statement/10-final.js +24 -5
- package/lib/shared/policy-statement/2-conditions.d.ts +0 -10
- package/lib/shared/policy-statement/2-conditions.js +9 -70
- package/lib/shared/policy-statement/3-actions.d.ts +7 -9
- package/lib/shared/policy-statement/3-actions.js +61 -109
- package/lib/shared/policy-statement/4-resources.d.ts +4 -10
- package/lib/shared/policy-statement/4-resources.js +2 -44
- package/lib/shared/policy-statement/5-effect.d.ts +0 -6
- package/lib/shared/policy-statement/5-effect.js +0 -8
- package/lib/shared/policy-statement/8-principals.d.ts +0 -9
- package/lib/shared/policy-statement/8-principals.js +0 -23
- package/lint/checkstyle-suppressions.xml +9 -0
- package/lint/go.mod +28 -0
- package/lint/go.sum +45 -0
- package/lint/pom.xml +41 -0
- package/lint/requirements.txt +6 -0
- package/package.json +16 -26
- package/python/iam_floyd/__init__.py +42 -0
- package/python/iam_floyd/_js.py +104 -0
- package/python/iam_floyd/py.typed +0 -0
- package/python/pyproject.toml +39 -0
- package/ruff.toml +11 -0
- package/skills/iam-floyd-usage/SKILL.md +4 -3
- package/.eslintrc +0 -31
package/.prettierignore
ADDED
package/.yamllint.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
extends: default
|
|
2
|
+
|
|
3
|
+
ignore-from-file: .gitignore
|
|
4
|
+
|
|
5
|
+
rules:
|
|
6
|
+
# Prettier formats the files and wraps long lines where it can
|
|
7
|
+
line-length: disable
|
|
8
|
+
comments:
|
|
9
|
+
min-spaces-from-content: 1
|
|
10
|
+
document-start: disable
|
|
11
|
+
truthy:
|
|
12
|
+
# `on` is a key in GitHub workflows
|
|
13
|
+
check-keys: false
|
package/AGENTS.md
CHANGED
|
@@ -22,7 +22,7 @@ IAM Floyd is an AWS IAM policy statement generator with a fluent interface. It g
|
|
|
22
22
|
|
|
23
23
|
Built in 10 numbered layers (`lib/shared/policy-statement/`):
|
|
24
24
|
|
|
25
|
-
```
|
|
25
|
+
```text
|
|
26
26
|
1-base → 2-conditions → 3-actions → 4-resources → 5-effect
|
|
27
27
|
→ 6-arn-defaults → 8-principals → 10-final (PolicyStatement)
|
|
28
28
|
```
|
|
@@ -46,6 +46,14 @@ One codebase produces two npm packages:
|
|
|
46
46
|
|
|
47
47
|
Publishing: the npm package of `cdk-iam-floyd` includes the `.jsii` (`make package-jsii publish LANGUAGES=typescript`), Python goes to PyPI and .NET to NuGet (both trusted publishing), Java to Maven Central (`bin/publish-maven`, signed bundle via the Central Portal API). Go has no registry: `bin/go-proxy` writes the module zip, which is attached to the GitHub release, and a static Go module proxy for the newest 30 releases, served by GitHub Pages under `udondan.github.io/iam-floyd/go`.
|
|
48
48
|
|
|
49
|
+
### Native Packages (transpiled core)
|
|
50
|
+
|
|
51
|
+
The standalone `iam-floyd` is being built natively for other languages, without jsii and without Node.js. The hand-written code in `lib/shared/` (the core) and `lib/collection/`, and the names of the AWS managed policies (`lib/generated/aws-managed-policies/iam-floyd.ts`), are transpiled with ts-morph by `lib/generator/transpile/` (`index.ts` collects the files of a module in import order, one backend per language, currently `python.ts`). The only imports from outside a module are the generated service classes, imported from their file (e.g. `../generated/policy-statements/ec2`). The transpiler supports only a narrow subset of TypeScript: anything else fails with file and line, so rewrite the code in supported constructs rather than extending the transpiler for single cases. The service classes are emitted from the model by `lib/generator/emit/python.ts`, with the jsii naming rules (snake_case, `if_`, `in_`), into the package `statement/`, which imports a service on first access. `bin/transpile.ts` writes `python/iam_floyd/_shared.py`, `_collection.py`, `_aws_managed_policies.py` and `statement/` (not committed). The package imports the collection and the managed policies on first access. `python/iam_floyd/_js.py` is the hand-written runtime for JavaScript semantics (number formatting, `toISOString`, sorting by UTF-16 code units, regular expressions).
|
|
52
|
+
|
|
53
|
+
`test/transpile/` runs the scenarios against TypeScript (the baseline) and each language, and diffs the output: those of `scenarios.json`, which test the core, and those that `services.ts` builds from the model, which call every method of every service. Then it runs the examples of the docs (`examples/<name>/<name>.py`) with the native package, through `python/examples.py`, which runs them with `iam_floyd` in place of `cdk_iam_floyd`, and compares them to the `.result` files with `test/jsii/examples/compare.py --standalone`, which skips the `*.cdk` examples. Last, it compares the AWS managed policies with those of TypeScript.
|
|
54
|
+
|
|
55
|
+
The Python package is built with hatchling from `python/pyproject.toml`; `bin/transpile.ts` writes the version of package.json into `iam_floyd/_version.py`. It supports Python 3.9 and newer, and has no dependencies at runtime. CI builds it and tests the wheel with the oldest and the newest supported Python; for a release, the tested wheel and sdist go to PyPI as `iam-floyd` (trusted publishing, job `publish-iam-floyd-python`).
|
|
56
|
+
|
|
49
57
|
## Development Commands
|
|
50
58
|
|
|
51
59
|
### Build
|
|
@@ -54,6 +62,7 @@ Publishing: the npm package of `cdk-iam-floyd` includes the `.jsii` (`make packa
|
|
|
54
62
|
make emit # emit lib/generated/policy-statements/ and lib/generated/index.ts from the model
|
|
55
63
|
make build # emit + tsc --build --force tsconfig.main.json tsconfig.types.json
|
|
56
64
|
make package # build + npm pack
|
|
65
|
+
make package-native # transpile and build the native packages of iam-floyd into dist/iam-floyd/ (Python: uv build)
|
|
57
66
|
make clean # remove node_modules, *.js, *.d.ts
|
|
58
67
|
make install # clean + npm i
|
|
59
68
|
```
|
|
@@ -75,11 +84,12 @@ make changelog # print the changes of the managed policies and the model s
|
|
|
75
84
|
The project has **no unit test framework**. Tests are integration-style: TypeScript examples are compiled, run, and their output is diffed against stored `.result` files.
|
|
76
85
|
|
|
77
86
|
```bash
|
|
78
|
-
make test
|
|
79
|
-
make test-typescript # same
|
|
80
|
-
make cdk-test
|
|
81
|
-
make cdk-all
|
|
82
|
-
make test-jsii
|
|
87
|
+
make test-typescript # compile examples/ + diff against *.result files (standalone)
|
|
88
|
+
make test-typescript-cdk # after `make cdk`: same for the CDK examples (examples/**/*.cdk.ts)
|
|
89
|
+
make cdk-test # CDK test: real deploy + destroy via AWS CDK
|
|
90
|
+
make cdk-all # cdk + install + build + cdk-test
|
|
91
|
+
make test-jsii # test the packages of `make package-jsii` against TypeScript; LANGUAGES=python limits the languages
|
|
92
|
+
make test-transpile # transpile, compare the scenarios of test/transpile/ with TypeScript and run the examples; PYTHON_WHEEL=<wheel> tests the built package
|
|
83
93
|
```
|
|
84
94
|
|
|
85
95
|
**Run a single example test manually:**
|
|
@@ -91,9 +101,6 @@ npx tsc -p tsconfig.test-iam-floyd.json
|
|
|
91
101
|
# 2. Run and compare output
|
|
92
102
|
node examples/allow/allow.js > /tmp/out.txt
|
|
93
103
|
diff /tmp/out.txt examples/allow/allow.result
|
|
94
|
-
|
|
95
|
-
# Or run the integration test harness directly:
|
|
96
|
-
npx ts-node test/main.ts
|
|
97
104
|
```
|
|
98
105
|
|
|
99
106
|
**Regenerate expected results** (after intentional changes):
|
|
@@ -105,9 +112,32 @@ make regenerate-code-example-results
|
|
|
105
112
|
### Linting
|
|
106
113
|
|
|
107
114
|
```bash
|
|
108
|
-
make
|
|
115
|
+
make lint # emit + bin/lint: all linters, must pass in CI
|
|
116
|
+
make lint-fix # emit + bin/lint --fix: fixes what can be fixed
|
|
117
|
+
make lint-cdk # after `make cdk`: eslint on the CDK variant
|
|
109
118
|
```
|
|
110
119
|
|
|
120
|
+
`bin/lint` runs:
|
|
121
|
+
|
|
122
|
+
| Linter | Files |
|
|
123
|
+
| ---------------------------- | ----------------------------------------------------- |
|
|
124
|
+
| eslint (`eslint.config.mjs`) | TypeScript and JavaScript, with types |
|
|
125
|
+
| prettier | everything prettier knows, except `.prettierignore` |
|
|
126
|
+
| markdownlint-cli2 | Markdown |
|
|
127
|
+
| exact versions | the dependencies of all `package.json` files |
|
|
128
|
+
| ruff, ruff format | Python (`ruff.toml`, the examples are not formatted) |
|
|
129
|
+
| yamllint | YAML (`.yamllint.yaml`) |
|
|
130
|
+
| doc8 | reStructuredText in `docs/source` (`doc8.ini`) |
|
|
131
|
+
| zizmor, actionlint | GitHub workflows |
|
|
132
|
+
| shellcheck, shfmt | shell scripts, found by their shebang |
|
|
133
|
+
| gofmt | Go |
|
|
134
|
+
| dotnet format whitespace | C# in `examples/` and `test/jsii/` |
|
|
135
|
+
| checkstyle (Google style) | Java in `examples/` and `test/jsii/` (`lint/pom.xml`) |
|
|
136
|
+
|
|
137
|
+
The versions of the linters are pinned, so Renovate updates them: npm packages in `package.json`, Python tools in `lint/requirements.txt` (run with `uv`), Go tools in `lint/go.mod` (`go tool`) and checkstyle in `lint/pom.xml`. Locally, linters whose runtime (uv, Go, .NET, Maven) is missing are skipped; in CI they fail. In the CDK variant, eslint uses `tsconfig.lint-cdk.json`, which resolves `cdk-iam-floyd` to `lib/`, and prettier is off because `mkcdk` writes unformatted code.
|
|
138
|
+
|
|
139
|
+
The actions in the workflows are pinned to commit SHAs with the version as comment.
|
|
140
|
+
|
|
111
141
|
### CDK Variant
|
|
112
142
|
|
|
113
143
|
```bash
|
|
@@ -177,13 +207,13 @@ Allowed manual edits:
|
|
|
177
207
|
- **No trailing whitespace**
|
|
178
208
|
- Line endings: LF (`\n`)
|
|
179
209
|
|
|
180
|
-
Run `make
|
|
210
|
+
Run `make lint` to check; Prettier is enforced via `eslint-plugin-prettier` and for the other files by `bin/lint`. `.editorconfig` sets 4 spaces for Python and C#, and tabs for Go.
|
|
181
211
|
|
|
182
212
|
### TypeScript
|
|
183
213
|
|
|
184
214
|
Strict settings enforced in `tsconfig.json`:
|
|
185
215
|
|
|
186
|
-
```
|
|
216
|
+
```text
|
|
187
217
|
strict: true
|
|
188
218
|
noImplicitAny: true
|
|
189
219
|
strictNullChecks: true
|
|
@@ -198,7 +228,7 @@ target: ES2020, module: CommonJS
|
|
|
198
228
|
- **Unused vars**: Prefix with `_` to suppress (`argsIgnorePattern: ^_`).
|
|
199
229
|
- **Naming conventions**: Enforced via `@typescript-eslint/naming-convention`. Use `camelCase` for variables/functions, `PascalCase` for classes/interfaces.
|
|
200
230
|
- **Template literals**: Prefer `` `${x}` `` over `'a' + x` (`prefer-template: error`).
|
|
201
|
-
- **Deprecated APIs**:
|
|
231
|
+
- **Deprecated APIs**: `@typescript-eslint/no-deprecated` rule is set to `error` — do not use deprecated APIs.
|
|
202
232
|
- **No `require()`**: Use ES `import`/`export`.
|
|
203
233
|
|
|
204
234
|
### Imports
|
|
@@ -261,6 +291,6 @@ Follow conventional commits:
|
|
|
261
291
|
- `index-managed-policies.yml` - Weekly on Sunday: updates managed policies, opens a `feat:` PR with `automerge` label
|
|
262
292
|
- `release-please.yml` - On push to main: release-please maintains the release PR (version in `package.json` and `docs/source/conf.py`, `CHANGELOG.md`). After each run, `bin/changelog-add-iam-changes` adds the changes of the managed policies and the model since the last release to the new changelog entry of the PR. Merging the PR creates the tag and a draft release, and starts `test-and-publish.yml` with the tag
|
|
263
293
|
- `automerge-schedule.yml` - Weekly on Monday: merges the release PR
|
|
264
|
-
- `test-and-publish.yml` - On PR: `make install test-typescript` + CDK deploy test + `make package-jsii test-jsii` per language. Started by `release-please.yml` with a tag: builds the packages from the tag, publishes to npm, PyPI, NuGet, Maven Central and the Go module proxy on GitHub Pages, sets the notes of the release from `CHANGELOG.md` and publishes the release
|
|
294
|
+
- `test-and-publish.yml` - On PR: `make install lint` (job `lint`), `make install test-typescript` + `make lint-cdk` + CDK deploy test + `make package-jsii test-jsii` per language + `make package-native test-transpile` against the built Python package. Started by `release-please.yml` with a tag: builds the packages from the tag, publishes to npm, PyPI, NuGet, Maven Central and the Go module proxy on GitHub Pages, sets the notes of the release from `CHANGELOG.md` and publishes the release
|
|
265
295
|
- `automerge.yml` - Auto-merges PRs labeled `automerge` after tests pass
|
|
266
296
|
- `test-docs.yml` - Builds Sphinx docs on `docs/**` changes
|
package/README.md
CHANGED
|
@@ -13,12 +13,14 @@
|
|
|
13
13
|
**AWS [IAM policy statement][statement] generator with fluent interface.**
|
|
14
14
|
|
|
15
15
|
<!-- stats -->
|
|
16
|
+
|
|
16
17
|
Support for:
|
|
17
18
|
|
|
18
19
|
- 455 Services
|
|
19
20
|
- 22036 Actions
|
|
20
21
|
- 2329 Resource Types
|
|
21
22
|
- 2484 Condition keys
|
|
23
|
+
|
|
22
24
|
<!-- /stats -->
|
|
23
25
|
|
|
24
26
|
**<br>This is an early version of the package. The API will change while I implement new features. Therefore make sure you use an exact version in your `package.json` before it reaches 1.0.0.**
|
|
@@ -70,10 +72,9 @@ This project is not affiliated, funded, or in any way associated with AWS.
|
|
|
70
72
|
|
|
71
73
|
IAM Floyd is licensed under [Apache License 2.0][license]. Dependencies might be released under different licenses.
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
[AWS CDK]: https://aws.amazon.com/cdk/
|
|
75
|
+
[source]: https://github.com/udondan/iam-floyd
|
|
76
|
+
[documentation]: https://iam-floyd.readthedocs.io/en/latest/
|
|
77
|
+
[license]: https://github.com/udondan/iam-floyd/blob/main/LICENSE
|
|
78
|
+
[statement]: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_statement.html
|
|
79
|
+
[packages]: https://iam-floyd.readthedocs.io/en/latest/packages.html
|
|
80
|
+
[AWS CDK]: https://aws.amazon.com/cdk/
|
package/doc8.ini
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import eslint from '@eslint/js';
|
|
2
|
+
import prettierRecommended from 'eslint-plugin-prettier/recommended';
|
|
3
|
+
import { defineConfig } from 'eslint/config';
|
|
4
|
+
import { readFileSync } from 'fs';
|
|
5
|
+
import globals from 'globals';
|
|
6
|
+
import tseslint from 'typescript-eslint';
|
|
7
|
+
|
|
8
|
+
// These files use the CDK variant, which only exists after `make cdk`. Before, they are linted without
|
|
9
|
+
// type information. After, all files are linted with tsconfig.lint-cdk.json, which resolves
|
|
10
|
+
// `cdk-iam-floyd` to lib/.
|
|
11
|
+
const cdkFiles = [
|
|
12
|
+
'lib/**/*.CDK.ts',
|
|
13
|
+
'lib/**/*-CDK.ts',
|
|
14
|
+
'examples/**/*.cdk.ts',
|
|
15
|
+
'test/cdk.ts',
|
|
16
|
+
'test/jsii/consumer/**/*.ts',
|
|
17
|
+
];
|
|
18
|
+
const isCdk =
|
|
19
|
+
JSON.parse(readFileSync('package.json', 'utf8')).name === 'cdk-iam-floyd';
|
|
20
|
+
|
|
21
|
+
export default defineConfig(
|
|
22
|
+
{
|
|
23
|
+
ignores: [
|
|
24
|
+
'**/node_modules/',
|
|
25
|
+
'**/cdk.out/',
|
|
26
|
+
'**/*.d.ts',
|
|
27
|
+
'lib/**/*.js',
|
|
28
|
+
'bin/**/*.js',
|
|
29
|
+
'examples/**/*.js',
|
|
30
|
+
'helper/**/*.js',
|
|
31
|
+
'test/**/*.js',
|
|
32
|
+
'tooter/**/*.js',
|
|
33
|
+
'lib/generated/',
|
|
34
|
+
'dist/',
|
|
35
|
+
'docs/build/',
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
files: ['**/*.ts'],
|
|
40
|
+
extends: [
|
|
41
|
+
eslint.configs.recommended,
|
|
42
|
+
tseslint.configs.recommendedTypeChecked,
|
|
43
|
+
tseslint.configs.stylisticTypeChecked,
|
|
44
|
+
],
|
|
45
|
+
languageOptions: {
|
|
46
|
+
parserOptions: isCdk
|
|
47
|
+
? { project: 'tsconfig.lint-cdk.json' }
|
|
48
|
+
: { projectService: true },
|
|
49
|
+
},
|
|
50
|
+
rules: {
|
|
51
|
+
'@typescript-eslint/no-unused-vars': [
|
|
52
|
+
'error',
|
|
53
|
+
{
|
|
54
|
+
argsIgnorePattern: '^_',
|
|
55
|
+
varsIgnorePattern: '^_',
|
|
56
|
+
caughtErrorsIgnorePattern: '^_',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
'@typescript-eslint/no-deprecated': 'error',
|
|
60
|
+
'@typescript-eslint/naming-convention': [
|
|
61
|
+
'error',
|
|
62
|
+
{
|
|
63
|
+
selector: 'default',
|
|
64
|
+
format: ['camelCase'],
|
|
65
|
+
leadingUnderscore: 'allow',
|
|
66
|
+
},
|
|
67
|
+
{ selector: 'import', format: ['camelCase', 'PascalCase'] },
|
|
68
|
+
{
|
|
69
|
+
selector: 'variable',
|
|
70
|
+
modifiers: ['const'],
|
|
71
|
+
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
|
|
72
|
+
leadingUnderscore: 'allow',
|
|
73
|
+
},
|
|
74
|
+
{ selector: 'typeLike', format: ['PascalCase'] },
|
|
75
|
+
{ selector: 'enumMember', format: ['camelCase', 'PascalCase'] },
|
|
76
|
+
// Keys of objects are given by the format, like IAM policies or AWS APIs
|
|
77
|
+
{ selector: 'objectLiteralProperty', format: null },
|
|
78
|
+
{ selector: 'typeProperty', format: null },
|
|
79
|
+
],
|
|
80
|
+
'prefer-template': 'error',
|
|
81
|
+
// `@ts-expect-error` fails where the line is valid in the other variant
|
|
82
|
+
'@typescript-eslint/ban-ts-comment': [
|
|
83
|
+
'error',
|
|
84
|
+
{ 'ts-ignore': 'allow-with-description' },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
// The layers call methods of the base class of the variant, which the standalone variant
|
|
90
|
+
// doesn't have
|
|
91
|
+
files: ['lib/shared/policy-statement/*.ts'],
|
|
92
|
+
rules: {
|
|
93
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
94
|
+
'@typescript-eslint/no-this-alias': 'off',
|
|
95
|
+
'@typescript-eslint/no-unsafe-assignment': 'off',
|
|
96
|
+
'@typescript-eslint/no-unsafe-call': 'off',
|
|
97
|
+
'@typescript-eslint/no-unsafe-member-access': 'off',
|
|
98
|
+
'@typescript-eslint/no-unsafe-return': 'off',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
files: cdkFiles,
|
|
103
|
+
extends: isCdk ? [] : [tseslint.configs.disableTypeChecked],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
files: ['**/*.js', '**/*.mjs'],
|
|
107
|
+
extends: [eslint.configs.recommended],
|
|
108
|
+
languageOptions: { globals: globals.node },
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
files: ['docs/source/_static/js/**/*.js'],
|
|
112
|
+
languageOptions: {
|
|
113
|
+
globals: { ...globals.browser, $: 'readonly' },
|
|
114
|
+
sourceType: 'script',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
prettierRecommended,
|
|
118
|
+
{
|
|
119
|
+
// `make cdk` writes code that isn't formatted. The formatting is checked in the standalone variant.
|
|
120
|
+
rules: isCdk ? { 'prettier/prettier': 'off' } : {},
|
|
121
|
+
},
|
|
122
|
+
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ec2 } from '../generated/policy-statements/ec2';
|
|
2
2
|
/**
|
|
3
3
|
* Allows stopping EC2 instance only for the user who started them
|
|
4
4
|
*
|
|
5
5
|
* @param tag The tag name, where the user information will be stored - default: `Owner`
|
|
6
6
|
*/
|
|
7
|
-
export declare function allowEc2InstanceDeleteByOwner(tag?: string):
|
|
7
|
+
export declare function allowEc2InstanceDeleteByOwner(tag?: string): Ec2[];
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.allowEc2InstanceDeleteByOwner = allowEc2InstanceDeleteByOwner;
|
|
4
|
-
const
|
|
4
|
+
const ec2_1 = require("../generated/policy-statements/ec2");
|
|
5
5
|
function allowEc2InstanceDeleteByOwner(tag) {
|
|
6
6
|
const tagName = tag ?? 'Owner';
|
|
7
7
|
return [
|
|
8
|
-
new
|
|
8
|
+
new ec2_1.Ec2()
|
|
9
9
|
.allow()
|
|
10
10
|
.toStartInstances()
|
|
11
11
|
.ifAwsRequestTag(tagName, '${aws:username}'),
|
|
12
|
-
new
|
|
12
|
+
new ec2_1.Ec2()
|
|
13
13
|
.allow()
|
|
14
14
|
.toStopInstances()
|
|
15
15
|
.ifResourceTag(tagName, '${aws:username}'),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Condenses a list of action names to wildcard patterns, e.g. `List*` or `*Vpn*`.
|
|
3
|
+
*
|
|
4
|
+
* A pattern is made from a substring that at least two of the selected actions have in common and that none of the
|
|
5
|
+
* excluded actions contains. IAM matches action names case-insensitive, so substrings are compared in lower case.
|
|
6
|
+
* The substrings are picked greedily, the one that covers the most actions not yet covered first. Actions that are not
|
|
7
|
+
* covered by any pattern are returned by name.
|
|
8
|
+
*
|
|
9
|
+
* @param selected Names of the actions to condense, without service prefix
|
|
10
|
+
* @param excluded Names of all other actions of the service, which the patterns must not match
|
|
11
|
+
*/
|
|
12
|
+
export declare function compactActionNames(selected: string[], excluded: string[]): string[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compactActionNames = compactActionNames;
|
|
4
|
+
const minLength = 3;
|
|
5
|
+
function compactActionNames(selected, excluded) {
|
|
6
|
+
const forbidden = new Set();
|
|
7
|
+
for (const name of excluded) {
|
|
8
|
+
const marked = `^${name}$`.toLowerCase();
|
|
9
|
+
for (let start = 0; start < marked.length; start++) {
|
|
10
|
+
for (let end = start + minLength; end <= marked.length; end++) {
|
|
11
|
+
forbidden.add(marked.substring(start, end));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const sources = new Map();
|
|
16
|
+
const spellings = new Map();
|
|
17
|
+
for (let index = 0; index < selected.length; index++) {
|
|
18
|
+
const marked = `^${selected[index]}$`;
|
|
19
|
+
const lower = marked.toLowerCase();
|
|
20
|
+
for (let start = 0; start < marked.length; start++) {
|
|
21
|
+
for (let end = start + minLength; end <= marked.length; end++) {
|
|
22
|
+
const key = lower.substring(start, end);
|
|
23
|
+
if (forbidden.has(key)) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
let indexes = sources.get(key);
|
|
27
|
+
if (indexes === undefined) {
|
|
28
|
+
indexes = [];
|
|
29
|
+
sources.set(key, indexes);
|
|
30
|
+
spellings.set(key, marked.substring(start, end));
|
|
31
|
+
}
|
|
32
|
+
if (indexes.length == 0 || indexes[indexes.length - 1] != index) {
|
|
33
|
+
indexes.push(index);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const redundant = new Set();
|
|
39
|
+
for (const [key, indexes] of sources) {
|
|
40
|
+
if (key.length <= minLength || indexes.length < 2) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const shorter = [key.substring(1), key.substring(0, key.length - 1)];
|
|
44
|
+
for (const candidate of shorter) {
|
|
45
|
+
if ((sources.get(candidate) ?? []).length == indexes.length) {
|
|
46
|
+
redundant.add(candidate);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
let candidates = [];
|
|
51
|
+
for (const [key, indexes] of sources) {
|
|
52
|
+
if (indexes.length >= 2 && !redundant.has(key)) {
|
|
53
|
+
candidates.push(key);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const covered = selected.map(() => false);
|
|
57
|
+
const patterns = [];
|
|
58
|
+
while (candidates.length > 0) {
|
|
59
|
+
let best = '';
|
|
60
|
+
let bestCount = 0;
|
|
61
|
+
const remaining = [];
|
|
62
|
+
for (const candidate of candidates) {
|
|
63
|
+
let count = 0;
|
|
64
|
+
for (const index of sources.get(candidate)) {
|
|
65
|
+
if (!covered[index]) {
|
|
66
|
+
count++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (count < 2) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
remaining.push(candidate);
|
|
73
|
+
if (count > bestCount ||
|
|
74
|
+
(count == bestCount && isBetter(candidate, best))) {
|
|
75
|
+
best = candidate;
|
|
76
|
+
bestCount = count;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (bestCount == 0) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
for (const index of sources.get(best)) {
|
|
83
|
+
covered[index] = true;
|
|
84
|
+
}
|
|
85
|
+
patterns.push(toPattern(spellings.get(best)));
|
|
86
|
+
candidates = remaining;
|
|
87
|
+
}
|
|
88
|
+
for (let index = 0; index < selected.length; index++) {
|
|
89
|
+
if (!covered[index]) {
|
|
90
|
+
patterns.push(selected[index]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return patterns;
|
|
94
|
+
}
|
|
95
|
+
function isBetter(candidate, best) {
|
|
96
|
+
const candidateIsStart = candidate.startsWith('^');
|
|
97
|
+
const bestIsStart = best.startsWith('^');
|
|
98
|
+
if (candidateIsStart != bestIsStart) {
|
|
99
|
+
return candidateIsStart;
|
|
100
|
+
}
|
|
101
|
+
if (candidate.length != best.length) {
|
|
102
|
+
return candidate.length > best.length;
|
|
103
|
+
}
|
|
104
|
+
return candidate < best;
|
|
105
|
+
}
|
|
106
|
+
function toPattern(substring) {
|
|
107
|
+
let pattern = substring;
|
|
108
|
+
if (pattern.startsWith('^')) {
|
|
109
|
+
pattern = pattern.substring(1);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
pattern = `*${pattern}`;
|
|
113
|
+
}
|
|
114
|
+
if (pattern.endsWith('$')) {
|
|
115
|
+
pattern = pattern.substring(0, pattern.length - 1);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
pattern = `${pattern}*`;
|
|
119
|
+
}
|
|
120
|
+
return pattern;
|
|
121
|
+
}
|
|
@@ -3,7 +3,6 @@ import { PolicyStatementWithPrincipal } from './8-principals';
|
|
|
3
3
|
* Represents a statement in an IAM policy document
|
|
4
4
|
*/
|
|
5
5
|
export declare class PolicyStatement extends PolicyStatementWithPrincipal {
|
|
6
|
-
protected cdkApplied: boolean;
|
|
7
6
|
/**
|
|
8
7
|
* JSON-ify the policy statement
|
|
9
8
|
*/
|
|
@@ -3,12 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PolicyStatement = void 0;
|
|
4
4
|
const _8_principals_1 = require("./8-principals");
|
|
5
5
|
class PolicyStatement extends _8_principals_1.PolicyStatementWithPrincipal {
|
|
6
|
-
constructor() {
|
|
7
|
-
super(...arguments);
|
|
8
|
-
this.cdkApplied = false;
|
|
9
|
-
}
|
|
10
6
|
toJSON() {
|
|
11
|
-
|
|
7
|
+
const statement = {};
|
|
8
|
+
if (this.sid.length) {
|
|
9
|
+
statement.Sid = this.sid;
|
|
10
|
+
}
|
|
11
|
+
if (this.hasConditions()) {
|
|
12
|
+
statement.Condition = this.floydConditions;
|
|
13
|
+
}
|
|
14
|
+
if (this.hasActions()) {
|
|
15
|
+
const actions = this.uniqueActions();
|
|
16
|
+
const mode = this.useNotAction ? 'NotAction' : 'Action';
|
|
17
|
+
statement[mode] = actions.length > 1 ? actions : actions[0];
|
|
18
|
+
}
|
|
19
|
+
this.ensureResource();
|
|
20
|
+
if (this.hasResources()) {
|
|
21
|
+
const resources = this.uniqueResources();
|
|
22
|
+
const mode = this.useNotResource ? 'NotResource' : 'Resource';
|
|
23
|
+
statement[mode] = resources.length > 1 ? resources : resources[0];
|
|
24
|
+
}
|
|
25
|
+
statement.Effect = this.effect;
|
|
26
|
+
if (this.hasPrincipals()) {
|
|
27
|
+
const mode = this.useNotPrincipal ? 'NotPrincipal' : 'Principal';
|
|
28
|
+
statement[mode] = this.myPrincipals;
|
|
29
|
+
}
|
|
30
|
+
return statement;
|
|
12
31
|
}
|
|
13
32
|
}
|
|
14
33
|
exports.PolicyStatement = PolicyStatement;
|
|
@@ -13,16 +13,6 @@ export type Conditions = Record<string, Condition>;
|
|
|
13
13
|
*/
|
|
14
14
|
export declare class PolicyStatementWithCondition extends PolicyStatementBase {
|
|
15
15
|
protected floydConditions: Conditions;
|
|
16
|
-
private cdkConditionsApplied;
|
|
17
|
-
/**
|
|
18
|
-
* Injects conditions into the statement.
|
|
19
|
-
*
|
|
20
|
-
* Only relevant for the main package. In CDK mode this only calls super.
|
|
21
|
-
*/
|
|
22
|
-
toJSON(): any;
|
|
23
|
-
toStatementJson(): any;
|
|
24
|
-
freeze(): any;
|
|
25
|
-
private cdkApplyConditions;
|
|
26
16
|
/**
|
|
27
17
|
* Checks weather a condition was applied to the policy.
|
|
28
18
|
*/
|