scim2-cli 0.1.0__tar.gz → 0.1.1__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/PKG-INFO +81 -0
- scim2_cli-0.1.1/README.md +54 -0
- {scim2_cli-0.1.0 → scim2_cli-0.1.1}/pyproject.toml +2 -2
- scim2_cli-0.1.1/scim2_cli/__init__.py +49 -0
- scim2_cli-0.1.1/scim2_cli/create.py +48 -0
- scim2_cli-0.1.1/scim2_cli/delete.py +45 -0
- scim2_cli-0.1.1/scim2_cli/query.py +132 -0
- scim2_cli-0.1.1/scim2_cli/replace.py +48 -0
- scim2_cli-0.1.1/scim2_cli/search.py +106 -0
- scim2_cli-0.1.1/scim2_cli/utils.py +9 -0
- scim2_cli-0.1.0/PKG-INFO +0 -30
- scim2_cli-0.1.0/README.md +0 -3
- scim2_cli-0.1.0/scim2_cli/__init__.py +0 -129
scim2_cli-0.1.1/PKG-INFO
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: scim2-cli
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: SCIM application development CLI
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: scim,scim2,provisioning
|
|
7
|
+
Author: Yaal Coop
|
|
8
|
+
Author-email: contact@yaal.coop
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
+
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
23
|
+
Requires-Dist: scim2-client (>=0.1.4,<0.2.0)
|
|
24
|
+
Requires-Dist: sphinx-click-rst-to-ansi-formatter (>=0.1.0,<0.2.0)
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# scim2-cli
|
|
28
|
+
|
|
29
|
+
An utility command line to help you perform requests against a SCIM server, while validating input and response payloads.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```shell
|
|
34
|
+
pip install scim2-cli
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Check the [reference](https://scim2-cli.readthedocs.io/en/latest/reference.html) for more details.
|
|
40
|
+
|
|
41
|
+
Here is an example of resource creation:
|
|
42
|
+
|
|
43
|
+
```shell
|
|
44
|
+
$ scim2 https://auth.example create user << EOL
|
|
45
|
+
{
|
|
46
|
+
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
47
|
+
"userName": "bjensen@example.com"
|
|
48
|
+
}
|
|
49
|
+
EOL
|
|
50
|
+
{
|
|
51
|
+
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
52
|
+
"id": "2819c223-7f76-453a-919d-413861904646",
|
|
53
|
+
"userName": "bjensen@example.com",
|
|
54
|
+
"meta": {
|
|
55
|
+
"resourceType": "User",
|
|
56
|
+
"created": "2010-01-23T04:56:22Z",
|
|
57
|
+
"lastModified": "2011-05-13T04:42:34Z",
|
|
58
|
+
"version": 'W\\/"3694e05e9dff590"',
|
|
59
|
+
"location": "https://example.com/v2/Users/2819c223-7f76-453a-919d-413861904646",
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Here is an example of resource query:
|
|
65
|
+
|
|
66
|
+
```shell
|
|
67
|
+
$ scim2 https://auth.example query user 2819c223-7f76-453a-919d-413861904646
|
|
68
|
+
{
|
|
69
|
+
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
70
|
+
"id": "2819c223-7f76-453a-919d-413861904646",
|
|
71
|
+
"userName": "bjensen@example.com",
|
|
72
|
+
"meta": {
|
|
73
|
+
"resourceType": "User",
|
|
74
|
+
"created": "2010-01-23T04:56:22Z",
|
|
75
|
+
"lastModified": "2011-05-13T04:42:34Z",
|
|
76
|
+
"version": 'W\\/"3694e05e9dff590"',
|
|
77
|
+
"location": "https://example.com/v2/Users/2819c223-7f76-453a-919d-413861904646",
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# scim2-cli
|
|
2
|
+
|
|
3
|
+
An utility command line to help you perform requests against a SCIM server, while validating input and response payloads.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```shell
|
|
8
|
+
pip install scim2-cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Check the [reference](https://scim2-cli.readthedocs.io/en/latest/reference.html) for more details.
|
|
14
|
+
|
|
15
|
+
Here is an example of resource creation:
|
|
16
|
+
|
|
17
|
+
```shell
|
|
18
|
+
$ scim2 https://auth.example create user << EOL
|
|
19
|
+
{
|
|
20
|
+
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
21
|
+
"userName": "bjensen@example.com"
|
|
22
|
+
}
|
|
23
|
+
EOL
|
|
24
|
+
{
|
|
25
|
+
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
26
|
+
"id": "2819c223-7f76-453a-919d-413861904646",
|
|
27
|
+
"userName": "bjensen@example.com",
|
|
28
|
+
"meta": {
|
|
29
|
+
"resourceType": "User",
|
|
30
|
+
"created": "2010-01-23T04:56:22Z",
|
|
31
|
+
"lastModified": "2011-05-13T04:42:34Z",
|
|
32
|
+
"version": 'W\\/"3694e05e9dff590"',
|
|
33
|
+
"location": "https://example.com/v2/Users/2819c223-7f76-453a-919d-413861904646",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Here is an example of resource query:
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
$ scim2 https://auth.example query user 2819c223-7f76-453a-919d-413861904646
|
|
42
|
+
{
|
|
43
|
+
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
|
44
|
+
"id": "2819c223-7f76-453a-919d-413861904646",
|
|
45
|
+
"userName": "bjensen@example.com",
|
|
46
|
+
"meta": {
|
|
47
|
+
"resourceType": "User",
|
|
48
|
+
"created": "2010-01-23T04:56:22Z",
|
|
49
|
+
"lastModified": "2011-05-13T04:42:34Z",
|
|
50
|
+
"version": 'W\\/"3694e05e9dff590"',
|
|
51
|
+
"location": "https://example.com/v2/Users/2819c223-7f76-453a-919d-413861904646",
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
@@ -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.1"
|
|
8
8
|
description = "SCIM application development CLI"
|
|
9
9
|
authors = ["Yaal Coop <contact@yaal.coop>"]
|
|
10
10
|
license = "MIT"
|
|
@@ -27,7 +27,7 @@ classifiers = [
|
|
|
27
27
|
[tool.poetry.dependencies]
|
|
28
28
|
python = "^3.10"
|
|
29
29
|
click = "^8.1.7"
|
|
30
|
-
|
|
30
|
+
scim2-client = "^0.1.4"
|
|
31
31
|
sphinx-click-rst-to-ansi-formatter = "^0.1.0"
|
|
32
32
|
|
|
33
33
|
[tool.poetry.group.doc]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from httpx import Client
|
|
5
|
+
from scim2_client import SCIMClient
|
|
6
|
+
from scim2_models import EnterpriseUser
|
|
7
|
+
from scim2_models import Group
|
|
8
|
+
from scim2_models import User
|
|
9
|
+
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
10
|
+
|
|
11
|
+
from .create import create_cli
|
|
12
|
+
from .delete import delete_cli
|
|
13
|
+
from .query import query_cli
|
|
14
|
+
from .replace import replace_cli
|
|
15
|
+
from .search import search_cli
|
|
16
|
+
from .utils import DOC_URL
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@click.group(cls=make_rst_to_ansi_formatter(DOC_URL, group=True))
|
|
20
|
+
@click.argument("url")
|
|
21
|
+
@click.pass_context
|
|
22
|
+
def cli(ctx, url):
|
|
23
|
+
"""SCIM application development CLI."""
|
|
24
|
+
|
|
25
|
+
ctx.ensure_object(dict)
|
|
26
|
+
ctx.obj["URL"] = url
|
|
27
|
+
client = Client(base_url=ctx.obj["URL"])
|
|
28
|
+
ctx.obj["client"] = SCIMClient(
|
|
29
|
+
client, resource_types=(User, User[EnterpriseUser], Group)
|
|
30
|
+
)
|
|
31
|
+
ctx.obj["resource_types"] = {
|
|
32
|
+
resource_type.__name__.lower(): resource_type
|
|
33
|
+
for resource_type in ctx.obj["client"].resource_types
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if not click.get_text_stream("stdin").isatty(): # pragma: no cover
|
|
37
|
+
if stdin := click.get_text_stream("stdin").read().strip():
|
|
38
|
+
try:
|
|
39
|
+
ctx.obj["stdin"] = json.loads(stdin)
|
|
40
|
+
except json.JSONDecodeError as exc:
|
|
41
|
+
message = f"Invalid JSON input.\n{exc}"
|
|
42
|
+
raise click.ClickException(message) from exc
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
cli.add_command(create_cli)
|
|
46
|
+
cli.add_command(query_cli)
|
|
47
|
+
cli.add_command(replace_cli)
|
|
48
|
+
cli.add_command(delete_cli)
|
|
49
|
+
cli.add_command(search_cli)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import httpx
|
|
3
|
+
from click import ClickException
|
|
4
|
+
from pydantic import ValidationError
|
|
5
|
+
from scim2_client import SCIMClientError
|
|
6
|
+
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
7
|
+
|
|
8
|
+
from .utils import DOC_URL
|
|
9
|
+
from .utils import formatted_payload
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="create")
|
|
13
|
+
@click.pass_context
|
|
14
|
+
@click.option(
|
|
15
|
+
"--indent/--no-indent",
|
|
16
|
+
is_flag=True,
|
|
17
|
+
default=True,
|
|
18
|
+
help="Indent JSON response payloads.",
|
|
19
|
+
)
|
|
20
|
+
def create_cli(ctx, indent):
|
|
21
|
+
"""Perform a `SCIM POST <https://www.rfc-editor.org/rfc/rfc7644#section-3.3>`_ request
|
|
22
|
+
on resources endpoint.
|
|
23
|
+
|
|
24
|
+
Input data is expected to be passed in JSON format to stdin:
|
|
25
|
+
|
|
26
|
+
.. code-block:: bash
|
|
27
|
+
|
|
28
|
+
echo '{"userName": "bjensen@example.com", "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"]}' | scim https://scim.example create user
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
payload = ctx.obj.get("stdin")
|
|
33
|
+
if not payload:
|
|
34
|
+
raise ClickException("Input data is missing")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
response = ctx.obj["client"].create(payload)
|
|
38
|
+
|
|
39
|
+
except (httpx.HTTPError, SCIMClientError) as exc:
|
|
40
|
+
raise ClickException(exc) from exc
|
|
41
|
+
|
|
42
|
+
except ValidationError as exc:
|
|
43
|
+
payload = formatted_payload(exc.response_payload, indent)
|
|
44
|
+
message = f"The server response is invalid:\n{payload}\n{exc}"
|
|
45
|
+
raise ClickException(message) from exc
|
|
46
|
+
|
|
47
|
+
payload = formatted_payload(response.model_dump(), indent)
|
|
48
|
+
click.echo(payload)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import httpx
|
|
3
|
+
from click import ClickException
|
|
4
|
+
from scim2_client import SCIMClientError
|
|
5
|
+
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
6
|
+
|
|
7
|
+
from .utils import DOC_URL
|
|
8
|
+
from .utils import formatted_payload
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="delete")
|
|
12
|
+
@click.argument("resource-type", required=True)
|
|
13
|
+
@click.argument("id", required=True)
|
|
14
|
+
@click.option(
|
|
15
|
+
"--indent/--no-indent",
|
|
16
|
+
is_flag=True,
|
|
17
|
+
default=True,
|
|
18
|
+
help="Indent JSON response payloads.",
|
|
19
|
+
)
|
|
20
|
+
@click.pass_context
|
|
21
|
+
def delete_cli(ctx, resource_type, id, indent):
|
|
22
|
+
"""Perform a `SCIM DELETE query <https://www.rfc-editor.org/rfc/rfc7644#section-3.6>`_ request.
|
|
23
|
+
|
|
24
|
+
.. code-block:: bash
|
|
25
|
+
|
|
26
|
+
scim https://scim.example delete user 1234
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
resource_type = ctx.obj["resource_types"][resource_type]
|
|
31
|
+
except KeyError:
|
|
32
|
+
ok_values = ", ".join(ctx.obj["resource_types"])
|
|
33
|
+
raise ClickException(
|
|
34
|
+
f"Unknown resource type '{resource_type}'. Available values are: {ok_values}'"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
try:
|
|
38
|
+
response = ctx.obj["client"].delete(resource_type, id)
|
|
39
|
+
|
|
40
|
+
except (httpx.HTTPError, SCIMClientError) as exc:
|
|
41
|
+
raise ClickException(exc) from exc
|
|
42
|
+
|
|
43
|
+
if response:
|
|
44
|
+
payload = formatted_payload(response.model_dump(), indent)
|
|
45
|
+
click.echo(payload)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
import httpx
|
|
6
|
+
from click import ClickException
|
|
7
|
+
from pydantic import ValidationError
|
|
8
|
+
from scim2_client import SCIMClientError
|
|
9
|
+
from scim2_models import SearchRequest
|
|
10
|
+
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
11
|
+
|
|
12
|
+
from .utils import DOC_URL
|
|
13
|
+
from .utils import formatted_payload
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="query")
|
|
17
|
+
@click.pass_context
|
|
18
|
+
@click.argument("resource_type", required=False)
|
|
19
|
+
@click.argument("id", required=False)
|
|
20
|
+
@click.option(
|
|
21
|
+
"--attribute",
|
|
22
|
+
multiple=True,
|
|
23
|
+
help="A multi-valued list of strings indicating the names of resource attributes to return in the response, overriding the set of attributes that would be returned by default.",
|
|
24
|
+
)
|
|
25
|
+
@click.option(
|
|
26
|
+
"--excluded-attribute",
|
|
27
|
+
multiple=True,
|
|
28
|
+
help="A multi-valued list of strings indicating the names of resource attributes to be removed from the default set of attributes to return.",
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--start-index",
|
|
32
|
+
type=int,
|
|
33
|
+
help="An integer indicating the 1-based index of the first query result.",
|
|
34
|
+
)
|
|
35
|
+
@click.option(
|
|
36
|
+
"--count",
|
|
37
|
+
type=int,
|
|
38
|
+
help="An integer indicating the desired maximum number of query results per page.",
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--filter", help="The filter string used to request a subset of resources."
|
|
42
|
+
)
|
|
43
|
+
@click.option(
|
|
44
|
+
"--sort-by",
|
|
45
|
+
help="A string indicating the attribute whose value SHALL be used to order the returned responses.",
|
|
46
|
+
)
|
|
47
|
+
@click.option(
|
|
48
|
+
"--sort-order",
|
|
49
|
+
help="A string indicating the order in which the “sortBy” parameter is applied.",
|
|
50
|
+
)
|
|
51
|
+
@click.option(
|
|
52
|
+
"--indent/--no-indent",
|
|
53
|
+
is_flag=True,
|
|
54
|
+
default=True,
|
|
55
|
+
help="Indent JSON response payloads.",
|
|
56
|
+
)
|
|
57
|
+
def query_cli(
|
|
58
|
+
ctx,
|
|
59
|
+
resource_type: Optional[str],
|
|
60
|
+
id: Optional[str],
|
|
61
|
+
attribute: List[str],
|
|
62
|
+
excluded_attribute: List[str],
|
|
63
|
+
start_index: int,
|
|
64
|
+
count: int,
|
|
65
|
+
filter: str,
|
|
66
|
+
sort_by: str,
|
|
67
|
+
sort_order: str,
|
|
68
|
+
indent: bool,
|
|
69
|
+
):
|
|
70
|
+
"""Perform a `SCIM GET <https://www.rfc-editor.org/rfc/rfc7644#section-3.4.1>`_ request
|
|
71
|
+
on the :code:`RESOURCE_TYPE` endpoint.
|
|
72
|
+
|
|
73
|
+
- If :code:`RESOURCE_TYPE` is :code:`user` and :code:`id` is `1234`, then the request will made on the :code:`/Users/1234` endpoint.
|
|
74
|
+
- If :code:`RESOURCE_TYPE` is :code:`user` and :code:`id` is not set, then the request will made on the :code:`/Users` endpoint.
|
|
75
|
+
- If :code:`RESOURCE_TYPE` is not set, then the request will made on the :code:`/` endpoint.
|
|
76
|
+
|
|
77
|
+
Data passed in JSON format to stdin is sent as request arguments and all the other query arguments are ignored:
|
|
78
|
+
|
|
79
|
+
.. code-block:: bash
|
|
80
|
+
|
|
81
|
+
echo '{"startIndex": 50, "count": 10}' | scim https://scim.example query user
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
if resource_type:
|
|
85
|
+
try:
|
|
86
|
+
resource_type = ctx.obj["resource_types"][resource_type]
|
|
87
|
+
except KeyError:
|
|
88
|
+
ok_values = ", ".join(ctx.obj["resource_types"])
|
|
89
|
+
raise ClickException(
|
|
90
|
+
f"Unknown resource type '{resource_type}. Available values are: {ok_values}'"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
if ctx.obj.get("stdin"):
|
|
94
|
+
check_request_payload = False
|
|
95
|
+
payload = ctx.obj.get("stdin")
|
|
96
|
+
|
|
97
|
+
else:
|
|
98
|
+
check_request_payload = True
|
|
99
|
+
payload = SearchRequest(
|
|
100
|
+
attributes=attribute,
|
|
101
|
+
excluded_attributes=excluded_attribute,
|
|
102
|
+
start_index=start_index,
|
|
103
|
+
count=count,
|
|
104
|
+
filter=filter,
|
|
105
|
+
sort_by=sort_by,
|
|
106
|
+
sort_order=sort_order,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
if resource_type:
|
|
111
|
+
response = ctx.obj["client"].query(
|
|
112
|
+
resource_type,
|
|
113
|
+
id,
|
|
114
|
+
search_request=payload,
|
|
115
|
+
check_request_payload=check_request_payload,
|
|
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
|
|
125
|
+
|
|
126
|
+
except ValidationError as exc:
|
|
127
|
+
payload = formatted_payload(exc.response_payload, indent)
|
|
128
|
+
message = f"The server response is invalid:\n{payload}\n{exc}"
|
|
129
|
+
raise ClickException(message) from exc
|
|
130
|
+
|
|
131
|
+
payload = formatted_payload(response.model_dump(), indent)
|
|
132
|
+
click.echo(payload)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import httpx
|
|
3
|
+
from click import ClickException
|
|
4
|
+
from pydantic import ValidationError
|
|
5
|
+
from scim2_client import SCIMClientError
|
|
6
|
+
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
7
|
+
|
|
8
|
+
from .utils import DOC_URL
|
|
9
|
+
from .utils import formatted_payload
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="replace")
|
|
13
|
+
@click.pass_context
|
|
14
|
+
@click.option(
|
|
15
|
+
"--indent/--no-indent",
|
|
16
|
+
is_flag=True,
|
|
17
|
+
default=True,
|
|
18
|
+
help="Indent JSON response payloads.",
|
|
19
|
+
)
|
|
20
|
+
def replace_cli(ctx, indent):
|
|
21
|
+
"""Perform a `SCIM PUT <https://www.rfc-editor.org/rfc/rfc7644#section-3.5.1>`_ request
|
|
22
|
+
on the resources endpoint.
|
|
23
|
+
|
|
24
|
+
Input data is expected to be passed in JSON format to stdin:
|
|
25
|
+
|
|
26
|
+
.. code-block:: bash
|
|
27
|
+
|
|
28
|
+
echo '{"userName": "bjensen@example.com", "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "id": "1234"}' | scim https://scim.example replace user
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
payload = ctx.obj.get("stdin")
|
|
33
|
+
if not payload:
|
|
34
|
+
raise ClickException("Input data is missing")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
response = ctx.obj["client"].replace(payload)
|
|
38
|
+
|
|
39
|
+
except (httpx.HTTPError, SCIMClientError) as exc:
|
|
40
|
+
raise ClickException(exc) from exc
|
|
41
|
+
|
|
42
|
+
except ValidationError as exc:
|
|
43
|
+
payload = formatted_payload(exc.response_payload, indent)
|
|
44
|
+
message = f"The server response is invalid:\n{payload}\n{exc}"
|
|
45
|
+
raise ClickException(message) from exc
|
|
46
|
+
|
|
47
|
+
payload = formatted_payload(response.model_dump(), indent)
|
|
48
|
+
click.echo(payload)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import httpx
|
|
5
|
+
from click import ClickException
|
|
6
|
+
from pydantic import ValidationError
|
|
7
|
+
from scim2_client import SCIMClientError
|
|
8
|
+
from scim2_models import SearchRequest
|
|
9
|
+
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
10
|
+
|
|
11
|
+
from .utils import DOC_URL
|
|
12
|
+
from .utils import formatted_payload
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command(cls=make_rst_to_ansi_formatter(DOC_URL), name="search")
|
|
16
|
+
@click.pass_context
|
|
17
|
+
@click.option(
|
|
18
|
+
"--attribute",
|
|
19
|
+
multiple=True,
|
|
20
|
+
help="A multi-valued list of strings indicating the names of resource attributes to return in the response, overriding the set of attributes that would be returned by default.",
|
|
21
|
+
)
|
|
22
|
+
@click.option(
|
|
23
|
+
"--excluded-attribute",
|
|
24
|
+
multiple=True,
|
|
25
|
+
help="A multi-valued list of strings indicating the names of resource attributes to be removed from the default set of attributes to return.",
|
|
26
|
+
)
|
|
27
|
+
@click.option(
|
|
28
|
+
"--start-index",
|
|
29
|
+
type=int,
|
|
30
|
+
help="An integer indicating the 1-based index of the first query result.",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--count",
|
|
34
|
+
type=int,
|
|
35
|
+
help="An integer indicating the desired maximum number of query results per page.",
|
|
36
|
+
)
|
|
37
|
+
@click.option(
|
|
38
|
+
"--filter", help="The filter string used to request a subset of resources."
|
|
39
|
+
)
|
|
40
|
+
@click.option(
|
|
41
|
+
"--sort-by",
|
|
42
|
+
help="A string indicating the attribute whose value SHALL be used to order the returned responses.",
|
|
43
|
+
)
|
|
44
|
+
@click.option(
|
|
45
|
+
"--sort-order",
|
|
46
|
+
help="A string indicating the order in which the “sortBy” parameter is applied.",
|
|
47
|
+
)
|
|
48
|
+
@click.option(
|
|
49
|
+
"--indent/--no-indent",
|
|
50
|
+
is_flag=True,
|
|
51
|
+
default=True,
|
|
52
|
+
help="Indent JSON response payloads.",
|
|
53
|
+
)
|
|
54
|
+
def search_cli(
|
|
55
|
+
ctx,
|
|
56
|
+
attribute: List[str],
|
|
57
|
+
excluded_attribute: List[str],
|
|
58
|
+
start_index: int,
|
|
59
|
+
count: int,
|
|
60
|
+
filter: str,
|
|
61
|
+
sort_by: str,
|
|
62
|
+
sort_order: str,
|
|
63
|
+
indent: bool,
|
|
64
|
+
):
|
|
65
|
+
"""Perform a `SCIM GET <https://www.rfc-editor.org/rfc/rfc7644#section-3.4.1>`_ request
|
|
66
|
+
on the :code:`/.search` endpoint.
|
|
67
|
+
|
|
68
|
+
Data passed in JSON format to stdin is sent as request arguments and all the other query arguments are ignored:
|
|
69
|
+
|
|
70
|
+
.. code-block:: bash
|
|
71
|
+
|
|
72
|
+
echo '{"startIndex": 50, "count": 10}' | scim https://scim.example search user
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
if ctx.obj.get("stdin"):
|
|
76
|
+
check_request_payload = False
|
|
77
|
+
payload = ctx.obj.get("stdin")
|
|
78
|
+
|
|
79
|
+
else:
|
|
80
|
+
check_request_payload = True
|
|
81
|
+
payload = SearchRequest(
|
|
82
|
+
attributes=attribute,
|
|
83
|
+
excluded_attributes=excluded_attribute,
|
|
84
|
+
start_index=start_index,
|
|
85
|
+
count=count,
|
|
86
|
+
filter=filter,
|
|
87
|
+
sort_by=sort_by,
|
|
88
|
+
sort_order=sort_order,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
response = ctx.obj["client"].search(
|
|
93
|
+
search_request=payload,
|
|
94
|
+
check_request_payload=check_request_payload,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
except (httpx.HTTPError, SCIMClientError) as exc:
|
|
98
|
+
raise ClickException(exc) from exc
|
|
99
|
+
|
|
100
|
+
except ValidationError as exc:
|
|
101
|
+
payload = formatted_payload(exc.response_payload, indent)
|
|
102
|
+
message = f"The server response is invalid:\n{payload}\n{exc}"
|
|
103
|
+
raise ClickException(message) from exc
|
|
104
|
+
|
|
105
|
+
payload = formatted_payload(response.model_dump(), indent)
|
|
106
|
+
click.echo(payload)
|
scim2_cli-0.1.0/PKG-INFO
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: scim2-cli
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: SCIM application development CLI
|
|
5
|
-
License: MIT
|
|
6
|
-
Keywords: scim,scim2,provisioning
|
|
7
|
-
Author: Yaal Coop
|
|
8
|
-
Author-email: contact@yaal.coop
|
|
9
|
-
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Environment :: Web Environment
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Operating System :: OS Independent
|
|
15
|
-
Classifier: Programming Language :: Python
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
-
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
23
|
-
Requires-Dist: requests (>=2.32.2,<3.0.0)
|
|
24
|
-
Requires-Dist: sphinx-click-rst-to-ansi-formatter (>=0.1.0,<0.2.0)
|
|
25
|
-
Description-Content-Type: text/markdown
|
|
26
|
-
|
|
27
|
-
# scim2-cli
|
|
28
|
-
|
|
29
|
-
A scim utility command line.
|
|
30
|
-
|
scim2_cli-0.1.0/README.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
import click
|
|
4
|
-
import requests
|
|
5
|
-
from sphinx_click.rst_to_ansi_formatter import make_rst_to_ansi_formatter
|
|
6
|
-
|
|
7
|
-
DOC_URL = "https://scim2-cli.readthedocs.io/"
|
|
8
|
-
BASE_HEADERS = {
|
|
9
|
-
"Accept": "application/scim+json",
|
|
10
|
-
"Content-Type": "application/scim+json",
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@click.group(cls=make_rst_to_ansi_formatter(DOC_URL, group=True))
|
|
15
|
-
@click.argument("url")
|
|
16
|
-
@click.pass_context
|
|
17
|
-
def cli(ctx, url):
|
|
18
|
-
"""SCIM application development CLI."""
|
|
19
|
-
ctx.ensure_object(dict)
|
|
20
|
-
ctx.obj["URL"] = url
|
|
21
|
-
|
|
22
|
-
if not click.get_text_stream("stdin").isatty(): # pragma: no cover
|
|
23
|
-
stdin = click.get_text_stream("stdin").read().strip()
|
|
24
|
-
ctx.obj["STDIN"] = json.loads(stdin)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@cli.command(cls=make_rst_to_ansi_formatter(DOC_URL))
|
|
28
|
-
@click.pass_context
|
|
29
|
-
def get(ctx):
|
|
30
|
-
"""Perform a `SCIM GET <https://www.rfc-editor.org/rfc/rfc7644#section-3.4.1>`_ request.
|
|
31
|
-
|
|
32
|
-
Data passed in JSON format to stdin is sent as request arguments:
|
|
33
|
-
|
|
34
|
-
.. code-block:: bash
|
|
35
|
-
|
|
36
|
-
echo '{"foo": "bar"}' | scim https://scim.example get
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
response = requests.get(
|
|
40
|
-
ctx.obj["URL"],
|
|
41
|
-
params=ctx.obj.get("STDIN"),
|
|
42
|
-
headers=BASE_HEADERS,
|
|
43
|
-
allow_redirects=True,
|
|
44
|
-
)
|
|
45
|
-
click.echo(response.text)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@cli.command(cls=make_rst_to_ansi_formatter(DOC_URL))
|
|
49
|
-
@click.pass_context
|
|
50
|
-
def post(ctx):
|
|
51
|
-
"""Perform a `SCIM POST <https://www.rfc-editor.org/rfc/rfc7644#section-3.3>`_ request.
|
|
52
|
-
|
|
53
|
-
Data passed in JSON format to stdin is sent as request payload:
|
|
54
|
-
|
|
55
|
-
.. code-block:: bash
|
|
56
|
-
|
|
57
|
-
echo '{"foo": "bar"}' | scim https://scim.example post
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
response = requests.post(
|
|
61
|
-
ctx.obj["URL"],
|
|
62
|
-
json=ctx.obj.get("STDIN"),
|
|
63
|
-
headers=BASE_HEADERS,
|
|
64
|
-
allow_redirects=True,
|
|
65
|
-
)
|
|
66
|
-
click.echo(response.text)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@cli.command(cls=make_rst_to_ansi_formatter(DOC_URL))
|
|
70
|
-
@click.pass_context
|
|
71
|
-
def put(ctx):
|
|
72
|
-
"""Perform a `SCIM PUT <https://www.rfc-editor.org/rfc/rfc7644#section-3.5.1>`_ request.
|
|
73
|
-
|
|
74
|
-
Data passed in JSON format to stdin is sent as request payload:
|
|
75
|
-
|
|
76
|
-
.. code-block:: bash
|
|
77
|
-
|
|
78
|
-
echo '{"foo": "bar"}' | scim https://scim.example put
|
|
79
|
-
"""
|
|
80
|
-
|
|
81
|
-
response = requests.put(
|
|
82
|
-
ctx.obj["URL"],
|
|
83
|
-
json=ctx.obj.get("STDIN"),
|
|
84
|
-
headers=BASE_HEADERS,
|
|
85
|
-
allow_redirects=True,
|
|
86
|
-
)
|
|
87
|
-
click.echo(response.text)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@cli.command(cls=make_rst_to_ansi_formatter(DOC_URL))
|
|
91
|
-
@click.pass_context
|
|
92
|
-
def patch(ctx):
|
|
93
|
-
"""Perform a `SCIM PATCH <https://www.rfc-editor.org/rfc/rfc7644#section-3.5.2>`_ request.
|
|
94
|
-
|
|
95
|
-
Data passed in JSON format to stdin is sent as request payload:
|
|
96
|
-
|
|
97
|
-
.. code-block:: bash
|
|
98
|
-
|
|
99
|
-
echo '{"foo": "bar"}' | scim https://scim.example patch
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
response = requests.patch(
|
|
103
|
-
ctx.obj["URL"],
|
|
104
|
-
json=ctx.obj.get("STDIN"),
|
|
105
|
-
headers=BASE_HEADERS,
|
|
106
|
-
allow_redirects=True,
|
|
107
|
-
)
|
|
108
|
-
click.echo(response.text)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
@cli.command(cls=make_rst_to_ansi_formatter(DOC_URL))
|
|
112
|
-
@click.pass_context
|
|
113
|
-
def delete(ctx):
|
|
114
|
-
"""Perform a `SCIM DELETE <https://www.rfc-editor.org/rfc/rfc7644#section-3.6>`_ request.
|
|
115
|
-
|
|
116
|
-
Data passed in JSON format to stdin is sent as request payload:
|
|
117
|
-
|
|
118
|
-
.. code-block:: bash
|
|
119
|
-
|
|
120
|
-
echo '{"foo": "bar"}' | scim https://scim.example delete
|
|
121
|
-
"""
|
|
122
|
-
|
|
123
|
-
response = requests.delete(
|
|
124
|
-
ctx.obj["URL"],
|
|
125
|
-
json=ctx.obj.get("STDIN"),
|
|
126
|
-
headers=BASE_HEADERS,
|
|
127
|
-
allow_redirects=True,
|
|
128
|
-
)
|
|
129
|
-
click.echo(response.text)
|