vgs-cli 0.0.1.dev0__py3-none-any.whl
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.
- vgs_cli-0.0.1.dev0.data/data/vgscli/calm.yaml +16 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/checkout.yaml +21 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/http-route-template.yaml +61 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/mft-route-template.yaml +10 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/payments-admin.yaml +25 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/service-account-schema.yaml +54 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/sub-account-checkout.yaml +23 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/vault-resources.yaml +710 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/vault-schema.yaml +36 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/vault-template.yaml +12 -0
- vgs_cli-0.0.1.dev0.data/data/vgscli/vgs-cli.yaml +17 -0
- vgs_cli-0.0.1.dev0.dist-info/METADATA +139 -0
- vgs_cli-0.0.1.dev0.dist-info/RECORD +56 -0
- vgs_cli-0.0.1.dev0.dist-info/WHEEL +5 -0
- vgs_cli-0.0.1.dev0.dist-info/entry_points.txt +2 -0
- vgs_cli-0.0.1.dev0.dist-info/licenses/LICENSE +22 -0
- vgs_cli-0.0.1.dev0.dist-info/top_level.txt +1 -0
- vgscli/__init__.py +0 -0
- vgscli/_version.py +32 -0
- vgscli/access_logs.py +65 -0
- vgscli/audits_api.py +102 -0
- vgscli/auth.py +68 -0
- vgscli/auth_server.py +131 -0
- vgscli/auth_utils.py +24 -0
- vgscli/callback_server.py +41 -0
- vgscli/cert_manager_api.py +34 -0
- vgscli/cli/__init__.py +23 -0
- vgscli/cli/commands/__init__.py +3 -0
- vgscli/cli/commands/apply.py +307 -0
- vgscli/cli/commands/generate.py +134 -0
- vgscli/cli/commands/get.py +200 -0
- vgscli/cli/types/__init__.py +2 -0
- vgscli/cli/types/resource_id.py +39 -0
- vgscli/cli/types/variable.py +21 -0
- vgscli/cli_utils.py +132 -0
- vgscli/click_extensions.py +88 -0
- vgscli/config_file.py +58 -0
- vgscli/errors.py +263 -0
- vgscli/file_token_util.py +30 -0
- vgscli/id_generator.py +46 -0
- vgscli/keyring_token_util.py +128 -0
- vgscli/resource-templates/http-route-template.yaml +61 -0
- vgscli/resource-templates/mft-route-template.yaml +10 -0
- vgscli/resource-templates/service-account/calm.yaml +16 -0
- vgscli/resource-templates/service-account/checkout.yaml +21 -0
- vgscli/resource-templates/service-account/payments-admin.yaml +25 -0
- vgscli/resource-templates/service-account/sub-account-checkout.yaml +23 -0
- vgscli/resource-templates/service-account/vgs-cli.yaml +17 -0
- vgscli/resource-templates/vault-template.yaml +12 -0
- vgscli/testing.py +48 -0
- vgscli/text.py +9 -0
- vgscli/token_handler.py +11 -0
- vgscli/validation-schemas/service-account-schema.yaml +54 -0
- vgscli/validation-schemas/vault-resources.yaml +710 -0
- vgscli/validation-schemas/vault-schema.yaml +36 -0
- vgscli/vgs.py +249 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
"$schema": http://json-schema.org/draft-07/schema#
|
|
3
|
+
type: object
|
|
4
|
+
properties:
|
|
5
|
+
apiVersion:
|
|
6
|
+
type: string
|
|
7
|
+
enum:
|
|
8
|
+
- 1.0.0
|
|
9
|
+
kind:
|
|
10
|
+
type: string
|
|
11
|
+
enum:
|
|
12
|
+
- Vault
|
|
13
|
+
data:
|
|
14
|
+
type: object
|
|
15
|
+
properties:
|
|
16
|
+
name:
|
|
17
|
+
type: string
|
|
18
|
+
minLength: 3
|
|
19
|
+
maxLength: 50
|
|
20
|
+
environment:
|
|
21
|
+
type: string
|
|
22
|
+
enum:
|
|
23
|
+
- SANDBOX
|
|
24
|
+
- LIVE
|
|
25
|
+
organizationId:
|
|
26
|
+
type: string
|
|
27
|
+
pattern: ^AC[A-Za-z0-9]{22}$
|
|
28
|
+
required:
|
|
29
|
+
- name
|
|
30
|
+
- environment
|
|
31
|
+
additionalProperties: false
|
|
32
|
+
required:
|
|
33
|
+
- apiVersion
|
|
34
|
+
- kind
|
|
35
|
+
- data
|
|
36
|
+
additionalProperties: false
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
apiVersion: 1.0.0
|
|
2
|
+
kind: Vault
|
|
3
|
+
data:
|
|
4
|
+
# Name of the vault to create. (Must be between 3 and 50 characters long.)
|
|
5
|
+
name: Very Good Vault
|
|
6
|
+
|
|
7
|
+
# Environment to create the vault within.
|
|
8
|
+
# See https://www.verygoodsecurity.com/docs/terminology/vaults#environments
|
|
9
|
+
environment: SANDBOX
|
|
10
|
+
|
|
11
|
+
# ID of the organization to associate the vault with.
|
|
12
|
+
organizationId: AC6mGNNRecR7K5N7AjX5niz4
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
apiVersion: 1.0.0
|
|
2
|
+
kind: ServiceAccount
|
|
3
|
+
data:
|
|
4
|
+
name: vgs-cli
|
|
5
|
+
{%- if vaults | length == 0 %}
|
|
6
|
+
{{- cli_warn("Service Account won't have access to any vaults inside organization. If you need it to access vault(s) please use --vault <vault-identifier>.") or "" }}
|
|
7
|
+
{%- else %}
|
|
8
|
+
vaults:
|
|
9
|
+
{%- for vault_id in vaults %}
|
|
10
|
+
- {{ vault_id }}
|
|
11
|
+
{%- endfor %}
|
|
12
|
+
{%- endif %}
|
|
13
|
+
scopes:
|
|
14
|
+
- name: access-logs:read
|
|
15
|
+
- name: organizations:read
|
|
16
|
+
- name: routes:write
|
|
17
|
+
- name: vaults:write
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vgs-cli
|
|
3
|
+
Version: 0.0.1.dev0
|
|
4
|
+
Summary: VGS Client
|
|
5
|
+
Home-page: https://github.com/verygoodsecurity/vgs-cli
|
|
6
|
+
Author: Very Good Security
|
|
7
|
+
Author-email: dev@verygoodsecurity.com
|
|
8
|
+
License: BSD
|
|
9
|
+
Platform: any
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: base58<3,>=2.1.1
|
|
18
|
+
Requires-Dist: click-plugins<2,>=1.1.1
|
|
19
|
+
Requires-Dist: click<9,>=7
|
|
20
|
+
Requires-Dist: configobj-dev>=2019.9.1
|
|
21
|
+
Requires-Dist: cryptography>=44.0.1
|
|
22
|
+
Requires-Dist: jsonschema<5,>=3.2.0
|
|
23
|
+
Requires-Dist: keyring<26,>=16.1.0
|
|
24
|
+
Requires-Dist: keyrings.alt<6,>=3.1
|
|
25
|
+
Requires-Dist: pyhumps
|
|
26
|
+
Requires-Dist: semver<4,>=3
|
|
27
|
+
Requires-Dist: termcolor<3,>=2
|
|
28
|
+
Requires-Dist: Jinja2>=2.10
|
|
29
|
+
Requires-Dist: vgs-api-client<1,>=0.0.51
|
|
30
|
+
Requires-Dist: async-timeout
|
|
31
|
+
Requires-Dist: pip>=25.3
|
|
32
|
+
Dynamic: author
|
|
33
|
+
Dynamic: author-email
|
|
34
|
+
Dynamic: classifier
|
|
35
|
+
Dynamic: description
|
|
36
|
+
Dynamic: description-content-type
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: license
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
Dynamic: platform
|
|
41
|
+
Dynamic: requires-dist
|
|
42
|
+
Dynamic: summary
|
|
43
|
+
|
|
44
|
+
# VGS CLI
|
|
45
|
+
|
|
46
|
+
[](https://dl.circleci.com/status-badge/redirect/gh/verygoodsecurity/vgs-cli/tree/master)
|
|
47
|
+
|
|
48
|
+
Command Line Tool for programmatic configurations on VGS.
|
|
49
|
+
|
|
50
|
+
[Official Documentation](https://www.verygoodsecurity.com/docs/vgs-cli/getting-started)
|
|
51
|
+
|
|
52
|
+
## Table of Contents
|
|
53
|
+
|
|
54
|
+
- [Requirements](#requirements)
|
|
55
|
+
- [Installation](#installation)
|
|
56
|
+
- [PyPI](#pypi)
|
|
57
|
+
- [Run](#run)
|
|
58
|
+
- [Running in Docker](#running-in-docker)
|
|
59
|
+
- [Commands](#commands)
|
|
60
|
+
- [Code Standards](#code-standards)
|
|
61
|
+
- [Automation with VGS CLI](#automation-with-vgs-cli)
|
|
62
|
+
- [Sphinx Documentation](#sphinx-documentation)
|
|
63
|
+
- [Plugins Development](#plugins-development)
|
|
64
|
+
|
|
65
|
+
## Requirements
|
|
66
|
+
[Python 3](https://www.python.org/downloads/) or [Docker](https://docs.docker.com/get-docker/).
|
|
67
|
+
|
|
68
|
+
## Installation
|
|
69
|
+
|
|
70
|
+
### PyPI
|
|
71
|
+
Install the latest version from [PyPI](https://pypi.org/project/vgs-cli/):
|
|
72
|
+
```
|
|
73
|
+
pip install vgs-cli
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Run
|
|
77
|
+
|
|
78
|
+
Verify your installation by running:
|
|
79
|
+
```
|
|
80
|
+
vgs --version
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Running in Docker
|
|
84
|
+
|
|
85
|
+
Check our [official documentation](https://www.verygoodsecurity.com/docs/vgs-cli/docker).
|
|
86
|
+
|
|
87
|
+
## Commands
|
|
88
|
+
|
|
89
|
+
- [`help`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#exploring-the-cli)
|
|
90
|
+
- [`login`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#login)
|
|
91
|
+
- [`logout`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#logout)
|
|
92
|
+
- [`routes get`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#get)
|
|
93
|
+
- [`routes apply`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#apply)
|
|
94
|
+
- [`logs access`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#access)
|
|
95
|
+
- [`access-credentials get`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#get)
|
|
96
|
+
- [`access-credentials generate`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#generate)
|
|
97
|
+
- [`organizations get`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#get)
|
|
98
|
+
- [`vaults get`](https://www.verygoodsecurity.com/docs/vgs-cli/commands#get)
|
|
99
|
+
|
|
100
|
+
## Code Standards
|
|
101
|
+
|
|
102
|
+
We follow the defaults enforced by [Black](https://black.readthedocs.io/en/stable/) and [isort](https://pycqa.github.io/isort/) (with the Black profile). A formatting workflow runs in CI and installs `black`, `isort`, and `ruff`. Before opening a pull request, install the tools with `pip install black isort ruff` and run:
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
black .
|
|
106
|
+
isort --profile black .
|
|
107
|
+
ruff check .
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Automation with VGS CLI
|
|
111
|
+
|
|
112
|
+
If you want to use the VGS CLI for automation you might be interested in creating a [service account](https://www.verygoodsecurity.com/docs/vgs-cli/service-account).
|
|
113
|
+
|
|
114
|
+
## Plugins Development
|
|
115
|
+
|
|
116
|
+
See [Click - Developing Plugins](https://github.com/click-contrib/click-plugins#developing-plugins).
|
|
117
|
+
|
|
118
|
+
In order to develop a plugin you need to register your commands to an entrypoint in `setup.py`.
|
|
119
|
+
|
|
120
|
+
Supported entrypoints:
|
|
121
|
+
|
|
122
|
+
- `vgs.plugins` - for extending `vgs` with sub-commands
|
|
123
|
+
- `vgs.get.plugins` - for extending `vgs get` with sub-commands
|
|
124
|
+
- `vgs.apply.plugins` - for extending `vgs apply` with sub-commands
|
|
125
|
+
- `vgs.logs.plugins` - for extending `vgs logs` with sub-commands
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
```python
|
|
129
|
+
entry_points='''
|
|
130
|
+
[vgs.plugins]
|
|
131
|
+
activate=vgscliplugin.myplugin:new_command
|
|
132
|
+
|
|
133
|
+
[vgs.get.plugins]
|
|
134
|
+
preferences=vgscliplugin.myplugin:new_get_command
|
|
135
|
+
'''
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Plugin catalog
|
|
139
|
+
- [vgs-cli-admin-plugin](https://github.com/verygoodsecurity/vgs-cli-admin-plugin)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/calm.yaml,sha256=kkZKW_D2gQHq5uDRn2eS8AMMVfSRP72gDerDY5q4hQU,482
|
|
2
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/checkout.yaml,sha256=ig5MZXQFiAAsQcRpLhdoBM-6eWq7hhV69H3IkLntjwA,588
|
|
3
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/http-route-template.yaml,sha256=nIXh_oF0pirA_dtFeyO-SG4p72No9KE7J_ShqAb_HyM,1662
|
|
4
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/mft-route-template.yaml,sha256=3TwiVS6yDir3uc0g0G_p5Hj4Gje2aq8Zi6vnnRa4j5Q,142
|
|
5
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/payments-admin.yaml,sha256=hXLVSB-6QYuvCEa3ozUdw8LBClxjVMs7t5ZSXsSsHJg,733
|
|
6
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/service-account-schema.yaml,sha256=L1pjNXgnks15N1wNgf_1GmGcOvxn0nu49v7ersUjFno,1090
|
|
7
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/sub-account-checkout.yaml,sha256=pDl0BRa8TZowlOJ8exs8JC--6JW8NPY_UFzaKXdDXqc,662
|
|
8
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/vault-resources.yaml,sha256=oOdNs5yhfipKS3VMGMeewz49RHyE77pBnzW6fxVPOIk,26334
|
|
9
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/vault-schema.yaml,sha256=sNiIIyIQ_iwfW234dSzsolyN4MADZLtV2mkcoG2A8Rs,622
|
|
10
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/vault-template.yaml,sha256=IlDisL3H5LXUDMkhUaouKMarRdu0-pJxpTz8ij_lp_M,383
|
|
11
|
+
vgs_cli-0.0.1.dev0.data/data/vgscli/vgs-cli.yaml,sha256=qvRq9_rmg1Wp1w6bO0JAVZrJmeOcvmBfbdMY8OnKn4Q,498
|
|
12
|
+
vgs_cli-0.0.1.dev0.dist-info/licenses/LICENSE,sha256=NwSyzgDYdzv6O2ns2C6UODGI8LN1VnVe_FPTnWS4eXY,1082
|
|
13
|
+
vgscli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
vgscli/_version.py,sha256=voOYriGKhzp76O3vbl7xYBRZGbaFSxSBjgP5ksZuebg,812
|
|
15
|
+
vgscli/access_logs.py,sha256=G0GjlLjWiC-N6OzK6-RCvWddcVGQlLCLAcyO9pLWIOM,1928
|
|
16
|
+
vgscli/audits_api.py,sha256=6FjpS1yTALio-DejgIS2eDw75ChjDp4GoAGXh4uZExU,3211
|
|
17
|
+
vgscli/auth.py,sha256=vdUZ12nqtefIuSHNmwCFvxoqwS3m6ZP5TqLZFNwKnSU,2104
|
|
18
|
+
vgscli/auth_server.py,sha256=e-xYoVebWM6cQNxuMr5rYzKuswNIrexnLSYo1ma48WY,4096
|
|
19
|
+
vgscli/auth_utils.py,sha256=aC1VI0dFdFeupLS9Okz4uDz-wHhhFabqiLUg2R5vO2s,600
|
|
20
|
+
vgscli/callback_server.py,sha256=oFFF5AGbbwhF7Hsc5fzS7jeGFzIMbebc9Qdq8uEl1PY,1055
|
|
21
|
+
vgscli/cert_manager_api.py,sha256=uBrqbk5Qn7z1bSugSdCBHde6XksclNsW9358UDii2fc,1061
|
|
22
|
+
vgscli/cli_utils.py,sha256=NzXK29_yIDrB-3T_XdLLB8NCWneQ-cpZFi9mazQL9tQ,3810
|
|
23
|
+
vgscli/click_extensions.py,sha256=y59iSxnh-BW_GUyRoBmWeH6dsiyXf6WNr6OKea72Z-Q,2962
|
|
24
|
+
vgscli/config_file.py,sha256=HB8lANU0qoO09Wnkf-QDzPh2qtrnFcqcetnCaQgtQ3U,1543
|
|
25
|
+
vgscli/errors.py,sha256=rQ8M3MbtcirwE3vxICEekga_JqWeOvZbqReswiOEzv8,8682
|
|
26
|
+
vgscli/file_token_util.py,sha256=537b54paMGw0u5PL5VJgQqQ7awKa9lQP88n3tUc_lyw,771
|
|
27
|
+
vgscli/id_generator.py,sha256=3CbvSOUSyLON76p6M8wUhVQVig8j75_rnWdpxLhBifM,1510
|
|
28
|
+
vgscli/keyring_token_util.py,sha256=Meo1XxlSpIUjYF1TqKqWCUKiNaidroiavUAGPmWS254,4215
|
|
29
|
+
vgscli/testing.py,sha256=MLrsvy3mEwvjtqyuG0UjwEX-rDkbWUo52aXvu2H2sTk,1437
|
|
30
|
+
vgscli/text.py,sha256=c2d1As7awq8PlEF9NiI6JrjsYYousj9YT0o1xOQ2MQM,172
|
|
31
|
+
vgscli/token_handler.py,sha256=MvtmlicqnGrcopNkTr8Q2RlKE7IDj_sJFxF_39Jd7W8,183
|
|
32
|
+
vgscli/vgs.py,sha256=n2_to8Dcb2cetkI9hYyyRehAJx3EOttMNZgxlPUsSeI,7097
|
|
33
|
+
vgscli/cli/__init__.py,sha256=4g6-RMMhl907F-MegDac28V1y8fDVhLGp3Scz27afNo,628
|
|
34
|
+
vgscli/cli/commands/__init__.py,sha256=h9vpkJzjaRQVZ6eR3FkSXvAjiDvngbxYzMSA30tiBCM,77
|
|
35
|
+
vgscli/cli/commands/apply.py,sha256=b74CFIIZ6R8CPz08EAlSIje5vt1S2sVqbMrDG7Gn4rc,8968
|
|
36
|
+
vgscli/cli/commands/generate.py,sha256=CIEcLnH5hEBftKP9mMjYWLh4ZvblwKXLw13PH4w88zs,3674
|
|
37
|
+
vgscli/cli/commands/get.py,sha256=48Q2rfAi3A1zmalQ9eUXn1v8hXSNf9cJWSNyg96JNWk,5102
|
|
38
|
+
vgscli/cli/types/__init__.py,sha256=a-P-SoGHpLzMg7CL1hhg5x_nf6sHq0qAuGm_AtIITmw,107
|
|
39
|
+
vgscli/cli/types/resource_id.py,sha256=7JILR-KjoVGQDHB40tXJfTcU53iR8wM9Zr24fY5le3o,1172
|
|
40
|
+
vgscli/cli/types/variable.py,sha256=IVcszPoWmTjeo_evDsLN06LuUZGTLL-wcZCVfBNBP8I,414
|
|
41
|
+
vgscli/resource-templates/http-route-template.yaml,sha256=nIXh_oF0pirA_dtFeyO-SG4p72No9KE7J_ShqAb_HyM,1662
|
|
42
|
+
vgscli/resource-templates/mft-route-template.yaml,sha256=3TwiVS6yDir3uc0g0G_p5Hj4Gje2aq8Zi6vnnRa4j5Q,142
|
|
43
|
+
vgscli/resource-templates/vault-template.yaml,sha256=IlDisL3H5LXUDMkhUaouKMarRdu0-pJxpTz8ij_lp_M,383
|
|
44
|
+
vgscli/resource-templates/service-account/calm.yaml,sha256=kkZKW_D2gQHq5uDRn2eS8AMMVfSRP72gDerDY5q4hQU,482
|
|
45
|
+
vgscli/resource-templates/service-account/checkout.yaml,sha256=ig5MZXQFiAAsQcRpLhdoBM-6eWq7hhV69H3IkLntjwA,588
|
|
46
|
+
vgscli/resource-templates/service-account/payments-admin.yaml,sha256=hXLVSB-6QYuvCEa3ozUdw8LBClxjVMs7t5ZSXsSsHJg,733
|
|
47
|
+
vgscli/resource-templates/service-account/sub-account-checkout.yaml,sha256=pDl0BRa8TZowlOJ8exs8JC--6JW8NPY_UFzaKXdDXqc,662
|
|
48
|
+
vgscli/resource-templates/service-account/vgs-cli.yaml,sha256=qvRq9_rmg1Wp1w6bO0JAVZrJmeOcvmBfbdMY8OnKn4Q,498
|
|
49
|
+
vgscli/validation-schemas/service-account-schema.yaml,sha256=L1pjNXgnks15N1wNgf_1GmGcOvxn0nu49v7ersUjFno,1090
|
|
50
|
+
vgscli/validation-schemas/vault-resources.yaml,sha256=oOdNs5yhfipKS3VMGMeewz49RHyE77pBnzW6fxVPOIk,26334
|
|
51
|
+
vgscli/validation-schemas/vault-schema.yaml,sha256=sNiIIyIQ_iwfW234dSzsolyN4MADZLtV2mkcoG2A8Rs,622
|
|
52
|
+
vgs_cli-0.0.1.dev0.dist-info/METADATA,sha256=iH5mcBiJXL7iEpZjPE3fGnjMbL_NKliqjqlO8fTQtd0,4684
|
|
53
|
+
vgs_cli-0.0.1.dev0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
54
|
+
vgs_cli-0.0.1.dev0.dist-info/entry_points.txt,sha256=wCWhRQiMX4Ja6kkqJlYr8LbuiywpmXyyRAZPsau-_RY,39
|
|
55
|
+
vgs_cli-0.0.1.dev0.dist-info/top_level.txt,sha256=gabTxCWMkl80q6ha_m-hs_1wQMfGntZcv5DEmkl8JEw,7
|
|
56
|
+
vgs_cli-0.0.1.dev0.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Very Good Security, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vgscli
|
vgscli/__init__.py
ADDED
|
File without changes
|
vgscli/_version.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import requests
|
|
5
|
+
from semver import VersionInfo
|
|
6
|
+
|
|
7
|
+
from vgscli.text import bold, green
|
|
8
|
+
|
|
9
|
+
__version__ = "0.0.1-dev"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# noinspection PyBroadException
|
|
13
|
+
def get_latest_version(**kwargs) -> Optional[VersionInfo]:
|
|
14
|
+
try:
|
|
15
|
+
response_json = requests.get(
|
|
16
|
+
"https://pypi.org/pypi/vgs-cli/json", **kwargs
|
|
17
|
+
).json()
|
|
18
|
+
return VersionInfo.parse(response_json["info"]["version"])
|
|
19
|
+
except Exception:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def check_for_updates() -> None:
|
|
24
|
+
latest_version = get_latest_version(timeout=2)
|
|
25
|
+
|
|
26
|
+
if latest_version and latest_version > __version__:
|
|
27
|
+
message = f"CLI update available from {bold(green(__version__))} to {bold(green(str(latest_version)))}."
|
|
28
|
+
click.echo(message, err=True)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def version():
|
|
32
|
+
return __version__
|
vgscli/access_logs.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def calculate_start_page(total_records, tail, records_per_page):
|
|
5
|
+
if tail > total_records or total_records <= records_per_page:
|
|
6
|
+
return 1
|
|
7
|
+
total_pages_num = math.ceil(total_records / records_per_page)
|
|
8
|
+
tail_pages_num = math.ceil(
|
|
9
|
+
(tail - total_records % records_per_page) / records_per_page
|
|
10
|
+
)
|
|
11
|
+
return total_pages_num - tail_pages_num
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def calculate_start_index(total_records, tail, records_per_page):
|
|
15
|
+
if tail > total_records:
|
|
16
|
+
return 0
|
|
17
|
+
offset_total = total_records % records_per_page
|
|
18
|
+
offset_tail = tail % records_per_page
|
|
19
|
+
return offset_total - offset_tail
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def fetch_logs(api, params, tail):
|
|
23
|
+
records_per_page = 30
|
|
24
|
+
count = 0
|
|
25
|
+
current_page = 1
|
|
26
|
+
start_page = None
|
|
27
|
+
|
|
28
|
+
params["sort"] = "occurred_at"
|
|
29
|
+
|
|
30
|
+
while True:
|
|
31
|
+
params["page[number]"] = current_page
|
|
32
|
+
result = api.access_logs.list(params=params)
|
|
33
|
+
page_data = result.body["data"]
|
|
34
|
+
|
|
35
|
+
if tail:
|
|
36
|
+
if not start_page:
|
|
37
|
+
start_page = calculate_start_page(
|
|
38
|
+
result.body["meta"]["count"], tail, records_per_page
|
|
39
|
+
)
|
|
40
|
+
current_page = start_page
|
|
41
|
+
if start_page != 1:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
if current_page == start_page:
|
|
45
|
+
start_index = calculate_start_index(
|
|
46
|
+
result.body["meta"]["count"], tail, records_per_page
|
|
47
|
+
)
|
|
48
|
+
page_data = page_data[start_index:]
|
|
49
|
+
elif tail - count < records_per_page:
|
|
50
|
+
page_data = page_data[: (tail - count)]
|
|
51
|
+
|
|
52
|
+
yield page_data
|
|
53
|
+
|
|
54
|
+
count += len(page_data)
|
|
55
|
+
current_page += 1
|
|
56
|
+
|
|
57
|
+
if not result.body["links"].get("next") or (tail and tail <= count):
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def prepare_filter(filter_options):
|
|
62
|
+
result = {}
|
|
63
|
+
for pair in filter_options.items():
|
|
64
|
+
result["filter[{}]".format(pair[0])] = pair[1]
|
|
65
|
+
return result
|
vgscli/audits_api.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
from simple_rest_client.api import API
|
|
6
|
+
from simple_rest_client.resource import Resource
|
|
7
|
+
|
|
8
|
+
from vgscli._version import __version__
|
|
9
|
+
from vgscli.errors import VaultNotFoundError
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
env_url = {
|
|
14
|
+
"dev": "https://audits.verygoodsecurity.io",
|
|
15
|
+
"prod": "https://audits.apps.verygood.systems",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AccessLogsResource(Resource):
|
|
20
|
+
actions = {
|
|
21
|
+
"retrieve": {"method": "GET", "url": "access-logs/{}"},
|
|
22
|
+
"list": {"method": "GET", "url": "access-logs"},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OperationsLogsResource(Resource):
|
|
27
|
+
actions = {
|
|
28
|
+
"list": {"method": "GET", "url": "op-pipeline-logs"},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class OperationLogsQueryConfig:
|
|
33
|
+
|
|
34
|
+
TENANT_ID_KEY = "filter[tenant_id]"
|
|
35
|
+
TRACE_ID_KEY = "filter[trace_id]"
|
|
36
|
+
|
|
37
|
+
PAGE_SIZE_KEY = "page[size]"
|
|
38
|
+
|
|
39
|
+
def __init__(self, tenant_id: str, trace_id: str, page_size: int = 1000):
|
|
40
|
+
self._configs: Dict[str, str] = {
|
|
41
|
+
OperationLogsQueryConfig.TENANT_ID_KEY: tenant_id,
|
|
42
|
+
OperationLogsQueryConfig.PAGE_SIZE_KEY: page_size,
|
|
43
|
+
OperationLogsQueryConfig.TRACE_ID_KEY: trace_id,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def to_query_params(self):
|
|
47
|
+
return deepcopy({k: v for k, v in self._configs.items() if v is not None})
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def tenant_id(self):
|
|
51
|
+
return self._configs.get(OperationLogsQueryConfig.TENANT_ID_KEY)
|
|
52
|
+
|
|
53
|
+
@tenant_id.setter
|
|
54
|
+
def tenant_id(self, value: str):
|
|
55
|
+
self._configs[OperationLogsQueryConfig.TENANT_ID_KEY] = value
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def page_size(self):
|
|
59
|
+
return self._configs.get(OperationLogsQueryConfig.PAGE_SIZE_KEY)
|
|
60
|
+
|
|
61
|
+
@page_size.setter
|
|
62
|
+
def page_size(self, value: str):
|
|
63
|
+
self._configs[OperationLogsQueryConfig.PAGE_SIZE_KEY] = value
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def trace_id(self):
|
|
67
|
+
return self._configs.get(OperationLogsQueryConfig.TRACE_ID_KEY)
|
|
68
|
+
|
|
69
|
+
@page_size.setter
|
|
70
|
+
def trace_id(self, value: str):
|
|
71
|
+
self._configs[OperationLogsQueryConfig.TRACE_ID_KEY] = value
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def create_api(ctx, vault_id, environment, token):
|
|
75
|
+
api = API(
|
|
76
|
+
api_root_url=env_url[environment],
|
|
77
|
+
params={}, # default params
|
|
78
|
+
headers={
|
|
79
|
+
"VGS-Tenant": vault_id,
|
|
80
|
+
"Content-Type": "application/vnd.api+json",
|
|
81
|
+
"Accept": "application/vnd.api+json",
|
|
82
|
+
"User-Agent": "VGS CLI {}".format(__version__),
|
|
83
|
+
"Authorization": "Bearer {}".format(token),
|
|
84
|
+
}, # default headers
|
|
85
|
+
timeout=50, # default timeout in seconds
|
|
86
|
+
append_slash=False, # append slash to final url
|
|
87
|
+
json_encode_body=True, # encode body as json
|
|
88
|
+
)
|
|
89
|
+
api.add_resource(resource_name="access_logs", resource_class=AccessLogsResource)
|
|
90
|
+
api.add_resource(
|
|
91
|
+
resource_name="operations_logs", resource_class=OperationsLogsResource
|
|
92
|
+
)
|
|
93
|
+
return api
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_api_url(ctx, vault_id, api):
|
|
97
|
+
response = api.accounts_api.get_vault_by_id(vault_id)
|
|
98
|
+
try:
|
|
99
|
+
return response.body["data"][0]["links"]["vault_management_api"]
|
|
100
|
+
except (KeyError, IndexError):
|
|
101
|
+
# if we weren't able to extract the vault_management_api it means that the provided vault_id doesn't exist
|
|
102
|
+
raise VaultNotFoundError(vault_id, ctx)
|
vgscli/auth.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from simple_rest_client.exceptions import ClientError
|
|
3
|
+
|
|
4
|
+
from vgscli.auth_server import AuthServer
|
|
5
|
+
from vgscli.errors import (
|
|
6
|
+
AuthenticationError,
|
|
7
|
+
AuthenticationRequiredError,
|
|
8
|
+
ClientCredentialsAuthenticationError,
|
|
9
|
+
TokenNotValidError,
|
|
10
|
+
)
|
|
11
|
+
from vgscli.keyring_token_util import KeyringTokenUtil
|
|
12
|
+
|
|
13
|
+
token_util = KeyringTokenUtil()
|
|
14
|
+
TOKEN_FILE_NAME = "vgs_token"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def handshake(ctx, environment):
|
|
18
|
+
try:
|
|
19
|
+
if not token_util.validate_access_token():
|
|
20
|
+
token_util.validate_refresh_token()
|
|
21
|
+
AuthServer(environment).refresh_authentication()
|
|
22
|
+
except ClientError as e:
|
|
23
|
+
if (
|
|
24
|
+
e.response.body
|
|
25
|
+
and e.response.body.get("error_description") == "Invalid refresh token"
|
|
26
|
+
):
|
|
27
|
+
raise AuthenticationRequiredError(ctx)
|
|
28
|
+
else:
|
|
29
|
+
raise AuthenticationError(ctx, e.args[0])
|
|
30
|
+
except TokenNotValidError:
|
|
31
|
+
raise AuthenticationRequiredError(ctx)
|
|
32
|
+
except Exception as e:
|
|
33
|
+
raise AuthenticationError(ctx, e.args[0])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def login(ctx, environment, **kwargs):
|
|
37
|
+
try:
|
|
38
|
+
access_token = AuthServer(environment).login(environment, **kwargs)
|
|
39
|
+
click.echo("Success!")
|
|
40
|
+
return access_token
|
|
41
|
+
except Exception as e:
|
|
42
|
+
raise AuthenticationError(ctx, e.args[0])
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def logout(ctx, environment):
|
|
46
|
+
try:
|
|
47
|
+
if token_util.tokens_exist():
|
|
48
|
+
AuthServer(environment).logout()
|
|
49
|
+
token_util.clear_tokens()
|
|
50
|
+
token_util.remove_encryption_secret()
|
|
51
|
+
click.echo("Success!")
|
|
52
|
+
else:
|
|
53
|
+
click.echo("Login data not found.")
|
|
54
|
+
except Exception as e:
|
|
55
|
+
raise AuthenticationError(ctx, e.args[0])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def client_credentials_login(ctx, client_id, client_secret, environment):
|
|
59
|
+
try:
|
|
60
|
+
if (
|
|
61
|
+
not token_util.is_access_token_valid()
|
|
62
|
+
or token_util.is_access_token_azp_changed(client_id)
|
|
63
|
+
):
|
|
64
|
+
AuthServer(environment).client_credentials_login(client_id, client_secret)
|
|
65
|
+
except (TokenNotValidError, ClientError):
|
|
66
|
+
raise ClientCredentialsAuthenticationError(ctx)
|
|
67
|
+
|
|
68
|
+
return True
|