scim2-cli 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/PKG-INFO +18 -5
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/README.md +15 -3
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/pyproject.toml +3 -2
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/scim2_cli/__init__.py +3 -4
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/scim2_cli/create.py +17 -12
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/scim2_cli/delete.py +26 -6
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/scim2_cli/query.py +21 -21
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/scim2_cli/replace.py +17 -12
- {scim2_cli-0.1.1 → scim2_cli-0.1.2}/scim2_cli/search.py +15 -9
- scim2_cli-0.1.2/scim2_cli/test.py +40 -0
- scim2_cli-0.1.2/scim2_cli/utils.py +36 -0
- scim2_cli-0.1.1/scim2_cli/utils.py +0 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: scim2-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: SCIM application development CLI
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: scim,scim2,provisioning
|
|
@@ -20,13 +20,21 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
21
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
22
|
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
23
|
-
Requires-Dist: scim2-client (>=0.1.
|
|
23
|
+
Requires-Dist: scim2-client (>=0.1.6,<0.2.0)
|
|
24
|
+
Requires-Dist: scim2-tester (>=0.1.2,<0.2.0)
|
|
24
25
|
Requires-Dist: sphinx-click-rst-to-ansi-formatter (>=0.1.0,<0.2.0)
|
|
25
26
|
Description-Content-Type: text/markdown
|
|
26
27
|
|
|
27
28
|
# scim2-cli
|
|
28
29
|
|
|
29
|
-
An utility command line to help you perform requests against a SCIM server, while validating input and response payloads.
|
|
30
|
+
An utility command line to help you perform requests against a SCIM server, while validating input and response payloads. It also uses [scim2-tester](https://scim2-tester.readthedocs.io) to perform a [SCIM server compliance test](https://scim2-cli.readthedocs.io/en/latest/reference.html#scim-url-test).
|
|
31
|
+
|
|
32
|
+
## What's SCIM anyway?
|
|
33
|
+
|
|
34
|
+
SCIM stands for System for Cross-domain Identity Management, and it is a provisioning protocol.
|
|
35
|
+
Provisioning is the action of managing a set of resources across different services, usually users and groups.
|
|
36
|
+
SCIM is often used between Identity Providers and applications in completion of standards like OAuth2 and OpenID Connect.
|
|
37
|
+
It allows users and groups creations, modifications and deletions to be synchronized between applications.
|
|
30
38
|
|
|
31
39
|
## Installation
|
|
32
40
|
|
|
@@ -41,7 +49,7 @@ Check the [reference](https://scim2-cli.readthedocs.io/en/latest/reference.html)
|
|
|
41
49
|
Here is an example of resource creation:
|
|
42
50
|
|
|
43
51
|
```shell
|
|
44
|
-
$ scim2 https://auth.example create user << EOL
|
|
52
|
+
$ scim2 https://auth.example --headers "Authorization: Bearer 12345" create user << EOL
|
|
45
53
|
{
|
|
46
54
|
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
47
55
|
"userName": "bjensen@example.com"
|
|
@@ -64,7 +72,7 @@ EOL
|
|
|
64
72
|
Here is an example of resource query:
|
|
65
73
|
|
|
66
74
|
```shell
|
|
67
|
-
$ scim2 https://auth.example query user 2819c223-7f76-453a-919d-413861904646
|
|
75
|
+
$ scim2 https://auth.example --header "Authorization: Bearer 12345" query user 2819c223-7f76-453a-919d-413861904646
|
|
68
76
|
{
|
|
69
77
|
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
70
78
|
"id": "2819c223-7f76-453a-919d-413861904646",
|
|
@@ -79,3 +87,8 @@ $ scim2 https://auth.example query user 2819c223-7f76-453a-919d-413861904646
|
|
|
79
87
|
}
|
|
80
88
|
```
|
|
81
89
|
|
|
90
|
+
scim2-cli belongs in a collection of SCIM tools developed by [Yaal Coop](https://yaal.coop),
|
|
91
|
+
with [scim2-models](https://github.com/yaal-coop/scim2-models),
|
|
92
|
+
[scim2-client](https://github.com/yaal-coop/scim2-client) and
|
|
93
|
+
[scim2-tester](https://github.com/yaal-coop/scim2-tester)
|
|
94
|
+
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# scim2-cli
|
|
2
2
|
|
|
3
|
-
An utility command line to help you perform requests against a SCIM server, while validating input and response payloads.
|
|
3
|
+
An utility command line to help you perform requests against a SCIM server, while validating input and response payloads. It also uses [scim2-tester](https://scim2-tester.readthedocs.io) to perform a [SCIM server compliance test](https://scim2-cli.readthedocs.io/en/latest/reference.html#scim-url-test).
|
|
4
|
+
|
|
5
|
+
## What's SCIM anyway?
|
|
6
|
+
|
|
7
|
+
SCIM stands for System for Cross-domain Identity Management, and it is a provisioning protocol.
|
|
8
|
+
Provisioning is the action of managing a set of resources across different services, usually users and groups.
|
|
9
|
+
SCIM is often used between Identity Providers and applications in completion of standards like OAuth2 and OpenID Connect.
|
|
10
|
+
It allows users and groups creations, modifications and deletions to be synchronized between applications.
|
|
4
11
|
|
|
5
12
|
## Installation
|
|
6
13
|
|
|
@@ -15,7 +22,7 @@ Check the [reference](https://scim2-cli.readthedocs.io/en/latest/reference.html)
|
|
|
15
22
|
Here is an example of resource creation:
|
|
16
23
|
|
|
17
24
|
```shell
|
|
18
|
-
$ scim2 https://auth.example create user << EOL
|
|
25
|
+
$ scim2 https://auth.example --headers "Authorization: Bearer 12345" create user << EOL
|
|
19
26
|
{
|
|
20
27
|
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
21
28
|
"userName": "bjensen@example.com"
|
|
@@ -38,7 +45,7 @@ EOL
|
|
|
38
45
|
Here is an example of resource query:
|
|
39
46
|
|
|
40
47
|
```shell
|
|
41
|
-
$ scim2 https://auth.example query user 2819c223-7f76-453a-919d-413861904646
|
|
48
|
+
$ scim2 https://auth.example --header "Authorization: Bearer 12345" query user 2819c223-7f76-453a-919d-413861904646
|
|
42
49
|
{
|
|
43
50
|
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
44
51
|
"id": "2819c223-7f76-453a-919d-413861904646",
|
|
@@ -52,3 +59,8 @@ $ scim2 https://auth.example query user 2819c223-7f76-453a-919d-413861904646
|
|
|
52
59
|
}
|
|
53
60
|
}
|
|
54
61
|
```
|
|
62
|
+
|
|
63
|
+
scim2-cli belongs in a collection of SCIM tools developed by [Yaal Coop](https://yaal.coop),
|
|
64
|
+
with [scim2-models](https://github.com/yaal-coop/scim2-models),
|
|
65
|
+
[scim2-client](https://github.com/yaal-coop/scim2-client) and
|
|
66
|
+
[scim2-tester](https://github.com/yaal-coop/scim2-tester)
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "scim2-cli"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "SCIM application development CLI"
|
|
9
9
|
authors = ["Yaal Coop <contact@yaal.coop>"]
|
|
10
10
|
license = "MIT"
|
|
@@ -27,7 +27,8 @@ classifiers = [
|
|
|
27
27
|
[tool.poetry.dependencies]
|
|
28
28
|
python = "^3.10"
|
|
29
29
|
click = "^8.1.7"
|
|
30
|
-
scim2-client = "^0.1.
|
|
30
|
+
scim2-client = "^0.1.6"
|
|
31
|
+
scim2-tester = "^0.1.2"
|
|
31
32
|
sphinx-click-rst-to-ansi-formatter = "^0.1.0"
|
|
32
33
|
|
|
33
34
|
[tool.poetry.group.doc]
|
|
@@ -3,7 +3,6 @@ import json
|
|
|
3
3
|
import click
|
|
4
4
|
from httpx import Client
|
|
5
5
|
from scim2_client import SCIMClient
|
|
6
|
-
from scim2_models import EnterpriseUser
|
|
7
6
|
from scim2_models import Group
|
|
8
7
|
from scim2_models import User
|
|
9
8
|
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
@@ -13,6 +12,7 @@ from .delete import delete_cli
|
|
|
13
12
|
from .query import query_cli
|
|
14
13
|
from .replace import replace_cli
|
|
15
14
|
from .search import search_cli
|
|
15
|
+
from .test import test_cli
|
|
16
16
|
from .utils import DOC_URL
|
|
17
17
|
|
|
18
18
|
|
|
@@ -25,9 +25,7 @@ def cli(ctx, url):
|
|
|
25
25
|
ctx.ensure_object(dict)
|
|
26
26
|
ctx.obj["URL"] = url
|
|
27
27
|
client = Client(base_url=ctx.obj["URL"])
|
|
28
|
-
ctx.obj["client"] = SCIMClient(
|
|
29
|
-
client, resource_types=(User, User[EnterpriseUser], Group)
|
|
30
|
-
)
|
|
28
|
+
ctx.obj["client"] = SCIMClient(client, resource_types=(User, Group))
|
|
31
29
|
ctx.obj["resource_types"] = {
|
|
32
30
|
resource_type.__name__.lower(): resource_type
|
|
33
31
|
for resource_type in ctx.obj["client"].resource_types
|
|
@@ -47,3 +45,4 @@ cli.add_command(query_cli)
|
|
|
47
45
|
cli.add_command(replace_cli)
|
|
48
46
|
cli.add_command(delete_cli)
|
|
49
47
|
cli.add_command(search_cli)
|
|
48
|
+
cli.add_command(test_cli)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
import click
|
|
2
|
-
import httpx
|
|
3
4
|
from click import ClickException
|
|
4
|
-
from pydantic import ValidationError
|
|
5
5
|
from scim2_client import SCIMClientError
|
|
6
6
|
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
7
7
|
|
|
8
8
|
from .utils import DOC_URL
|
|
9
9
|
from .utils import formatted_payload
|
|
10
|
+
from .utils import split_headers
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="create")
|
|
@@ -17,7 +18,10 @@ from .utils import formatted_payload
|
|
|
17
18
|
default=True,
|
|
18
19
|
help="Indent JSON response payloads.",
|
|
19
20
|
)
|
|
20
|
-
|
|
21
|
+
@click.option(
|
|
22
|
+
"-h", "--headers", multiple=True, help="Header to pass in the HTTP requests."
|
|
23
|
+
)
|
|
24
|
+
def create_cli(ctx, indent, headers):
|
|
21
25
|
"""Perform a `SCIM POST <https://www.rfc-editor.org/rfc/rfc7644#section-3.3>`_ request
|
|
22
26
|
on resources endpoint.
|
|
23
27
|
|
|
@@ -34,15 +38,16 @@ def create_cli(ctx, indent):
|
|
|
34
38
|
raise ClickException("Input data is missing")
|
|
35
39
|
|
|
36
40
|
try:
|
|
37
|
-
response = ctx.obj["client"].create(payload)
|
|
38
|
-
|
|
39
|
-
except
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
response = ctx.obj["client"].create(payload, headers=split_headers(headers))
|
|
42
|
+
|
|
43
|
+
except SCIMClientError as scim_exc:
|
|
44
|
+
message = str(scim_exc)
|
|
45
|
+
if sys.version_info >= (3, 11) and hasattr(
|
|
46
|
+
scim_exc, "__notes__"
|
|
47
|
+
): # pragma: no cover
|
|
48
|
+
for note in scim_exc.__notes__:
|
|
49
|
+
message = f"{message}\n{note}"
|
|
50
|
+
raise ClickException(message) from scim_exc
|
|
46
51
|
|
|
47
52
|
payload = formatted_payload(response.model_dump(), indent)
|
|
48
53
|
click.echo(payload)
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
import click
|
|
2
|
-
import httpx
|
|
3
4
|
from click import ClickException
|
|
4
5
|
from scim2_client import SCIMClientError
|
|
6
|
+
from scim2_models import Message
|
|
7
|
+
from scim2_models import Resource
|
|
5
8
|
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
6
9
|
|
|
7
10
|
from .utils import DOC_URL
|
|
8
11
|
from .utils import formatted_payload
|
|
12
|
+
from .utils import split_headers
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="delete")
|
|
12
16
|
@click.argument("resource-type", required=True)
|
|
13
17
|
@click.argument("id", required=True)
|
|
18
|
+
@click.option(
|
|
19
|
+
"-h", "--headers", multiple=True, help="Header to pass in the HTTP requests."
|
|
20
|
+
)
|
|
14
21
|
@click.option(
|
|
15
22
|
"--indent/--no-indent",
|
|
16
23
|
is_flag=True,
|
|
@@ -18,7 +25,7 @@ from .utils import formatted_payload
|
|
|
18
25
|
help="Indent JSON response payloads.",
|
|
19
26
|
)
|
|
20
27
|
@click.pass_context
|
|
21
|
-
def delete_cli(ctx, resource_type, id, indent):
|
|
28
|
+
def delete_cli(ctx, resource_type, id, headers, indent):
|
|
22
29
|
"""Perform a `SCIM DELETE query <https://www.rfc-editor.org/rfc/rfc7644#section-3.6>`_ request.
|
|
23
30
|
|
|
24
31
|
.. code-block:: bash
|
|
@@ -35,11 +42,24 @@ def delete_cli(ctx, resource_type, id, indent):
|
|
|
35
42
|
)
|
|
36
43
|
|
|
37
44
|
try:
|
|
38
|
-
response = ctx.obj["client"].delete(
|
|
45
|
+
response = ctx.obj["client"].delete(
|
|
46
|
+
resource_type, id, headers=split_headers(headers)
|
|
47
|
+
)
|
|
39
48
|
|
|
40
|
-
except
|
|
41
|
-
|
|
49
|
+
except SCIMClientError as scim_exc:
|
|
50
|
+
message = str(scim_exc)
|
|
51
|
+
if sys.version_info >= (3, 11) and hasattr(
|
|
52
|
+
scim_exc, "__notes__"
|
|
53
|
+
): # pragma: no cover
|
|
54
|
+
for note in scim_exc.__notes__:
|
|
55
|
+
message = f"{message}\n{note}"
|
|
56
|
+
raise ClickException(message) from scim_exc
|
|
42
57
|
|
|
43
58
|
if response:
|
|
44
|
-
payload =
|
|
59
|
+
payload = (
|
|
60
|
+
response.model_dump()
|
|
61
|
+
if isinstance(response, (Resource, Message))
|
|
62
|
+
else response
|
|
63
|
+
)
|
|
64
|
+
payload = formatted_payload(payload, indent)
|
|
45
65
|
click.echo(payload)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
from typing import List
|
|
2
3
|
from typing import Optional
|
|
3
4
|
|
|
4
5
|
import click
|
|
5
|
-
import httpx
|
|
6
6
|
from click import ClickException
|
|
7
|
-
from pydantic import ValidationError
|
|
8
7
|
from scim2_client import SCIMClientError
|
|
9
8
|
from scim2_models import SearchRequest
|
|
10
9
|
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
11
10
|
|
|
12
11
|
from .utils import DOC_URL
|
|
13
12
|
from .utils import formatted_payload
|
|
13
|
+
from .utils import split_headers
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="query")
|
|
@@ -48,6 +48,9 @@ from .utils import formatted_payload
|
|
|
48
48
|
"--sort-order",
|
|
49
49
|
help="A string indicating the order in which the “sortBy” parameter is applied.",
|
|
50
50
|
)
|
|
51
|
+
@click.option(
|
|
52
|
+
"-h", "--headers", multiple=True, help="Header to pass in the HTTP requests."
|
|
53
|
+
)
|
|
51
54
|
@click.option(
|
|
52
55
|
"--indent/--no-indent",
|
|
53
56
|
is_flag=True,
|
|
@@ -65,6 +68,7 @@ def query_cli(
|
|
|
65
68
|
filter: str,
|
|
66
69
|
sort_by: str,
|
|
67
70
|
sort_order: str,
|
|
71
|
+
headers: List[str],
|
|
68
72
|
indent: bool,
|
|
69
73
|
):
|
|
70
74
|
"""Perform a `SCIM GET <https://www.rfc-editor.org/rfc/rfc7644#section-3.4.1>`_ request
|
|
@@ -107,26 +111,22 @@ def query_cli(
|
|
|
107
111
|
)
|
|
108
112
|
|
|
109
113
|
try:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
else:
|
|
119
|
-
response = ctx.obj["client"].query_all(
|
|
120
|
-
search_request=payload, check_request_payload=check_request_payload
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
except (httpx.HTTPError, SCIMClientError) as exc:
|
|
124
|
-
raise ClickException(exc) from exc
|
|
114
|
+
response = ctx.obj["client"].query(
|
|
115
|
+
resource_type,
|
|
116
|
+
id,
|
|
117
|
+
search_request=payload,
|
|
118
|
+
check_request_payload=check_request_payload,
|
|
119
|
+
headers=split_headers(headers),
|
|
120
|
+
)
|
|
125
121
|
|
|
126
|
-
except
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
except SCIMClientError as scim_exc:
|
|
123
|
+
message = str(scim_exc)
|
|
124
|
+
if sys.version_info >= (3, 11) and hasattr(
|
|
125
|
+
scim_exc, "__notes__"
|
|
126
|
+
): # pragma: no cover
|
|
127
|
+
for note in scim_exc.__notes__:
|
|
128
|
+
message = f"{message}\n{note}"
|
|
129
|
+
raise ClickException(message) from scim_exc
|
|
130
130
|
|
|
131
131
|
payload = formatted_payload(response.model_dump(), indent)
|
|
132
132
|
click.echo(payload)
|
|
@@ -1,23 +1,27 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
import click
|
|
2
|
-
import httpx
|
|
3
4
|
from click import ClickException
|
|
4
|
-
from pydantic import ValidationError
|
|
5
5
|
from scim2_client import SCIMClientError
|
|
6
6
|
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
7
7
|
|
|
8
8
|
from .utils import DOC_URL
|
|
9
9
|
from .utils import formatted_payload
|
|
10
|
+
from .utils import split_headers
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="replace")
|
|
13
14
|
@click.pass_context
|
|
15
|
+
@click.option(
|
|
16
|
+
"-h", "--headers", multiple=True, help="Header to pass in the HTTP requests."
|
|
17
|
+
)
|
|
14
18
|
@click.option(
|
|
15
19
|
"--indent/--no-indent",
|
|
16
20
|
is_flag=True,
|
|
17
21
|
default=True,
|
|
18
22
|
help="Indent JSON response payloads.",
|
|
19
23
|
)
|
|
20
|
-
def replace_cli(ctx, indent):
|
|
24
|
+
def replace_cli(ctx, headers, indent):
|
|
21
25
|
"""Perform a `SCIM PUT <https://www.rfc-editor.org/rfc/rfc7644#section-3.5.1>`_ request
|
|
22
26
|
on the resources endpoint.
|
|
23
27
|
|
|
@@ -34,15 +38,16 @@ def replace_cli(ctx, indent):
|
|
|
34
38
|
raise ClickException("Input data is missing")
|
|
35
39
|
|
|
36
40
|
try:
|
|
37
|
-
response = ctx.obj["client"].replace(payload)
|
|
38
|
-
|
|
39
|
-
except
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
response = ctx.obj["client"].replace(payload, headers=split_headers(headers))
|
|
42
|
+
|
|
43
|
+
except SCIMClientError as scim_exc:
|
|
44
|
+
message = str(scim_exc)
|
|
45
|
+
if sys.version_info >= (3, 11) and hasattr(
|
|
46
|
+
scim_exc, "__notes__"
|
|
47
|
+
): # pragma: no cover
|
|
48
|
+
for note in scim_exc.__notes__:
|
|
49
|
+
message = f"{message}\n{note}"
|
|
50
|
+
raise ClickException(message) from scim_exc
|
|
46
51
|
|
|
47
52
|
payload = formatted_payload(response.model_dump(), indent)
|
|
48
53
|
click.echo(payload)
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
from typing import List
|
|
2
3
|
|
|
3
4
|
import click
|
|
4
|
-
import httpx
|
|
5
5
|
from click import ClickException
|
|
6
|
-
from pydantic import ValidationError
|
|
7
6
|
from scim2_client import SCIMClientError
|
|
8
7
|
from scim2_models import SearchRequest
|
|
9
8
|
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
10
9
|
|
|
11
10
|
from .utils import DOC_URL
|
|
12
11
|
from .utils import formatted_payload
|
|
12
|
+
from .utils import split_headers
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="search")
|
|
@@ -45,6 +45,9 @@ from .utils import formatted_payload
|
|
|
45
45
|
"--sort-order",
|
|
46
46
|
help="A string indicating the order in which the “sortBy” parameter is applied.",
|
|
47
47
|
)
|
|
48
|
+
@click.option(
|
|
49
|
+
"-h", "--headers", multiple=True, help="Header to pass in the HTTP requests."
|
|
50
|
+
)
|
|
48
51
|
@click.option(
|
|
49
52
|
"--indent/--no-indent",
|
|
50
53
|
is_flag=True,
|
|
@@ -60,6 +63,7 @@ def search_cli(
|
|
|
60
63
|
filter: str,
|
|
61
64
|
sort_by: str,
|
|
62
65
|
sort_order: str,
|
|
66
|
+
headers: List[str],
|
|
63
67
|
indent: bool,
|
|
64
68
|
):
|
|
65
69
|
"""Perform a `SCIM GET <https://www.rfc-editor.org/rfc/rfc7644#section-3.4.1>`_ request
|
|
@@ -92,15 +96,17 @@ def search_cli(
|
|
|
92
96
|
response = ctx.obj["client"].search(
|
|
93
97
|
search_request=payload,
|
|
94
98
|
check_request_payload=check_request_payload,
|
|
99
|
+
headers=split_headers(headers),
|
|
95
100
|
)
|
|
96
101
|
|
|
97
|
-
except
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
except SCIMClientError as scim_exc:
|
|
103
|
+
message = str(scim_exc)
|
|
104
|
+
if sys.version_info >= (3, 11) and hasattr(
|
|
105
|
+
scim_exc, "__notes__"
|
|
106
|
+
): # pragma: no cover
|
|
107
|
+
for note in scim_exc.__notes__:
|
|
108
|
+
message = f"{message}\n{note}"
|
|
109
|
+
raise ClickException(message) from scim_exc
|
|
104
110
|
|
|
105
111
|
payload = formatted_payload(response.model_dump(), indent)
|
|
106
112
|
click.echo(payload)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from scim2_tester import Status
|
|
3
|
+
from scim2_tester import check_server
|
|
4
|
+
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
5
|
+
|
|
6
|
+
from .utils import DOC_URL
|
|
7
|
+
from .utils import Color
|
|
8
|
+
from .utils import split_headers
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="test")
|
|
12
|
+
@click.pass_context
|
|
13
|
+
@click.option(
|
|
14
|
+
"-h", "--headers", multiple=True, help="Header to pass in the HTTP requests."
|
|
15
|
+
)
|
|
16
|
+
@click.option("-v", "--verbose", is_flag=True, help="Enables verbose mode")
|
|
17
|
+
def test_cli(ctx, headers, verbose):
|
|
18
|
+
"""Perform a server SCIM compliance check using :doc:`scim2-tester
|
|
19
|
+
<scim2-tester:index>`.
|
|
20
|
+
|
|
21
|
+
.. code-block:: bash
|
|
22
|
+
|
|
23
|
+
scim https://scim.example test
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
client = ctx.obj["client"]
|
|
27
|
+
client.client.headers.update(split_headers(headers))
|
|
28
|
+
results = check_server(client)
|
|
29
|
+
click.echo(f"Performing a SCIM compliance check on {client.client.base_url} ...")
|
|
30
|
+
for result in results:
|
|
31
|
+
if result.status == Status.SUCCESS:
|
|
32
|
+
status = click.style(result.status.name, fg=Color.green)
|
|
33
|
+
else:
|
|
34
|
+
status = click.style(result.status.name, fg=Color.red)
|
|
35
|
+
click.echo(f"{status} {result.title}")
|
|
36
|
+
|
|
37
|
+
if result.reason:
|
|
38
|
+
click.echo(f" {result.reason}")
|
|
39
|
+
if verbose and result.data:
|
|
40
|
+
click.echo(f" {result.data}")
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
DOC_URL = "https://scim2-cli.readthedocs.io/"
|
|
5
|
+
INDENTATION_SIZE = 4
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Color(str, Enum):
|
|
9
|
+
black = "black"
|
|
10
|
+
red = "red"
|
|
11
|
+
green = "green"
|
|
12
|
+
yellow = "yellow"
|
|
13
|
+
blue = "blue"
|
|
14
|
+
magenta = "magenta"
|
|
15
|
+
cyan = "cyan"
|
|
16
|
+
white = "white"
|
|
17
|
+
bright_black = "bright_black"
|
|
18
|
+
bright_red = "bright_red"
|
|
19
|
+
bright_green = "bright_green"
|
|
20
|
+
bright_yellow = "bright_yellow"
|
|
21
|
+
bright_blue = "bright_blue"
|
|
22
|
+
bright_magenta = "bright_magenta"
|
|
23
|
+
bright_cyan = "bright_cyan"
|
|
24
|
+
bright_white = "bright_white"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def formatted_payload(obj, indent):
|
|
28
|
+
indent = INDENTATION_SIZE if indent else False
|
|
29
|
+
return json.dumps(obj, indent=indent)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def split_headers(headers):
|
|
33
|
+
return {
|
|
34
|
+
header[: header.index(":")].strip(): header[header.index(":") + 1 :].strip()
|
|
35
|
+
for header in headers
|
|
36
|
+
}
|