okdata-cli 3.2.0__py3-none-any.whl → 4.1.0__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.
- okdata/cli/__main__.py +6 -5
- okdata/cli/command.py +2 -2
- okdata/cli/commands/datasets/datasets.py +4 -0
- okdata/cli/commands/datasets/questions.py +5 -56
- okdata/cli/commands/datasets/wizards.py +0 -7
- okdata/cli/commands/permissions.py +15 -14
- okdata/cli/commands/pubs/__init__.py +3 -0
- okdata/cli/commands/{pubreg → pubs}/client.py +29 -4
- okdata/cli/commands/{pubreg/pubreg.py → pubs/pubs.py} +83 -57
- okdata/cli/commands/{pubreg → pubs}/questions.py +80 -21
- okdata/cli/commands/{pubreg → pubs}/wizards.py +29 -16
- okdata/cli/commands/{datasets/validators.py → validators.py} +32 -45
- okdata/cli/commands/wizard.py +8 -0
- okdata/cli/data/output-format/datasets_dataset_config.json +0 -1
- okdata/cli/output.py +3 -4
- {okdata_cli-3.2.0.dist-info → okdata_cli-4.1.0.dist-info}/METADATA +3 -3
- {okdata_cli-3.2.0.dist-info → okdata_cli-4.1.0.dist-info}/RECORD +27 -27
- {okdata_cli-3.2.0.dist-info → okdata_cli-4.1.0.dist-info}/WHEEL +1 -1
- okdata/cli/commands/pubreg/__init__.py +0 -3
- /okdata/cli/data/output-format/{pubreg_audit_log_config.json → pubs_audit_log_config.json} +0 -0
- /okdata/cli/data/output-format/{pubreg_clients_config.json → pubs_clients_config.json} +0 -0
- /okdata/cli/data/output-format/{pubreg_multiple_client_keys_config.json → pubs_multiple_client_keys_config.json} +0 -0
- /okdata/cli/data/output-format/{pubreg_single_client_keys_config.json → pubs_single_client_keys_config.json} +0 -0
- /okdata_cli-3.2.0-nspkg.pth → /okdata_cli-4.1.0-nspkg.pth +0 -0
- {okdata_cli-3.2.0.dist-info → okdata_cli-4.1.0.dist-info}/LICENSE +0 -0
- {okdata_cli-3.2.0.dist-info → okdata_cli-4.1.0.dist-info}/entry_points.txt +0 -0
- {okdata_cli-3.2.0.dist-info → okdata_cli-4.1.0.dist-info}/namespace_packages.txt +0 -0
- {okdata_cli-3.2.0.dist-info → okdata_cli-4.1.0.dist-info}/top_level.txt +0 -0
okdata/cli/__main__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
|
-
from keycloak.exceptions import KeycloakGetError
|
|
4
|
+
from keycloak.exceptions import KeycloakGetError, KeycloakPostError
|
|
5
5
|
from okdata.sdk.exceptions import ApiAuthenticateError
|
|
6
6
|
from requests.exceptions import RequestException
|
|
7
7
|
|
|
@@ -9,7 +9,7 @@ from okdata.cli import MAINTAINER
|
|
|
9
9
|
from okdata.cli.command import BaseCommand
|
|
10
10
|
from okdata.cli.commands.datasets import DatasetsCommand
|
|
11
11
|
from okdata.cli.commands.permissions import PermissionsCommand
|
|
12
|
-
from okdata.cli.commands.
|
|
12
|
+
from okdata.cli.commands.pubs import PubsCommand
|
|
13
13
|
from okdata.cli.commands.status import StatusCommand
|
|
14
14
|
from okdata.cli.commands.teams.teams import TeamsCommand
|
|
15
15
|
|
|
@@ -40,11 +40,12 @@ def main():
|
|
|
40
40
|
"An error occurred (ApiAuthenticateError): Invalid credentials",
|
|
41
41
|
{"error": 1, "message": "Invalid credentials"},
|
|
42
42
|
)
|
|
43
|
-
except KeycloakGetError as e:
|
|
43
|
+
except (KeycloakGetError, KeycloakPostError) as e:
|
|
44
44
|
error = json.loads(e.error_message)
|
|
45
|
+
error_type = e.__class__.__name__
|
|
45
46
|
instance.log.info(f"Keycloak reported: {e}")
|
|
46
47
|
instance.print(
|
|
47
|
-
f"An error occurred (
|
|
48
|
+
f"An error occurred ({error_type}): {error['error_description']}"
|
|
48
49
|
)
|
|
49
50
|
except (EOFError, KeyboardInterrupt):
|
|
50
51
|
instance.print("\nAbort.")
|
|
@@ -69,7 +70,7 @@ def get_command_class(argv):
|
|
|
69
70
|
commands = {
|
|
70
71
|
"datasets": DatasetsCommand,
|
|
71
72
|
"permissions": PermissionsCommand,
|
|
72
|
-
"
|
|
73
|
+
"pubs": PubsCommand,
|
|
73
74
|
"status": StatusCommand,
|
|
74
75
|
"teams": TeamsCommand,
|
|
75
76
|
}
|
okdata/cli/command.py
CHANGED
|
@@ -21,7 +21,7 @@ class BaseCommand:
|
|
|
21
21
|
usage:
|
|
22
22
|
okdata datasets [options]
|
|
23
23
|
okdata permissions [options]
|
|
24
|
-
okdata
|
|
24
|
+
okdata pubs [options]
|
|
25
25
|
okdata status [options]
|
|
26
26
|
okdata teams [options]
|
|
27
27
|
okdata -h | --help
|
|
@@ -29,7 +29,7 @@ usage:
|
|
|
29
29
|
Commands available:
|
|
30
30
|
datasets
|
|
31
31
|
permissions
|
|
32
|
-
|
|
32
|
+
pubs
|
|
33
33
|
status
|
|
34
34
|
teams
|
|
35
35
|
|
|
@@ -292,6 +292,10 @@ Options:{BASE_COMMAND_OPTIONS}
|
|
|
292
292
|
parts = dataset_uri.split("/")
|
|
293
293
|
dataset_id, version, edition = parts + [None] * (3 - len(parts))
|
|
294
294
|
|
|
295
|
+
# First verify that the dataset exists; `get_dataset` raises an error
|
|
296
|
+
# if not.
|
|
297
|
+
self.sdk.get_dataset(dataset_id)
|
|
298
|
+
|
|
295
299
|
if auto_resolve:
|
|
296
300
|
if not version:
|
|
297
301
|
version = self._get_latest_version(dataset_id)["version"]
|
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import csv
|
|
2
|
-
|
|
3
1
|
from questionary import Choice
|
|
4
2
|
|
|
5
|
-
from okdata.cli.commands.
|
|
3
|
+
from okdata.cli.commands.validators import (
|
|
6
4
|
KeywordValidator,
|
|
7
|
-
PhoneValidator,
|
|
8
5
|
SimpleEmailValidator,
|
|
9
|
-
SpatialResolutionValidator,
|
|
10
|
-
SpatialValidator,
|
|
11
|
-
StandardsValidator,
|
|
12
6
|
TitleValidator,
|
|
13
7
|
)
|
|
14
|
-
from okdata.cli.commands.wizard import required_style
|
|
8
|
+
from okdata.cli.commands.wizard import filter_comma_separated, required_style
|
|
15
9
|
|
|
16
10
|
pipeline_choices = {
|
|
17
11
|
"file": [
|
|
18
12
|
Choice("Lagre dataen slik den er", "data-copy"),
|
|
13
|
+
Choice("Konverter fra CSV til Delta", "csv-to-delta"),
|
|
14
|
+
Choice("Konverter fra JSON til Delta", "json-to-delta"),
|
|
19
15
|
Choice("Konverter fra CSV til Parquet", "csv-to-parquet"),
|
|
20
16
|
Choice("Konverter fra Excel til CSV", "pipeline-excel-to-csv"),
|
|
21
17
|
Choice("Ingen prosessering (krever manuell konfigurasjon av pipeline)", False),
|
|
@@ -30,14 +26,7 @@ available_pipelines = [
|
|
|
30
26
|
]
|
|
31
27
|
|
|
32
28
|
|
|
33
|
-
def
|
|
34
|
-
values = next(
|
|
35
|
-
csv.reader([value], delimiter=",", escapechar="\\", skipinitialspace=True)
|
|
36
|
-
)
|
|
37
|
-
return [x.strip() for x in values if x]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def qs_create(include_extra_metadata=True):
|
|
29
|
+
def qs_create():
|
|
41
30
|
return [
|
|
42
31
|
{
|
|
43
32
|
**required_style,
|
|
@@ -67,38 +56,6 @@ def qs_create(include_extra_metadata=True):
|
|
|
67
56
|
"validate": KeywordValidator,
|
|
68
57
|
"filter": filter_comma_separated,
|
|
69
58
|
},
|
|
70
|
-
{
|
|
71
|
-
**required_style,
|
|
72
|
-
"type": "confirm",
|
|
73
|
-
"name": "contains_geodata",
|
|
74
|
-
"message": "Inneholder datasettet geodata?",
|
|
75
|
-
"default": False,
|
|
76
|
-
"when": lambda x: include_extra_metadata,
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
"type": "text",
|
|
80
|
-
"name": "spatial",
|
|
81
|
-
"message": "Romlig avgrensning (komma-separert)",
|
|
82
|
-
"validate": SpatialValidator,
|
|
83
|
-
"filter": filter_comma_separated,
|
|
84
|
-
"when": lambda x: include_extra_metadata and x["contains_geodata"],
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
"type": "text",
|
|
88
|
-
"name": "spatialResolutionInMeters",
|
|
89
|
-
"message": "Romlig oppløsning (i meter)",
|
|
90
|
-
"validate": SpatialResolutionValidator,
|
|
91
|
-
"filter": lambda v: float(v.replace(",", ".")) if v else None,
|
|
92
|
-
"when": lambda x: include_extra_metadata and x["contains_geodata"],
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
"type": "text",
|
|
96
|
-
"name": "conformsTo",
|
|
97
|
-
"message": "I samsvar med standarder (komma-separert)",
|
|
98
|
-
"validate": StandardsValidator,
|
|
99
|
-
"filter": filter_comma_separated,
|
|
100
|
-
"when": lambda x: include_extra_metadata,
|
|
101
|
-
},
|
|
102
59
|
{
|
|
103
60
|
**required_style,
|
|
104
61
|
"type": "select",
|
|
@@ -138,7 +95,6 @@ def qs_create(include_extra_metadata=True):
|
|
|
138
95
|
"http://creativecommons.org/publicdomain/zero/1.0/",
|
|
139
96
|
),
|
|
140
97
|
],
|
|
141
|
-
"when": lambda x: include_extra_metadata,
|
|
142
98
|
},
|
|
143
99
|
{"type": "text", "name": "name", "message": "Kontaktperson – navn"},
|
|
144
100
|
{
|
|
@@ -148,13 +104,6 @@ def qs_create(include_extra_metadata=True):
|
|
|
148
104
|
"message": "Kontaktperson – epost",
|
|
149
105
|
"validate": SimpleEmailValidator,
|
|
150
106
|
},
|
|
151
|
-
{
|
|
152
|
-
**required_style,
|
|
153
|
-
"type": "text",
|
|
154
|
-
"name": "phone",
|
|
155
|
-
"message": "Kontaktperson – telefon",
|
|
156
|
-
"validate": PhoneValidator,
|
|
157
|
-
},
|
|
158
107
|
{"type": "text", "name": "publisher", "message": "Utgiver"},
|
|
159
108
|
{
|
|
160
109
|
**required_style,
|
|
@@ -29,17 +29,10 @@ class DatasetCreateWizard:
|
|
|
29
29
|
"contactPoint": {
|
|
30
30
|
"name": choices["name"],
|
|
31
31
|
"email": choices["email"],
|
|
32
|
-
"phone": choices["phone"],
|
|
33
32
|
},
|
|
34
33
|
"publisher": choices["publisher"],
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
if choices.get("spatial"):
|
|
38
|
-
config["spatial"] = choices["spatial"]
|
|
39
|
-
if choices.get("spatialResolutionInMeters"):
|
|
40
|
-
config["spatialResolutionInMeters"] = choices["spatialResolutionInMeters"]
|
|
41
|
-
if choices.get("conformsTo"):
|
|
42
|
-
config["conformsTo"] = choices["conformsTo"]
|
|
43
36
|
if choices.get("license"):
|
|
44
37
|
config["license"] = choices["license"]
|
|
45
38
|
|
|
@@ -12,12 +12,13 @@ class PermissionsCommand(BaseCommand):
|
|
|
12
12
|
|
|
13
13
|
Usage:
|
|
14
14
|
okdata permissions ls [<resource_name>] [options]
|
|
15
|
-
okdata permissions add <resource_name> <user> <scope> [--team | --client] [options]
|
|
15
|
+
okdata permissions add <resource_name> <user> [<scope>] [--team | --client] [options]
|
|
16
16
|
okdata permissions rm <resource_name> <user> [<scope>] [--team | --client] [options]
|
|
17
17
|
|
|
18
18
|
Examples:
|
|
19
19
|
okdata permissions ls
|
|
20
20
|
okdata permissions ls okdata:dataset:my-dataset
|
|
21
|
+
okdata permissions add okdata:dataset:my-dataset janedoe
|
|
21
22
|
okdata permissions add okdata:dataset:my-dataset janedoe okdata:dataset:read
|
|
22
23
|
okdata permissions rm okdata:dataset:my-dataset janedoe
|
|
23
24
|
okdata permissions rm okdata:dataset:my-dataset janedoe okdata:dataset:write
|
|
@@ -57,10 +58,10 @@ Options:{BASE_COMMAND_OPTIONS}
|
|
|
57
58
|
]
|
|
58
59
|
if self.cmd("add"):
|
|
59
60
|
self.add_user(resource_name, user_class(user), scope)
|
|
60
|
-
self.print("Granted {} on '{}'
|
|
61
|
+
self.print("Granted {} on '{}' to {} '{}'".format(*fmt_args))
|
|
61
62
|
else:
|
|
62
63
|
self.remove_user(resource_name, user_class(user), scope)
|
|
63
|
-
self.print("Revoked {} on '{}'
|
|
64
|
+
self.print("Revoked {} on '{}' from {} '{}'".format(*fmt_args))
|
|
64
65
|
|
|
65
66
|
def list_my_permissions(self):
|
|
66
67
|
"""Print all permissions for the current user."""
|
|
@@ -99,9 +100,14 @@ Options:{BASE_COMMAND_OPTIONS}
|
|
|
99
100
|
out.add_rows(sorted(results, key=itemgetter("user_type", "user_name")))
|
|
100
101
|
self.print(f"Permissions for '{resource_name}'", out)
|
|
101
102
|
|
|
102
|
-
def add_user(self, resource_name, user, scope):
|
|
103
|
-
"""Grant `scope` on `resource_name` to `user`.
|
|
104
|
-
|
|
103
|
+
def add_user(self, resource_name, user, scope=None):
|
|
104
|
+
"""Grant `scope` on `resource_name` to `user`.
|
|
105
|
+
|
|
106
|
+
When `scope` is unspecified, grant every permission on `resource_name`.
|
|
107
|
+
"""
|
|
108
|
+
return self.client.update_permission(
|
|
109
|
+
resource_name, scope or "__all__", add_users=[user]
|
|
110
|
+
)
|
|
105
111
|
|
|
106
112
|
def remove_user(self, resource_name, user, scope=None):
|
|
107
113
|
"""Revoke `scope` on `resource_name` for `user`.
|
|
@@ -109,11 +115,6 @@ Options:{BASE_COMMAND_OPTIONS}
|
|
|
109
115
|
When `scope` is unspecified, revoke every permission on `resource_name`
|
|
110
116
|
for `user`.
|
|
111
117
|
"""
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
for permission in self.client.get_permissions(resource_name):
|
|
116
|
-
if user.user_id in permission[f"{user.user_type}s"]:
|
|
117
|
-
self.client.update_permission(
|
|
118
|
-
resource_name, permission["scope"], remove_users=[user]
|
|
119
|
-
)
|
|
118
|
+
return self.client.update_permission(
|
|
119
|
+
resource_name, scope or "__all__", remove_users=[user]
|
|
120
|
+
)
|
|
@@ -5,23 +5,48 @@ from okdata.sdk import SDK
|
|
|
5
5
|
log = logging.getLogger()
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class PubsClient(SDK):
|
|
9
9
|
def __init__(self, config=None, auth=None, env=None):
|
|
10
|
-
self.__name__ = "
|
|
10
|
+
self.__name__ = "pubs"
|
|
11
11
|
super().__init__(config, auth, env)
|
|
12
12
|
self.api_url = "https://api.data{}.oslo.systems/maskinporten".format(
|
|
13
13
|
"-dev" if self.config.config["env"] == "dev" else ""
|
|
14
14
|
)
|
|
15
15
|
|
|
16
|
-
def
|
|
16
|
+
def create_maskinporten_client(self, team_id, provider, integration, scopes, env):
|
|
17
17
|
data = {
|
|
18
|
+
"client_type": "maskinporten",
|
|
18
19
|
"team_id": team_id,
|
|
19
20
|
"provider": provider,
|
|
20
21
|
"integration": integration,
|
|
21
22
|
"scopes": scopes,
|
|
22
23
|
"env": env,
|
|
23
24
|
}
|
|
24
|
-
log.info(f"Creating client with payload: {data}")
|
|
25
|
+
log.info(f"Creating Maskinporten client with payload: {data}")
|
|
26
|
+
return self.post(f"{self.api_url}/clients", data=data).json()
|
|
27
|
+
|
|
28
|
+
def create_idporten_client(
|
|
29
|
+
self,
|
|
30
|
+
team_id,
|
|
31
|
+
integration,
|
|
32
|
+
client_uri,
|
|
33
|
+
frontchannel_logout_uri,
|
|
34
|
+
redirect_uris,
|
|
35
|
+
post_logout_redirect_uris,
|
|
36
|
+
env,
|
|
37
|
+
):
|
|
38
|
+
data = {
|
|
39
|
+
"client_type": "idporten",
|
|
40
|
+
"team_id": team_id,
|
|
41
|
+
"provider": "idporten",
|
|
42
|
+
"integration": integration,
|
|
43
|
+
"client_uri": client_uri,
|
|
44
|
+
"frontchannel_logout_uri": frontchannel_logout_uri,
|
|
45
|
+
"redirect_uris": redirect_uris,
|
|
46
|
+
"post_logout_redirect_uris": post_logout_redirect_uris,
|
|
47
|
+
"env": env,
|
|
48
|
+
}
|
|
49
|
+
log.info(f"Creating ID-porten client with payload: {data}")
|
|
25
50
|
return self.post(f"{self.api_url}/clients", data=data).json()
|
|
26
51
|
|
|
27
52
|
def get_clients(self, env):
|
|
@@ -6,9 +6,14 @@ from okdata.sdk.team.client import TeamClient
|
|
|
6
6
|
|
|
7
7
|
from okdata.cli import MAINTAINER
|
|
8
8
|
from okdata.cli.command import BASE_COMMAND_OPTIONS, BaseCommand
|
|
9
|
-
from okdata.cli.commands.
|
|
10
|
-
from okdata.cli.commands.
|
|
11
|
-
|
|
9
|
+
from okdata.cli.commands.pubs.client import PubsClient
|
|
10
|
+
from okdata.cli.commands.pubs.questions import (
|
|
11
|
+
NoClientsError,
|
|
12
|
+
NoKeysError,
|
|
13
|
+
client_types,
|
|
14
|
+
providers,
|
|
15
|
+
)
|
|
16
|
+
from okdata.cli.commands.pubs.wizards import (
|
|
12
17
|
audit_log_wizard,
|
|
13
18
|
create_client_wizard,
|
|
14
19
|
create_key_wizard,
|
|
@@ -21,33 +26,33 @@ from okdata.cli.commands.teams.questions import NoTeamError
|
|
|
21
26
|
from okdata.cli.output import create_output
|
|
22
27
|
|
|
23
28
|
|
|
24
|
-
class
|
|
25
|
-
__doc__ = f"""Oslo :: Public
|
|
29
|
+
class PubsCommand(BaseCommand):
|
|
30
|
+
__doc__ = f"""Oslo :: Public services
|
|
26
31
|
|
|
27
32
|
Usage:
|
|
28
|
-
okdata
|
|
29
|
-
okdata
|
|
30
|
-
okdata
|
|
31
|
-
okdata
|
|
32
|
-
okdata
|
|
33
|
-
okdata
|
|
34
|
-
okdata
|
|
33
|
+
okdata pubs create-client [options]
|
|
34
|
+
okdata pubs list-clients [options]
|
|
35
|
+
okdata pubs delete-client [options]
|
|
36
|
+
okdata pubs create-key [options]
|
|
37
|
+
okdata pubs list-keys [options]
|
|
38
|
+
okdata pubs delete-key [options]
|
|
39
|
+
okdata pubs audit-log [options]
|
|
35
40
|
|
|
36
41
|
Examples:
|
|
37
|
-
okdata
|
|
38
|
-
okdata
|
|
39
|
-
okdata
|
|
40
|
-
okdata
|
|
41
|
-
okdata
|
|
42
|
-
okdata
|
|
43
|
-
okdata
|
|
42
|
+
okdata pubs create-client
|
|
43
|
+
okdata pubs list-clients
|
|
44
|
+
okdata pubs delete-client
|
|
45
|
+
okdata pubs create-key
|
|
46
|
+
okdata pubs list-keys
|
|
47
|
+
okdata pubs delete-key
|
|
48
|
+
okdata pubs audit-log
|
|
44
49
|
|
|
45
50
|
Options:{BASE_COMMAND_OPTIONS}
|
|
46
51
|
"""
|
|
47
52
|
|
|
48
53
|
def __init__(self):
|
|
49
54
|
super().__init__()
|
|
50
|
-
self.
|
|
55
|
+
self.pubs_client = PubsClient(env=self.opt("env"))
|
|
51
56
|
self.team_client = TeamClient(env=self.opt("env"))
|
|
52
57
|
|
|
53
58
|
def handler(self):
|
|
@@ -77,40 +82,55 @@ Options:{BASE_COMMAND_OPTIONS}
|
|
|
77
82
|
return
|
|
78
83
|
|
|
79
84
|
team_id = config["team_id"]
|
|
85
|
+
client_type_id = config["client_type_id"]
|
|
80
86
|
provider_id = config["provider_id"]
|
|
81
|
-
provider_name = config["provider_name"]
|
|
82
87
|
integration = config["integration"]
|
|
83
88
|
scopes = config["scopes"]
|
|
84
89
|
env = config["env"]
|
|
85
90
|
|
|
86
91
|
self.confirm_to_continue(
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
"Will create a new {} client{} in {}{}.".format(
|
|
93
|
+
client_types[client_type_id],
|
|
94
|
+
f" for {providers[provider_id]}" if provider_id else "",
|
|
95
|
+
env,
|
|
96
|
+
f" with scopes {scopes}" if scopes else "",
|
|
97
|
+
)
|
|
89
98
|
)
|
|
90
99
|
|
|
91
100
|
self.print("Creating client...")
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
if client_type_id == "maskinporten":
|
|
102
|
+
response = self.pubs_client.create_maskinporten_client(
|
|
103
|
+
team_id, provider_id, integration, scopes, env
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
response = self.pubs_client.create_idporten_client(
|
|
107
|
+
team_id,
|
|
108
|
+
integration,
|
|
109
|
+
config["client_uri"],
|
|
110
|
+
config["frontchannel_logout_uri"],
|
|
111
|
+
config["redirect_uris"],
|
|
112
|
+
config["post_logout_redirect_uris"],
|
|
113
|
+
env,
|
|
114
|
+
)
|
|
95
115
|
client_name = response["client_name"]
|
|
96
116
|
self.print(
|
|
97
117
|
f"""
|
|
98
118
|
Done! Created a new client '{client_name}'.
|
|
99
119
|
You may now go ahead and create a key for it by running:
|
|
100
120
|
|
|
101
|
-
okdata
|
|
121
|
+
okdata pubs create-key"""
|
|
102
122
|
)
|
|
103
123
|
|
|
104
124
|
def list_clients(self):
|
|
105
125
|
config = list_clients_wizard()
|
|
106
126
|
env = config["env"]
|
|
107
|
-
clients = self.
|
|
108
|
-
out = create_output(self.opt("format"), "
|
|
127
|
+
clients = self.pubs_client.get_clients(env)
|
|
128
|
+
out = create_output(self.opt("format"), "pubs_clients_config.json")
|
|
109
129
|
out.add_rows(sorted(clients, key=itemgetter("client_name")))
|
|
110
130
|
self.print(f"Clients in {env}:", out)
|
|
111
131
|
|
|
112
132
|
def delete_client(self):
|
|
113
|
-
choices = delete_client_wizard(self.
|
|
133
|
+
choices = delete_client_wizard(self.pubs_client)
|
|
114
134
|
env = choices["env"]
|
|
115
135
|
client_id = choices["client_id"]
|
|
116
136
|
client_name = choices["client_name"]
|
|
@@ -123,16 +143,18 @@ You may now go ahead and create a key for it by running:
|
|
|
123
143
|
client_name,
|
|
124
144
|
env,
|
|
125
145
|
(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
146
|
+
(
|
|
147
|
+
f" along with the key stored in AWS account {aws_account} "
|
|
148
|
+
f"({aws_region})"
|
|
149
|
+
)
|
|
150
|
+
if delete_from_aws
|
|
151
|
+
else ""
|
|
152
|
+
),
|
|
131
153
|
),
|
|
132
154
|
)
|
|
133
155
|
|
|
134
156
|
self.print(f"Deleting client '{client_name}' [{env}]...")
|
|
135
|
-
res = self.
|
|
157
|
+
res = self.pubs_client.delete_client(env, client_id, aws_account, aws_region)
|
|
136
158
|
deleted_ssm_params = res["deleted_ssm_params"]
|
|
137
159
|
|
|
138
160
|
self.print("\nDone! The client is deleted and will no longer work.")
|
|
@@ -203,11 +225,11 @@ You may now go ahead and create a key for it by running:
|
|
|
203
225
|
|
|
204
226
|
def create_client_key(self):
|
|
205
227
|
try:
|
|
206
|
-
config = create_key_wizard(self.
|
|
228
|
+
config = create_key_wizard(self.pubs_client)
|
|
207
229
|
except NoClientsError:
|
|
208
230
|
self.print(
|
|
209
231
|
"No clients in the given environment yet!\n\n"
|
|
210
|
-
" Create one with `okdata
|
|
232
|
+
" Create one with `okdata pubs create-client`.\n"
|
|
211
233
|
)
|
|
212
234
|
return
|
|
213
235
|
|
|
@@ -226,22 +248,26 @@ You may now go ahead and create a key for it by running:
|
|
|
226
248
|
client_name,
|
|
227
249
|
env,
|
|
228
250
|
(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
251
|
+
(
|
|
252
|
+
f"send it to AWS account {aws_account} ({aws_region}), "
|
|
253
|
+
"REPLACING any existing key for this client"
|
|
254
|
+
)
|
|
255
|
+
if key_destination == "aws"
|
|
256
|
+
else "save it locally"
|
|
257
|
+
),
|
|
258
|
+
),
|
|
259
|
+
(
|
|
260
|
+
"The key will be rotated automatically every night on weekdays.\n"
|
|
261
|
+
if enable_auto_rotate
|
|
262
|
+
else "The key will NOT be rotated automatically.\n"
|
|
234
263
|
),
|
|
235
|
-
"The key will be rotated automatically every night on weekdays.\n"
|
|
236
|
-
if enable_auto_rotate
|
|
237
|
-
else "The key will NOT be rotated automatically.\n",
|
|
238
264
|
]
|
|
239
265
|
)
|
|
240
266
|
)
|
|
241
267
|
|
|
242
268
|
self.print("Creating key for '{}' [{}]...".format(client_name, env))
|
|
243
269
|
|
|
244
|
-
key = self.
|
|
270
|
+
key = self.pubs_client.create_key(
|
|
245
271
|
env, client_id, aws_account, aws_region, enable_auto_rotate
|
|
246
272
|
)
|
|
247
273
|
|
|
@@ -256,10 +282,10 @@ You may now go ahead and create a key for it by running:
|
|
|
256
282
|
)
|
|
257
283
|
|
|
258
284
|
def _list_client_keys_single(self, client_id, client_name, env):
|
|
259
|
-
keys = self.
|
|
285
|
+
keys = self.pubs_client.get_keys(env, client_id)
|
|
260
286
|
out = create_output(
|
|
261
287
|
self.opt("format"),
|
|
262
|
-
"
|
|
288
|
+
"pubs_single_client_keys_config.json",
|
|
263
289
|
)
|
|
264
290
|
out.add_rows(sorted(keys, key=itemgetter("expires")))
|
|
265
291
|
self.print(f"Keys for client {client_name} [{env}]:", out)
|
|
@@ -269,17 +295,17 @@ You may now go ahead and create a key for it by running:
|
|
|
269
295
|
keys = [
|
|
270
296
|
{**key, "client_name": client["name"]}
|
|
271
297
|
for client in clients
|
|
272
|
-
for key in self.
|
|
298
|
+
for key in self.pubs_client.get_keys(env, client["id"])
|
|
273
299
|
]
|
|
274
300
|
out = create_output(
|
|
275
301
|
self.opt("format"),
|
|
276
|
-
"
|
|
302
|
+
"pubs_multiple_client_keys_config.json",
|
|
277
303
|
)
|
|
278
304
|
out.add_rows(sorted(keys, key=itemgetter("expires")))
|
|
279
305
|
self.print(f"All client keys [{env}]:", out)
|
|
280
306
|
|
|
281
307
|
def list_client_keys(self):
|
|
282
|
-
choices = list_keys_wizard(self.
|
|
308
|
+
choices = list_keys_wizard(self.pubs_client)
|
|
283
309
|
env = choices["env"]
|
|
284
310
|
|
|
285
311
|
if "clients" in choices:
|
|
@@ -291,7 +317,7 @@ You may now go ahead and create a key for it by running:
|
|
|
291
317
|
|
|
292
318
|
def delete_client_key(self):
|
|
293
319
|
try:
|
|
294
|
-
choices = delete_key_wizard(self.
|
|
320
|
+
choices = delete_key_wizard(self.pubs_client)
|
|
295
321
|
except NoKeysError:
|
|
296
322
|
self.print("The selected client doesn't have any keys.")
|
|
297
323
|
return
|
|
@@ -306,17 +332,17 @@ You may now go ahead and create a key for it by running:
|
|
|
306
332
|
)
|
|
307
333
|
|
|
308
334
|
self.print(f"Deleting key '{key_id}' from '{client_name}' [{env}]...")
|
|
309
|
-
self.
|
|
335
|
+
self.pubs_client.delete_key(env, client_id, key_id)
|
|
310
336
|
self.print("Done! The key is deleted and will no longer work.")
|
|
311
337
|
|
|
312
338
|
def audit_log(self):
|
|
313
|
-
choices = audit_log_wizard(self.
|
|
339
|
+
choices = audit_log_wizard(self.pubs_client)
|
|
314
340
|
env = choices["env"]
|
|
315
341
|
client_id = choices["client_id"]
|
|
316
342
|
client_name = choices["client_name"]
|
|
317
343
|
|
|
318
|
-
audit_log = self.
|
|
344
|
+
audit_log = self.pubs_client.get_audit_log(env, client_id)
|
|
319
345
|
|
|
320
|
-
out = create_output(self.opt("format"), "
|
|
346
|
+
out = create_output(self.opt("format"), "pubs_audit_log_config.json")
|
|
321
347
|
out.add_rows(sorted(audit_log, key=itemgetter("timestamp")))
|
|
322
348
|
self.print(f"Audit log for client {client_name} [{env}]:", out)
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import re
|
|
2
1
|
from operator import itemgetter
|
|
3
2
|
|
|
4
3
|
from questionary import Choice
|
|
5
4
|
|
|
6
|
-
from okdata.cli.commands.
|
|
5
|
+
from okdata.cli.commands.validators import (
|
|
6
|
+
AWSAccountValidator,
|
|
7
|
+
IntegrationValidator,
|
|
8
|
+
URIListValidator,
|
|
9
|
+
URIValidator,
|
|
10
|
+
)
|
|
11
|
+
from okdata.cli.commands.wizard import filter_comma_separated, required_style
|
|
12
|
+
|
|
13
|
+
client_types = {
|
|
14
|
+
"idporten": "ID-porten",
|
|
15
|
+
"maskinporten": "Maskinporten",
|
|
16
|
+
}
|
|
7
17
|
|
|
8
|
-
|
|
18
|
+
providers = {
|
|
9
19
|
"freg": "Folkeregisteret",
|
|
10
20
|
"krr": "Kontaktregisteret",
|
|
11
21
|
"skatt": "Skatteetaten",
|
|
@@ -62,18 +72,29 @@ def q_env():
|
|
|
62
72
|
**required_style,
|
|
63
73
|
"type": "select",
|
|
64
74
|
"name": "env",
|
|
65
|
-
"message": "
|
|
75
|
+
"message": "Environment",
|
|
66
76
|
"choices": _environments,
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
|
|
80
|
+
def q_client_type():
|
|
81
|
+
return {
|
|
82
|
+
**required_style,
|
|
83
|
+
"type": "select",
|
|
84
|
+
"name": "client_type_id",
|
|
85
|
+
"message": "Client type",
|
|
86
|
+
"choices": [Choice(pname, pid) for pid, pname in client_types.items()],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
70
90
|
def q_provider():
|
|
71
91
|
return {
|
|
72
92
|
**required_style,
|
|
73
93
|
"type": "select",
|
|
74
94
|
"name": "provider_id",
|
|
75
95
|
"message": "Provider",
|
|
76
|
-
"choices": [Choice(pname, pid) for pid, pname in
|
|
96
|
+
"choices": [Choice(pname, pid) for pid, pname in providers.items()],
|
|
97
|
+
"when": lambda x: x.get("client_type_id") == "maskinporten",
|
|
77
98
|
}
|
|
78
99
|
|
|
79
100
|
|
|
@@ -83,19 +104,13 @@ def q_scopes():
|
|
|
83
104
|
"type": "checkbox",
|
|
84
105
|
"name": "scopes",
|
|
85
106
|
"message": "Scopes",
|
|
86
|
-
"choices": lambda x: _scopes[x["provider_id"]],
|
|
107
|
+
"choices": lambda x: _scopes[x["provider_id"]] if "provider_id" in x else [],
|
|
87
108
|
"validate": (lambda choices: bool(choices) or "Select at least one scope"),
|
|
109
|
+
"when": lambda x: x.get("client_type_id") == "maskinporten",
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
|
|
91
113
|
def q_integration():
|
|
92
|
-
def _validate_integration(text):
|
|
93
|
-
if len(text) > 30:
|
|
94
|
-
return "Too long!"
|
|
95
|
-
if not re.fullmatch("[0-9a-z-]+", text):
|
|
96
|
-
return 'Only lowercase letters, numbers and "-", please'
|
|
97
|
-
return True
|
|
98
|
-
|
|
99
114
|
return {
|
|
100
115
|
**required_style,
|
|
101
116
|
"type": "text",
|
|
@@ -104,13 +119,59 @@ def q_integration():
|
|
|
104
119
|
"instruction": (
|
|
105
120
|
"(identifying in which system or case this client will be used)"
|
|
106
121
|
),
|
|
107
|
-
"validate":
|
|
122
|
+
"validate": IntegrationValidator,
|
|
108
123
|
}
|
|
109
124
|
|
|
110
125
|
|
|
111
|
-
def
|
|
126
|
+
def q_redirect_uris():
|
|
127
|
+
return {
|
|
128
|
+
**required_style,
|
|
129
|
+
"type": "text",
|
|
130
|
+
"name": "redirect_uris",
|
|
131
|
+
"message": "Redirect URIs (comma-separated)",
|
|
132
|
+
"filter": filter_comma_separated,
|
|
133
|
+
"when": lambda x: x.get("client_type_id") == "idporten",
|
|
134
|
+
"validate": URIListValidator,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def q_post_logout_redirect_uris():
|
|
139
|
+
return {
|
|
140
|
+
**required_style,
|
|
141
|
+
"type": "text",
|
|
142
|
+
"name": "post_logout_redirect_uris",
|
|
143
|
+
"message": "Post logout Redirect URIs (comma-separated)",
|
|
144
|
+
"filter": filter_comma_separated,
|
|
145
|
+
"when": lambda x: x.get("client_type_id") == "idporten",
|
|
146
|
+
"validate": URIListValidator,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def q_frontchannel_logout_uri():
|
|
151
|
+
return {
|
|
152
|
+
**required_style,
|
|
153
|
+
"type": "text",
|
|
154
|
+
"name": "frontchannel_logout_uri",
|
|
155
|
+
"message": "Frontchannel logout URI",
|
|
156
|
+
"when": lambda x: x.get("client_type_id") == "idporten",
|
|
157
|
+
"validate": URIValidator,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def q_client_uri():
|
|
162
|
+
return {
|
|
163
|
+
**required_style,
|
|
164
|
+
"type": "text",
|
|
165
|
+
"name": "client_uri",
|
|
166
|
+
"message": "Client URI (back URI)",
|
|
167
|
+
"when": lambda x: x.get("client_type_id") == "idporten",
|
|
168
|
+
"validate": URIValidator,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def q_client(pubs_client, allow_all=False):
|
|
112
173
|
def _client_choices(env):
|
|
113
|
-
clients =
|
|
174
|
+
clients = pubs_client.get_clients(env)
|
|
114
175
|
|
|
115
176
|
if not clients:
|
|
116
177
|
raise NoClientsError
|
|
@@ -165,9 +226,7 @@ def q_aws_account():
|
|
|
165
226
|
"name": "aws_account",
|
|
166
227
|
"message": "AWS account number",
|
|
167
228
|
"when": lambda x: x.get("key_destination") == "aws" or x.get("delete_from_aws"),
|
|
168
|
-
"validate":
|
|
169
|
-
lambda t: bool(re.fullmatch("[0-9]{12}", t)) or "12 digits, please"
|
|
170
|
-
),
|
|
229
|
+
"validate": AWSAccountValidator,
|
|
171
230
|
}
|
|
172
231
|
|
|
173
232
|
|
|
@@ -198,9 +257,9 @@ def q_enable_auto_rotate():
|
|
|
198
257
|
}
|
|
199
258
|
|
|
200
259
|
|
|
201
|
-
def q_key(
|
|
260
|
+
def q_key(pubs_client):
|
|
202
261
|
def _key_choices(env, client_id):
|
|
203
|
-
keys =
|
|
262
|
+
keys = pubs_client.get_keys(env, client_id)
|
|
204
263
|
|
|
205
264
|
if not keys:
|
|
206
265
|
raise NoKeysError
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
from okdata.cli.commands.
|
|
2
|
-
_providers,
|
|
1
|
+
from okdata.cli.commands.pubs.questions import (
|
|
3
2
|
q_aws_account,
|
|
4
3
|
q_aws_region,
|
|
5
4
|
q_client,
|
|
5
|
+
q_client_type,
|
|
6
|
+
q_client_uri,
|
|
6
7
|
q_delete_from_aws,
|
|
7
8
|
q_enable_auto_rotate,
|
|
8
9
|
q_env,
|
|
10
|
+
q_frontchannel_logout_uri,
|
|
9
11
|
q_integration,
|
|
10
12
|
q_key,
|
|
11
13
|
q_key_destination,
|
|
14
|
+
q_post_logout_redirect_uris,
|
|
12
15
|
q_provider,
|
|
16
|
+
q_redirect_uris,
|
|
13
17
|
q_scopes,
|
|
14
18
|
)
|
|
15
19
|
from okdata.cli.commands.teams.questions import q_team
|
|
@@ -20,17 +24,26 @@ def create_client_wizard(team_client):
|
|
|
20
24
|
choices = run_questionnaire(
|
|
21
25
|
q_env(),
|
|
22
26
|
q_team(team_client),
|
|
27
|
+
q_client_type(),
|
|
23
28
|
q_provider(),
|
|
24
29
|
q_scopes(),
|
|
25
30
|
q_integration(),
|
|
31
|
+
q_redirect_uris(),
|
|
32
|
+
q_post_logout_redirect_uris(),
|
|
33
|
+
q_frontchannel_logout_uri(),
|
|
34
|
+
q_client_uri(),
|
|
26
35
|
)
|
|
27
36
|
return {
|
|
28
37
|
"env": choices["env"],
|
|
29
38
|
"team_id": choices.get("team_id"),
|
|
30
|
-
"
|
|
31
|
-
"
|
|
39
|
+
"client_type_id": choices["client_type_id"],
|
|
40
|
+
"provider_id": choices.get("provider_id"),
|
|
32
41
|
"integration": choices["integration"],
|
|
33
|
-
"scopes": choices
|
|
42
|
+
"scopes": choices.get("scopes"),
|
|
43
|
+
"redirect_uris": choices.get("redirect_uris"),
|
|
44
|
+
"post_logout_redirect_uris": choices.get("post_logout_redirect_uris"),
|
|
45
|
+
"frontchannel_logout_uri": choices.get("frontchannel_logout_uri"),
|
|
46
|
+
"client_uri": choices.get("client_uri"),
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
|
|
@@ -39,10 +52,10 @@ def list_clients_wizard():
|
|
|
39
52
|
return {"env": choices["env"]}
|
|
40
53
|
|
|
41
54
|
|
|
42
|
-
def delete_client_wizard(
|
|
55
|
+
def delete_client_wizard(pubs_client):
|
|
43
56
|
choices = run_questionnaire(
|
|
44
57
|
q_env(),
|
|
45
|
-
q_client(
|
|
58
|
+
q_client(pubs_client),
|
|
46
59
|
q_delete_from_aws(),
|
|
47
60
|
q_aws_account(),
|
|
48
61
|
q_aws_region(),
|
|
@@ -57,10 +70,10 @@ def delete_client_wizard(pubreg_client):
|
|
|
57
70
|
}
|
|
58
71
|
|
|
59
72
|
|
|
60
|
-
def create_key_wizard(
|
|
73
|
+
def create_key_wizard(pubs_client):
|
|
61
74
|
choices = run_questionnaire(
|
|
62
75
|
q_env(),
|
|
63
|
-
q_client(
|
|
76
|
+
q_client(pubs_client),
|
|
64
77
|
q_key_destination(),
|
|
65
78
|
q_aws_account(),
|
|
66
79
|
q_aws_region(),
|
|
@@ -77,8 +90,8 @@ def create_key_wizard(pubreg_client):
|
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
|
|
80
|
-
def list_keys_wizard(
|
|
81
|
-
choices = run_questionnaire(q_env(), q_client(
|
|
93
|
+
def list_keys_wizard(pubs_client):
|
|
94
|
+
choices = run_questionnaire(q_env(), q_client(pubs_client, True))
|
|
82
95
|
return {
|
|
83
96
|
"env": choices["env"],
|
|
84
97
|
**(
|
|
@@ -94,11 +107,11 @@ def list_keys_wizard(pubreg_client):
|
|
|
94
107
|
}
|
|
95
108
|
|
|
96
109
|
|
|
97
|
-
def delete_key_wizard(
|
|
110
|
+
def delete_key_wizard(pubs_client):
|
|
98
111
|
choices = run_questionnaire(
|
|
99
112
|
q_env(),
|
|
100
|
-
q_client(
|
|
101
|
-
q_key(
|
|
113
|
+
q_client(pubs_client),
|
|
114
|
+
q_key(pubs_client),
|
|
102
115
|
)
|
|
103
116
|
return {
|
|
104
117
|
"env": choices["env"],
|
|
@@ -108,8 +121,8 @@ def delete_key_wizard(pubreg_client):
|
|
|
108
121
|
}
|
|
109
122
|
|
|
110
123
|
|
|
111
|
-
def audit_log_wizard(
|
|
112
|
-
choices = run_questionnaire(q_env(), q_client(
|
|
124
|
+
def audit_log_wizard(pubs_client):
|
|
125
|
+
choices = run_questionnaire(q_env(), q_client(pubs_client))
|
|
113
126
|
return {
|
|
114
127
|
"env": choices["env"],
|
|
115
128
|
"client_id": choices["client"]["id"],
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import re
|
|
2
1
|
import csv
|
|
3
2
|
import datetime
|
|
3
|
+
import re
|
|
4
|
+
from types import SimpleNamespace
|
|
5
|
+
from urllib.parse import urlparse
|
|
4
6
|
|
|
5
7
|
from questionary import Validator, ValidationError
|
|
6
8
|
|
|
@@ -31,15 +33,6 @@ class SimpleEmailValidator(Validator):
|
|
|
31
33
|
)
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
class PhoneValidator(Validator):
|
|
35
|
-
def validate(self, document):
|
|
36
|
-
if not re.match(r"^[0-9]+$", document.text):
|
|
37
|
-
raise ValidationError(
|
|
38
|
-
message="Please enter a valid phone - all numbers and no space, example: 232334455",
|
|
39
|
-
cursor_position=len(document.text),
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
36
|
class TitleValidator(Validator):
|
|
44
37
|
def validate(self, document):
|
|
45
38
|
if len(document.text) < 5:
|
|
@@ -68,55 +61,49 @@ class KeywordValidator(Validator):
|
|
|
68
61
|
)
|
|
69
62
|
|
|
70
63
|
|
|
71
|
-
class
|
|
64
|
+
class IntegrationValidator(Validator):
|
|
72
65
|
def validate(self, document):
|
|
73
|
-
|
|
74
|
-
[document.text], delimiter=",", escapechar="\\", skipinitialspace=True
|
|
75
|
-
)
|
|
76
|
-
standards = [x.strip() for x in next(standards)]
|
|
77
|
-
for standard in standards:
|
|
78
|
-
if len(standard) < 3:
|
|
79
|
-
raise ValidationError(
|
|
80
|
-
message="Each standard reference must be at least 3 characters",
|
|
81
|
-
cursor_position=len(document.text),
|
|
82
|
-
)
|
|
83
|
-
if len(standards) != len(set(standards)):
|
|
66
|
+
if len(document.text) > 30:
|
|
84
67
|
raise ValidationError(
|
|
85
|
-
message="
|
|
68
|
+
message="Too long!", cursor_position=len(document.text)
|
|
69
|
+
)
|
|
70
|
+
if not re.fullmatch("[0-9a-z-]+", document.text):
|
|
71
|
+
raise ValidationError(
|
|
72
|
+
message='Only lowercase letters, numbers and "-", please',
|
|
86
73
|
cursor_position=len(document.text),
|
|
87
74
|
)
|
|
88
75
|
|
|
89
76
|
|
|
90
|
-
class
|
|
77
|
+
class AWSAccountValidator(Validator):
|
|
91
78
|
def validate(self, document):
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if
|
|
79
|
+
if not re.fullmatch("[0-9]{12}", document.text):
|
|
80
|
+
raise ValidationError(
|
|
81
|
+
message="12 digits, please", cursor_position=len(document.text)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class URIValidator(Validator):
|
|
86
|
+
def validate(self, document):
|
|
87
|
+
parsed = urlparse(document.text)
|
|
88
|
+
|
|
89
|
+
if not (parsed.scheme and parsed.netloc):
|
|
103
90
|
raise ValidationError(
|
|
104
|
-
message="
|
|
91
|
+
message="Please enter a valid URI, including scheme (e.g. https://...)",
|
|
105
92
|
cursor_position=len(document.text),
|
|
106
93
|
)
|
|
107
94
|
|
|
108
95
|
|
|
109
|
-
class
|
|
96
|
+
class URIListValidator(Validator):
|
|
110
97
|
def validate(self, document):
|
|
111
|
-
|
|
112
|
-
return None
|
|
98
|
+
uri_validator = URIValidator()
|
|
113
99
|
try:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
except ValueError:
|
|
100
|
+
for val in document.text.split(","):
|
|
101
|
+
uri_validator.validate(SimpleNamespace(text=val))
|
|
102
|
+
except ValidationError:
|
|
118
103
|
raise ValidationError(
|
|
119
|
-
message=
|
|
104
|
+
message=(
|
|
105
|
+
"Please enter a comma-separated list of URIs, including scheme "
|
|
106
|
+
"(e.g. https://...)"
|
|
107
|
+
),
|
|
120
108
|
cursor_position=len(document.text),
|
|
121
109
|
)
|
|
122
|
-
return None
|
okdata/cli/commands/wizard.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import csv
|
|
1
2
|
import sys
|
|
2
3
|
|
|
3
4
|
from prompt_toolkit.styles import Style
|
|
@@ -17,3 +18,10 @@ def run_questionnaire(*questions):
|
|
|
17
18
|
sys.exit()
|
|
18
19
|
|
|
19
20
|
return choices
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def filter_comma_separated(value):
|
|
24
|
+
values = next(
|
|
25
|
+
csv.reader([value], delimiter=",", escapechar="\\", skipinitialspace=True)
|
|
26
|
+
)
|
|
27
|
+
return [x.strip() for x in values if x]
|
okdata/cli/output.py
CHANGED
|
@@ -41,12 +41,11 @@ class TableOutput(PrettyTable):
|
|
|
41
41
|
super().__init__(column_headers)
|
|
42
42
|
|
|
43
43
|
def field_values(self, row, row_key, key):
|
|
44
|
-
|
|
44
|
+
values = []
|
|
45
45
|
for field in self.config[key]["fields"]:
|
|
46
46
|
if field in row[row_key]:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return value
|
|
47
|
+
values.append(str(row[row_key][field]))
|
|
48
|
+
return "\n".join(values)
|
|
50
49
|
|
|
51
50
|
def get_row_value(self, row, key):
|
|
52
51
|
row_key = self.config[key]["key"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: okdata-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.1.0
|
|
4
4
|
Summary: CLI for services provided by Oslo Origo
|
|
5
5
|
Home-page: https://github.com/oslokommune/okdata-cli/
|
|
6
6
|
Author: Oslo Origo
|
|
@@ -19,7 +19,7 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: PrettyTable
|
|
21
21
|
Requires-Dist: docopt
|
|
22
|
-
Requires-Dist: okdata-sdk <4,>=3.1
|
|
22
|
+
Requires-Dist: okdata-sdk <4,>=3.1.1
|
|
23
23
|
Requires-Dist: questionary <2.0.0,>=1.10.0
|
|
24
24
|
Requires-Dist: requests
|
|
25
25
|
|
|
@@ -29,7 +29,7 @@ Okdata CLI provides a unified interface to data services for Oslo Origo teams.
|
|
|
29
29
|
|
|
30
30
|
* [Install](doc/install.md)
|
|
31
31
|
* [Configuration](doc/configuration.md)
|
|
32
|
-
* [Public
|
|
32
|
+
* [Public services](doc/pubs.md)
|
|
33
33
|
* [Teams](doc/teams.md)
|
|
34
34
|
* [Datasets](doc/datasets.md)
|
|
35
35
|
* [Permissions](doc/permissions.md)
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
okdata_cli-
|
|
1
|
+
okdata_cli-4.1.0-nspkg.pth,sha256=4nrP4RXuGOCDLZ0px0sO63KvnhR4W_t4MWel1CEdd2U,539
|
|
2
2
|
okdata/cli/__init__.py,sha256=72PLqW3-JGeKxQGusk3AX78ITwcn7DDawEMNLlsurz4,27
|
|
3
|
-
okdata/cli/__main__.py,sha256=
|
|
4
|
-
okdata/cli/command.py,sha256=
|
|
3
|
+
okdata/cli/__main__.py,sha256=HpSOoNdhNn68GB9rkJZXDPx07RWxQjBkCEVIz9N_ydo,2608
|
|
4
|
+
okdata/cli/command.py,sha256=H65OJP72fRgip_vK_vJ8TDNan18OyO6WQNtj797mO3U,4281
|
|
5
5
|
okdata/cli/io.py,sha256=cNm-h86fm3ez59A9rhWO0xnHBTIe-_Xj9BtcjKa34lg,730
|
|
6
|
-
okdata/cli/output.py,sha256=
|
|
6
|
+
okdata/cli/output.py,sha256=HAQKEwTj_GJXzIUqi4U3-VAt6CClRO2f3k6ZuDRjQ5A,4793
|
|
7
7
|
okdata/cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
okdata/cli/commands/permissions.py,sha256=
|
|
8
|
+
okdata/cli/commands/permissions.py,sha256=eLR9IGPi5OMZ7r46Bfl0SKS6WthmU2rSRcQh3n8mG9o,4476
|
|
9
9
|
okdata/cli/commands/status.py,sha256=NkAuNEUWVumDsU-axFSOrX7AAJ-XV0fevsBbxpbl5CU,3998
|
|
10
|
-
okdata/cli/commands/
|
|
10
|
+
okdata/cli/commands/validators.py,sha256=lkLo4UPmwXkKzEIFs0jucPVBV4jOPTZjzefpAvP1vdU,3882
|
|
11
|
+
okdata/cli/commands/wizard.py,sha256=FFNK6h50atWh0Tl7lOx1lSU0jC6QNRz0SqDuv5OcRew,541
|
|
11
12
|
okdata/cli/commands/datasets/__init__.py,sha256=kS1uB6u_0VI1jm_TQjaCvTMqPFHnKSQITPUuPB_SY00,69
|
|
12
|
-
okdata/cli/commands/datasets/datasets.py,sha256=
|
|
13
|
-
okdata/cli/commands/datasets/questions.py,sha256=
|
|
14
|
-
okdata/cli/commands/datasets/
|
|
15
|
-
okdata/cli/commands/
|
|
16
|
-
okdata/cli/commands/
|
|
17
|
-
okdata/cli/commands/
|
|
18
|
-
okdata/cli/commands/
|
|
19
|
-
okdata/cli/commands/
|
|
20
|
-
okdata/cli/commands/pubreg/wizards.py,sha256=_oEyJWQMaIfH0fIJs5KEggqtT1YimfwualeNGCqjzAU,3121
|
|
13
|
+
okdata/cli/commands/datasets/datasets.py,sha256=vt5MnhkLOAv-TFCcz4MkapBsDYwimXPV6TkoF5KFb3A,14045
|
|
14
|
+
okdata/cli/commands/datasets/questions.py,sha256=svesJS9MGwN3y2Utsy43HwWKPxe-K15neKt8A2eozqg,4004
|
|
15
|
+
okdata/cli/commands/datasets/wizards.py,sha256=X2xwiZpQhum9looqYbcuGgvSO1J_wAkaRNQf7NudxqU,3335
|
|
16
|
+
okdata/cli/commands/pubs/__init__.py,sha256=T105M_46YzyZ2Cici48QKNyjfJTIQJ1uyMFlo91rIZ4,81
|
|
17
|
+
okdata/cli/commands/pubs/client.py,sha256=dOabTkeZKPvHFbgwZC4SX4DyvnvEezpeSiLQ1EY1hlo,3223
|
|
18
|
+
okdata/cli/commands/pubs/pubs.py,sha256=WOeZvg8bUUJ7bXSvtfByF84IlH70jFF6-fe3tsvjhAY,12002
|
|
19
|
+
okdata/cli/commands/pubs/questions.py,sha256=0UwZ87Zcx5eAWLO3FI4aoLGvY3lQuLqEcYa6kUT8OLo,7020
|
|
20
|
+
okdata/cli/commands/pubs/wizards.py,sha256=fNhj-7G7ePEYVLrhmdXdNzRSdD-9BzVAcJxujuz7LwQ,3613
|
|
21
21
|
okdata/cli/commands/teams/questions.py,sha256=UqfHK2bwl6retyYYC-IywG_39aRDC-WbKYpMIQ83gfE,2997
|
|
22
22
|
okdata/cli/commands/teams/teams.py,sha256=ta4RRgRp-89CACh98nvbfbaxnE3EnloHVkNslBtgwKY,5633
|
|
23
23
|
okdata/cli/commands/teams/util.py,sha256=c7U6jI7CN7MUyfsK6Z3AawL0SJJBlFar4FvXfsJQzSk,456
|
|
@@ -25,25 +25,25 @@ okdata/cli/commands/teams/wizards.py,sha256=bARELvUPdcbj5O7O1xwEL8Bcv7YNZYt2zyXq
|
|
|
25
25
|
okdata/cli/data/output-format/datasets_config.json,sha256=rAvcNeBc0d53tWSAlWnt8IxvjNDWqm-b1sEgfk5IvF8,180
|
|
26
26
|
okdata/cli/data/output-format/datasets_copy_file_config.json,sha256=wAKnX_B7d1keRGdIQBwsvF25oyD1aC2ZYLTDi_KPWzY,262
|
|
27
27
|
okdata/cli/data/output-format/datasets_copy_file_config_2.json,sha256=7U0ka_IgSJv6vxQ6q7La8MPMy45Vwxb39_YSlQBMha0,206
|
|
28
|
-
okdata/cli/data/output-format/datasets_dataset_config.json,sha256=
|
|
28
|
+
okdata/cli/data/output-format/datasets_dataset_config.json,sha256=J8uVCrU5ehHs3jYrqRm5HgJHnYmkWJaoZCcZnVWilpA,344
|
|
29
29
|
okdata/cli/data/output-format/datasets_dataset_version_config.json,sha256=PW1PoW7PWkJj76RyfD5xEbYoeCf1yeC94s-ua5Z1GU8,345
|
|
30
30
|
okdata/cli/data/output-format/datasets_dataset_version_edition_config.json,sha256=_UECCluFSMWwyIiZ1rBCS2LOnxiN1roZVEbQ71AW5Fo,328
|
|
31
31
|
okdata/cli/data/output-format/datasets_dataset_version_edition_distributions_config.json,sha256=xTuQTNfBnnJFDF5aFLY5bBT1-qzd5cbceKqqxmF0NUU,119
|
|
32
32
|
okdata/cli/data/output-format/datasets_dataset_versions_config.json,sha256=rqrhf-xK_RYkbP3dCZrn6m7CDVNn_Pg0GknGj2wDtHw,209
|
|
33
33
|
okdata/cli/data/output-format/my_permissions_config.json,sha256=VPx95G_e6D6OJQJmXrS8ASKwUJP0uETqWam681edBQU,146
|
|
34
34
|
okdata/cli/data/output-format/permissions_config.json,sha256=uPrYKhnEZEImsFZ4CpVdeOb8NrtdlnvfT4L0pvqiBuw,204
|
|
35
|
-
okdata/cli/data/output-format/
|
|
36
|
-
okdata/cli/data/output-format/
|
|
37
|
-
okdata/cli/data/output-format/
|
|
38
|
-
okdata/cli/data/output-format/
|
|
35
|
+
okdata/cli/data/output-format/pubs_audit_log_config.json,sha256=hXZV2-OUKElGiHTVQbslZqe_IeP1eGjktzhhAAcIbzg,301
|
|
36
|
+
okdata/cli/data/output-format/pubs_clients_config.json,sha256=aFbQIQldHfsRnI39SCE0cmm-3MDFq57r0ZsIzZ9HsHA,246
|
|
37
|
+
okdata/cli/data/output-format/pubs_multiple_client_keys_config.json,sha256=_mGvDgNeLBZZwos6JXVBESyHjYNIje8zKv08MKMVaug,201
|
|
38
|
+
okdata/cli/data/output-format/pubs_single_client_keys_config.json,sha256=LO78-h-9o_tfHSNQ_4dOfsxuNvoc1TxbOPTvseEZy6w,125
|
|
39
39
|
okdata/cli/data/output-format/status_config.json,sha256=oq9uYZbPZrM2ksWrQDd9-VWO0Ea7jMe02TDgxg6XLEA,354
|
|
40
40
|
okdata/cli/data/output-format/status_history_config.json,sha256=Gh-l8w9688P0hdp_DtJ9cxMc1bdv5DuNp5QBftOnb9M,721
|
|
41
41
|
okdata/cli/data/output-format/team_members_config.json,sha256=CYzlVLU3f41kpxBbovXYDYRYOvhScfYU2yxtwHFMcuQ,183
|
|
42
42
|
okdata/cli/data/output-format/teams_config.json,sha256=z49y_7m3b9W02guXXcSdlX9XDAaIj0rRBDcqU68m9i0,124
|
|
43
|
-
okdata_cli-
|
|
44
|
-
okdata_cli-
|
|
45
|
-
okdata_cli-
|
|
46
|
-
okdata_cli-
|
|
47
|
-
okdata_cli-
|
|
48
|
-
okdata_cli-
|
|
49
|
-
okdata_cli-
|
|
43
|
+
okdata_cli-4.1.0.dist-info/LICENSE,sha256=9oNBrI2Aw9TCn-T0CamN4hpU6Dzm-IkFzX0573Q211Q,1077
|
|
44
|
+
okdata_cli-4.1.0.dist-info/METADATA,sha256=ymI274uuzhj-H3WCGCCxfUrzyWLpQSHYlnJxTBoVUT4,1196
|
|
45
|
+
okdata_cli-4.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
46
|
+
okdata_cli-4.1.0.dist-info/entry_points.txt,sha256=OyY5saQTVah33LZ9ixM40rsMPgsOKErioZA8rtVjgKo,52
|
|
47
|
+
okdata_cli-4.1.0.dist-info/namespace_packages.txt,sha256=Pdf-ZpWHLtLL295sDedsbAP3f6H189730mXrfVAVCcY,7
|
|
48
|
+
okdata_cli-4.1.0.dist-info/top_level.txt,sha256=cS1IEDv6oxHgvQHrGSxFxjHTv5CNc9dLowH1vpCqF_k,11
|
|
49
|
+
okdata_cli-4.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|