digiwin-iam-sdk 0.1.0__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.
- digiwin_iam_sdk-0.1.0/LICENSE +21 -0
- digiwin_iam_sdk-0.1.0/PKG-INFO +278 -0
- digiwin_iam_sdk-0.1.0/README.md +248 -0
- digiwin_iam_sdk-0.1.0/pyproject.toml +62 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/__init__.py +43 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/api/__init__.py +17 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/api/_base.py +78 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/api/identity.py +666 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/api/orgs.py +141 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/api/tenants.py +289 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/api/users.py +219 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/client.py +206 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/config.py +38 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/crypto.py +160 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/exceptions.py +38 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/models/__init__.py +44 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/models/common.py +30 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/models/identity.py +136 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/models/org.py +68 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/models/tenant.py +103 -0
- digiwin_iam_sdk-0.1.0/src/dapware_iam/models/user.py +87 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Digiwin Middleware Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: digiwin-iam-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Digiwin IAM (Identity and Access Management) System
|
|
5
|
+
Author: Digiwin Middleware Team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: authentication,dapware,digiwin,iam,identity
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: cryptography>=42.0
|
|
20
|
+
Requires-Dist: httpx>=0.27.0
|
|
21
|
+
Requires-Dist: pydantic>=2.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
28
|
+
Requires-Dist: twine>=6.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# digiwin-iam-sdk
|
|
32
|
+
|
|
33
|
+
[](https://www.python.org/downloads/)
|
|
34
|
+
|
|
35
|
+
Python SDK for the Digiwin IAM (Identity and Access Management) system.
|
|
36
|
+
|
|
37
|
+
PyPI distribution name: `digiwin-iam-sdk`
|
|
38
|
+
|
|
39
|
+
Import namespace: `dapware_iam`
|
|
40
|
+
|
|
41
|
+
## Features
|
|
42
|
+
|
|
43
|
+
- Identity, user, tenant, and org API modules
|
|
44
|
+
- Transparent RSA + AES login handshake
|
|
45
|
+
- Synchronous and asynchronous clients
|
|
46
|
+
- Pydantic-based request and response models
|
|
47
|
+
- Typed exceptions for auth, validation, server, and crypto failures
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
Install from PyPI:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install digiwin-iam-sdk
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Install from source for development:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install -e ".[dev]"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
### Synchronous client
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from dapware_iam import IAMClient
|
|
69
|
+
|
|
70
|
+
with IAMClient(
|
|
71
|
+
base_url="https://iam-test.digiwincloud.com.cn",
|
|
72
|
+
app_token="your-app-token",
|
|
73
|
+
) as client:
|
|
74
|
+
user = client.identity.login("your_user_id", "your_password")
|
|
75
|
+
print(user.token)
|
|
76
|
+
|
|
77
|
+
token_info = client.identity.analyze_token()
|
|
78
|
+
print(token_info.user_id)
|
|
79
|
+
|
|
80
|
+
current_user = client.users.get_user(user_id=token_info.user_id)
|
|
81
|
+
tenants = client.tenants.get_tenants()
|
|
82
|
+
|
|
83
|
+
print(current_user.user_id)
|
|
84
|
+
print([tenant.id for tenant in tenants])
|
|
85
|
+
|
|
86
|
+
client.identity.logout()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Asynchronous client
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import asyncio
|
|
93
|
+
|
|
94
|
+
from dapware_iam import AsyncIAMClient
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def main() -> None:
|
|
98
|
+
async with AsyncIAMClient(
|
|
99
|
+
base_url="https://iam-test.digiwincloud.com.cn",
|
|
100
|
+
app_token="your-app-token",
|
|
101
|
+
) as client:
|
|
102
|
+
user = await client.identity.login("your_user_id", "your_password")
|
|
103
|
+
print(user.token)
|
|
104
|
+
|
|
105
|
+
token_info = await client.identity.analyze_token()
|
|
106
|
+
current_user = await client.users.get_user(user_id=token_info.user_id)
|
|
107
|
+
tenants = await client.tenants.get_tenants()
|
|
108
|
+
|
|
109
|
+
print(current_user.user_id)
|
|
110
|
+
print([tenant.id for tenant in tenants])
|
|
111
|
+
|
|
112
|
+
await client.identity.logout()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
asyncio.run(main())
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Tenant-Scoped APIs
|
|
119
|
+
|
|
120
|
+
Some endpoints require a tenant-scoped token. In the environments validated so
|
|
121
|
+
far, the default login token can be used for:
|
|
122
|
+
|
|
123
|
+
- `identity.analyze_token()`
|
|
124
|
+
- `users.get_user(...)`
|
|
125
|
+
- `tenants.get_tenants(...)`
|
|
126
|
+
|
|
127
|
+
The following methods should be treated as tenant-scoped and called only after
|
|
128
|
+
`identity.refresh_token_with_tenant(...)` succeeds:
|
|
129
|
+
|
|
130
|
+
- `tenants.get_current()`
|
|
131
|
+
- `tenants.get_current_simple()`
|
|
132
|
+
- `tenants.get_applications(...)`
|
|
133
|
+
- `orgs.get_cascade()`
|
|
134
|
+
- `orgs.get_org_list(...)`
|
|
135
|
+
- `orgs.get_users_in_org(...)`
|
|
136
|
+
- `orgs.get_users_cascade(...)`
|
|
137
|
+
- `orgs.create_or_update(...)`
|
|
138
|
+
- `orgs.delete(...)`
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
with IAMClient(base_url="...", app_token="...") as client:
|
|
144
|
+
client.identity.login("user", "password")
|
|
145
|
+
client.identity.refresh_token_with_tenant(tenant_id="your-tenant")
|
|
146
|
+
|
|
147
|
+
tenant = client.tenants.get_current()
|
|
148
|
+
org_tree = client.orgs.get_cascade()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
If the target tenant has not purchased the current application, the backend may
|
|
152
|
+
reject the tenant switch with an authorization error.
|
|
153
|
+
|
|
154
|
+
## API Overview
|
|
155
|
+
|
|
156
|
+
### `IAMClient` and `AsyncIAMClient`
|
|
157
|
+
|
|
158
|
+
| Parameter | Type | Default | Description |
|
|
159
|
+
|---|---|---|---|
|
|
160
|
+
| `base_url` | `str` | - | IAM service base URL |
|
|
161
|
+
| `app_token` | `str` | - | `digi-middleware-auth-app` JWT |
|
|
162
|
+
| `timeout` | `float` | `30.0` | Request timeout in seconds |
|
|
163
|
+
| `verify_ssl` | `bool` | `True` | Whether to verify SSL certificates |
|
|
164
|
+
| `extra_headers` | `dict[str, str] \| None` | `None` | Extra headers added to every request |
|
|
165
|
+
|
|
166
|
+
### `client.identity`
|
|
167
|
+
|
|
168
|
+
| Method | Endpoint | Description |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| `login(user_id, password, ...)` | `POST /login` | Cloud user login |
|
|
171
|
+
| `login_ad(user_id, password, ...)` | `POST /login` | Digiwin AD login |
|
|
172
|
+
| `login_verification_code(phone, code)` | `POST /login` | SMS code login |
|
|
173
|
+
| `internal_login(tenant_id, user_id, password, ...)` | `POST /internal/login` | Enterprise internal login |
|
|
174
|
+
| `logout(clear_all=False)` | `POST /logout` | Logout |
|
|
175
|
+
| `get_public_key()` | `GET /publickey` | Get the server RSA public key |
|
|
176
|
+
| `get_aes_key(encrypted_key)` | `POST /aeskey` | Exchange AES key |
|
|
177
|
+
| `analyze_token()` | `POST /token/analyze` | Parse the current token |
|
|
178
|
+
| `analyze_token_internal()` | `GET /token/analyze/internal` | Parse token with the internal variant |
|
|
179
|
+
| `get_login_info()` | `POST /login/info` | Get login info for the current token |
|
|
180
|
+
| `refresh_token_with_tenant(...)` | `POST /token/refresh/tenant` | Switch tenant and refresh token |
|
|
181
|
+
| `refresh_user_token()` | `POST /token/refresh/user` | Refresh user token |
|
|
182
|
+
| `create_access_token(...)` | `POST /token/grant/access` | Create an SSO access token |
|
|
183
|
+
|
|
184
|
+
### `client.users`
|
|
185
|
+
|
|
186
|
+
| Method | Endpoint | Description |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| `get_user(user_id=None, user_sid=0)` | `POST /api/iam/v2/user` | Query one user |
|
|
189
|
+
| `get_user_list(user_ids)` | `POST /user/list` | Batch query users in the current tenant |
|
|
190
|
+
| `query_users(query_type, ...)` | `POST /user/query` | Query users by org or status |
|
|
191
|
+
| `get_user_simple()` | `GET /user/simple` | Query current-tenant simple users |
|
|
192
|
+
| `check_email_exists(email)` | `POST /user/email/exist` | Check whether email is registered |
|
|
193
|
+
| `check_phone_exists(phone)` | `POST /user/mobilephone/exist` | Check whether phone is registered |
|
|
194
|
+
| `check_user_exists(user_id)` | `POST /user/exists` | Check whether a user exists |
|
|
195
|
+
| `get_user_roles(...)` | `POST /user/role` | Query user roles |
|
|
196
|
+
|
|
197
|
+
### `client.tenants`
|
|
198
|
+
|
|
199
|
+
| Method | Endpoint | Description |
|
|
200
|
+
|---|---|---|
|
|
201
|
+
| `get_tenants(...)` | `POST /api/iam/v2/tenant` | List the current user's available tenants |
|
|
202
|
+
| `get_current()` | `POST /tenant/current` | Get current tenant detail |
|
|
203
|
+
| `get_simple(tenant_id)` | `POST /tenant/simple` | Get one tenant's simple info |
|
|
204
|
+
| `get_current_simple()` | `GET /tenant/current/simple` | Get current tenant simple info |
|
|
205
|
+
| `query_tenants(keyword)` | `POST /tenant/query` | Search open tenants |
|
|
206
|
+
| `check_user_in_tenant(user_id)` | `GET /tenant/check/userintenant` | Check whether a user exists in the current tenant |
|
|
207
|
+
| `get_applications(...)` | `GET /tenant/application` | Get current tenant applications |
|
|
208
|
+
| `invite_user(...)` | `POST /tenant/user/invite/new` | Invite a user into a tenant |
|
|
209
|
+
| `remove_user(...)` | `POST /tenant/remove/user` | Remove a user from a tenant |
|
|
210
|
+
|
|
211
|
+
### `client.orgs`
|
|
212
|
+
|
|
213
|
+
| Method | Endpoint | Description |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| `get_cascade()` | `GET /org/cascade` | Get org tree |
|
|
216
|
+
| `get_org_list(org_sids)` | `POST /org/list` | Batch query orgs by sid |
|
|
217
|
+
| `get_users_in_org(...)` | `POST /org/userinorg` | Query users in one org |
|
|
218
|
+
| `get_users_cascade(...)` | `POST /org/userinorg/cascade` | Query users in an org subtree |
|
|
219
|
+
| `create_or_update(org_info)` | `POST /org/update` | Create or update an org |
|
|
220
|
+
| `delete(org_sid)` | `POST /org/del` | Delete an org |
|
|
221
|
+
|
|
222
|
+
## Error Handling
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from dapware_iam import AuthenticationError, IAMClient, ServerError
|
|
226
|
+
|
|
227
|
+
with IAMClient(base_url="...", app_token="...") as client:
|
|
228
|
+
try:
|
|
229
|
+
client.identity.login("user", "wrong_password")
|
|
230
|
+
except AuthenticationError as exc:
|
|
231
|
+
print(exc)
|
|
232
|
+
print(exc.status_code)
|
|
233
|
+
print(exc.body)
|
|
234
|
+
except ServerError as exc:
|
|
235
|
+
print(exc)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Exception hierarchy:
|
|
239
|
+
|
|
240
|
+
```text
|
|
241
|
+
IAMError
|
|
242
|
+
|- AuthenticationError
|
|
243
|
+
|- AuthorizationError
|
|
244
|
+
|- NotFoundError
|
|
245
|
+
|- ValidationError
|
|
246
|
+
|- ServerError
|
|
247
|
+
`- CryptoError
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Configuration
|
|
251
|
+
|
|
252
|
+
Disable SSL verification only for trusted internal environments that use
|
|
253
|
+
self-signed certificates:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
client = IAMClient(
|
|
257
|
+
base_url="https://internal-iam.company.com",
|
|
258
|
+
app_token="...",
|
|
259
|
+
verify_ssl=False,
|
|
260
|
+
)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Development And Release
|
|
264
|
+
|
|
265
|
+
Manual release instructions live in `RELEASING.md`. The short version is:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
python -m pytest -q
|
|
269
|
+
python -m build
|
|
270
|
+
python -m twine check dist/*
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Upload to TestPyPI first, validate the exact built artifacts, then upload the
|
|
274
|
+
same files to PyPI.
|
|
275
|
+
|
|
276
|
+
## License
|
|
277
|
+
|
|
278
|
+
MIT
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# digiwin-iam-sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.python.org/downloads/)
|
|
4
|
+
|
|
5
|
+
Python SDK for the Digiwin IAM (Identity and Access Management) system.
|
|
6
|
+
|
|
7
|
+
PyPI distribution name: `digiwin-iam-sdk`
|
|
8
|
+
|
|
9
|
+
Import namespace: `dapware_iam`
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Identity, user, tenant, and org API modules
|
|
14
|
+
- Transparent RSA + AES login handshake
|
|
15
|
+
- Synchronous and asynchronous clients
|
|
16
|
+
- Pydantic-based request and response models
|
|
17
|
+
- Typed exceptions for auth, validation, server, and crypto failures
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Install from PyPI:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install digiwin-iam-sdk
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Install from source for development:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install -e ".[dev]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Synchronous client
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from dapware_iam import IAMClient
|
|
39
|
+
|
|
40
|
+
with IAMClient(
|
|
41
|
+
base_url="https://iam-test.digiwincloud.com.cn",
|
|
42
|
+
app_token="your-app-token",
|
|
43
|
+
) as client:
|
|
44
|
+
user = client.identity.login("your_user_id", "your_password")
|
|
45
|
+
print(user.token)
|
|
46
|
+
|
|
47
|
+
token_info = client.identity.analyze_token()
|
|
48
|
+
print(token_info.user_id)
|
|
49
|
+
|
|
50
|
+
current_user = client.users.get_user(user_id=token_info.user_id)
|
|
51
|
+
tenants = client.tenants.get_tenants()
|
|
52
|
+
|
|
53
|
+
print(current_user.user_id)
|
|
54
|
+
print([tenant.id for tenant in tenants])
|
|
55
|
+
|
|
56
|
+
client.identity.logout()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Asynchronous client
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import asyncio
|
|
63
|
+
|
|
64
|
+
from dapware_iam import AsyncIAMClient
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
async def main() -> None:
|
|
68
|
+
async with AsyncIAMClient(
|
|
69
|
+
base_url="https://iam-test.digiwincloud.com.cn",
|
|
70
|
+
app_token="your-app-token",
|
|
71
|
+
) as client:
|
|
72
|
+
user = await client.identity.login("your_user_id", "your_password")
|
|
73
|
+
print(user.token)
|
|
74
|
+
|
|
75
|
+
token_info = await client.identity.analyze_token()
|
|
76
|
+
current_user = await client.users.get_user(user_id=token_info.user_id)
|
|
77
|
+
tenants = await client.tenants.get_tenants()
|
|
78
|
+
|
|
79
|
+
print(current_user.user_id)
|
|
80
|
+
print([tenant.id for tenant in tenants])
|
|
81
|
+
|
|
82
|
+
await client.identity.logout()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
asyncio.run(main())
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Tenant-Scoped APIs
|
|
89
|
+
|
|
90
|
+
Some endpoints require a tenant-scoped token. In the environments validated so
|
|
91
|
+
far, the default login token can be used for:
|
|
92
|
+
|
|
93
|
+
- `identity.analyze_token()`
|
|
94
|
+
- `users.get_user(...)`
|
|
95
|
+
- `tenants.get_tenants(...)`
|
|
96
|
+
|
|
97
|
+
The following methods should be treated as tenant-scoped and called only after
|
|
98
|
+
`identity.refresh_token_with_tenant(...)` succeeds:
|
|
99
|
+
|
|
100
|
+
- `tenants.get_current()`
|
|
101
|
+
- `tenants.get_current_simple()`
|
|
102
|
+
- `tenants.get_applications(...)`
|
|
103
|
+
- `orgs.get_cascade()`
|
|
104
|
+
- `orgs.get_org_list(...)`
|
|
105
|
+
- `orgs.get_users_in_org(...)`
|
|
106
|
+
- `orgs.get_users_cascade(...)`
|
|
107
|
+
- `orgs.create_or_update(...)`
|
|
108
|
+
- `orgs.delete(...)`
|
|
109
|
+
|
|
110
|
+
Example:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
with IAMClient(base_url="...", app_token="...") as client:
|
|
114
|
+
client.identity.login("user", "password")
|
|
115
|
+
client.identity.refresh_token_with_tenant(tenant_id="your-tenant")
|
|
116
|
+
|
|
117
|
+
tenant = client.tenants.get_current()
|
|
118
|
+
org_tree = client.orgs.get_cascade()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
If the target tenant has not purchased the current application, the backend may
|
|
122
|
+
reject the tenant switch with an authorization error.
|
|
123
|
+
|
|
124
|
+
## API Overview
|
|
125
|
+
|
|
126
|
+
### `IAMClient` and `AsyncIAMClient`
|
|
127
|
+
|
|
128
|
+
| Parameter | Type | Default | Description |
|
|
129
|
+
|---|---|---|---|
|
|
130
|
+
| `base_url` | `str` | - | IAM service base URL |
|
|
131
|
+
| `app_token` | `str` | - | `digi-middleware-auth-app` JWT |
|
|
132
|
+
| `timeout` | `float` | `30.0` | Request timeout in seconds |
|
|
133
|
+
| `verify_ssl` | `bool` | `True` | Whether to verify SSL certificates |
|
|
134
|
+
| `extra_headers` | `dict[str, str] \| None` | `None` | Extra headers added to every request |
|
|
135
|
+
|
|
136
|
+
### `client.identity`
|
|
137
|
+
|
|
138
|
+
| Method | Endpoint | Description |
|
|
139
|
+
|---|---|---|
|
|
140
|
+
| `login(user_id, password, ...)` | `POST /login` | Cloud user login |
|
|
141
|
+
| `login_ad(user_id, password, ...)` | `POST /login` | Digiwin AD login |
|
|
142
|
+
| `login_verification_code(phone, code)` | `POST /login` | SMS code login |
|
|
143
|
+
| `internal_login(tenant_id, user_id, password, ...)` | `POST /internal/login` | Enterprise internal login |
|
|
144
|
+
| `logout(clear_all=False)` | `POST /logout` | Logout |
|
|
145
|
+
| `get_public_key()` | `GET /publickey` | Get the server RSA public key |
|
|
146
|
+
| `get_aes_key(encrypted_key)` | `POST /aeskey` | Exchange AES key |
|
|
147
|
+
| `analyze_token()` | `POST /token/analyze` | Parse the current token |
|
|
148
|
+
| `analyze_token_internal()` | `GET /token/analyze/internal` | Parse token with the internal variant |
|
|
149
|
+
| `get_login_info()` | `POST /login/info` | Get login info for the current token |
|
|
150
|
+
| `refresh_token_with_tenant(...)` | `POST /token/refresh/tenant` | Switch tenant and refresh token |
|
|
151
|
+
| `refresh_user_token()` | `POST /token/refresh/user` | Refresh user token |
|
|
152
|
+
| `create_access_token(...)` | `POST /token/grant/access` | Create an SSO access token |
|
|
153
|
+
|
|
154
|
+
### `client.users`
|
|
155
|
+
|
|
156
|
+
| Method | Endpoint | Description |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `get_user(user_id=None, user_sid=0)` | `POST /api/iam/v2/user` | Query one user |
|
|
159
|
+
| `get_user_list(user_ids)` | `POST /user/list` | Batch query users in the current tenant |
|
|
160
|
+
| `query_users(query_type, ...)` | `POST /user/query` | Query users by org or status |
|
|
161
|
+
| `get_user_simple()` | `GET /user/simple` | Query current-tenant simple users |
|
|
162
|
+
| `check_email_exists(email)` | `POST /user/email/exist` | Check whether email is registered |
|
|
163
|
+
| `check_phone_exists(phone)` | `POST /user/mobilephone/exist` | Check whether phone is registered |
|
|
164
|
+
| `check_user_exists(user_id)` | `POST /user/exists` | Check whether a user exists |
|
|
165
|
+
| `get_user_roles(...)` | `POST /user/role` | Query user roles |
|
|
166
|
+
|
|
167
|
+
### `client.tenants`
|
|
168
|
+
|
|
169
|
+
| Method | Endpoint | Description |
|
|
170
|
+
|---|---|---|
|
|
171
|
+
| `get_tenants(...)` | `POST /api/iam/v2/tenant` | List the current user's available tenants |
|
|
172
|
+
| `get_current()` | `POST /tenant/current` | Get current tenant detail |
|
|
173
|
+
| `get_simple(tenant_id)` | `POST /tenant/simple` | Get one tenant's simple info |
|
|
174
|
+
| `get_current_simple()` | `GET /tenant/current/simple` | Get current tenant simple info |
|
|
175
|
+
| `query_tenants(keyword)` | `POST /tenant/query` | Search open tenants |
|
|
176
|
+
| `check_user_in_tenant(user_id)` | `GET /tenant/check/userintenant` | Check whether a user exists in the current tenant |
|
|
177
|
+
| `get_applications(...)` | `GET /tenant/application` | Get current tenant applications |
|
|
178
|
+
| `invite_user(...)` | `POST /tenant/user/invite/new` | Invite a user into a tenant |
|
|
179
|
+
| `remove_user(...)` | `POST /tenant/remove/user` | Remove a user from a tenant |
|
|
180
|
+
|
|
181
|
+
### `client.orgs`
|
|
182
|
+
|
|
183
|
+
| Method | Endpoint | Description |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `get_cascade()` | `GET /org/cascade` | Get org tree |
|
|
186
|
+
| `get_org_list(org_sids)` | `POST /org/list` | Batch query orgs by sid |
|
|
187
|
+
| `get_users_in_org(...)` | `POST /org/userinorg` | Query users in one org |
|
|
188
|
+
| `get_users_cascade(...)` | `POST /org/userinorg/cascade` | Query users in an org subtree |
|
|
189
|
+
| `create_or_update(org_info)` | `POST /org/update` | Create or update an org |
|
|
190
|
+
| `delete(org_sid)` | `POST /org/del` | Delete an org |
|
|
191
|
+
|
|
192
|
+
## Error Handling
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from dapware_iam import AuthenticationError, IAMClient, ServerError
|
|
196
|
+
|
|
197
|
+
with IAMClient(base_url="...", app_token="...") as client:
|
|
198
|
+
try:
|
|
199
|
+
client.identity.login("user", "wrong_password")
|
|
200
|
+
except AuthenticationError as exc:
|
|
201
|
+
print(exc)
|
|
202
|
+
print(exc.status_code)
|
|
203
|
+
print(exc.body)
|
|
204
|
+
except ServerError as exc:
|
|
205
|
+
print(exc)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Exception hierarchy:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
IAMError
|
|
212
|
+
|- AuthenticationError
|
|
213
|
+
|- AuthorizationError
|
|
214
|
+
|- NotFoundError
|
|
215
|
+
|- ValidationError
|
|
216
|
+
|- ServerError
|
|
217
|
+
`- CryptoError
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Configuration
|
|
221
|
+
|
|
222
|
+
Disable SSL verification only for trusted internal environments that use
|
|
223
|
+
self-signed certificates:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
client = IAMClient(
|
|
227
|
+
base_url="https://internal-iam.company.com",
|
|
228
|
+
app_token="...",
|
|
229
|
+
verify_ssl=False,
|
|
230
|
+
)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Development And Release
|
|
234
|
+
|
|
235
|
+
Manual release instructions live in `RELEASING.md`. The short version is:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
python -m pytest -q
|
|
239
|
+
python -m build
|
|
240
|
+
python -m twine check dist/*
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Upload to TestPyPI first, validate the exact built artifacts, then upload the
|
|
244
|
+
same files to PyPI.
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "digiwin-iam-sdk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Python SDK for Digiwin IAM (Identity and Access Management) System"
|
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.10"
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Digiwin Middleware Team" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["iam", "digiwin", "dapware", "identity", "authentication"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"httpx>=0.27.0",
|
|
30
|
+
"pydantic>=2.0",
|
|
31
|
+
"cryptography>=42.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"build>=1.2",
|
|
37
|
+
"pytest>=8.0",
|
|
38
|
+
"pytest-asyncio>=0.24",
|
|
39
|
+
"pytest-httpx>=0.30",
|
|
40
|
+
"ruff>=0.4",
|
|
41
|
+
"twine>=6.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.version]
|
|
45
|
+
path = "src/dapware_iam/__init__.py"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.sdist]
|
|
48
|
+
include = [
|
|
49
|
+
"/src",
|
|
50
|
+
"/README.md",
|
|
51
|
+
"/LICENSE",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.wheel]
|
|
55
|
+
packages = ["src/dapware_iam"]
|
|
56
|
+
|
|
57
|
+
[tool.pytest.ini_options]
|
|
58
|
+
asyncio_mode = "auto"
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
target-version = "py310"
|
|
62
|
+
line-length = 120
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Dapware IAM Python SDK.
|
|
2
|
+
|
|
3
|
+
Provides synchronous and asynchronous Python clients for the Digiwin IAM
|
|
4
|
+
(Identity and Access Management) system.
|
|
5
|
+
|
|
6
|
+
Quick start::
|
|
7
|
+
|
|
8
|
+
from dapware_iam import IAMClient
|
|
9
|
+
|
|
10
|
+
with IAMClient(
|
|
11
|
+
base_url="https://iam-test.digiwincloud.com.cn",
|
|
12
|
+
app_token="your-app-token",
|
|
13
|
+
) as client:
|
|
14
|
+
user = client.identity.login("user_id", "password")
|
|
15
|
+
print(user.token)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .client import AsyncIAMClient, IAMClient
|
|
19
|
+
from .config import IAMConfig
|
|
20
|
+
from .exceptions import (
|
|
21
|
+
AuthenticationError,
|
|
22
|
+
AuthorizationError,
|
|
23
|
+
CryptoError,
|
|
24
|
+
IAMError,
|
|
25
|
+
NotFoundError,
|
|
26
|
+
ServerError,
|
|
27
|
+
ValidationError,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__version__ = "0.1.0"
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"AsyncIAMClient",
|
|
34
|
+
"AuthenticationError",
|
|
35
|
+
"AuthorizationError",
|
|
36
|
+
"CryptoError",
|
|
37
|
+
"IAMClient",
|
|
38
|
+
"IAMConfig",
|
|
39
|
+
"IAMError",
|
|
40
|
+
"NotFoundError",
|
|
41
|
+
"ServerError",
|
|
42
|
+
"ValidationError",
|
|
43
|
+
]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""API sub-package."""
|
|
2
|
+
|
|
3
|
+
from .identity import AsyncIdentityAPI, IdentityAPI
|
|
4
|
+
from .orgs import AsyncOrgsAPI, OrgsAPI
|
|
5
|
+
from .tenants import AsyncTenantsAPI, TenantsAPI
|
|
6
|
+
from .users import AsyncUsersAPI, UsersAPI
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"AsyncIdentityAPI",
|
|
10
|
+
"AsyncOrgsAPI",
|
|
11
|
+
"AsyncTenantsAPI",
|
|
12
|
+
"AsyncUsersAPI",
|
|
13
|
+
"IdentityAPI",
|
|
14
|
+
"OrgsAPI",
|
|
15
|
+
"TenantsAPI",
|
|
16
|
+
"UsersAPI",
|
|
17
|
+
]
|