From 6e269ed3fadf22fccc49d954357e3b2ead5b5296 Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Wed, 17 Jul 2024 16:32:14 +0200 Subject: [PATCH] Add support for namespaces This patch updates the API spec to add support for namespaces. --- .openapi-generator/FILES | 2 + .openapi-generator/VERSION | 2 +- CHANGELOG.md | 6 +- docs/DefaultApi.md | 136 +++++++++++- docs/NamespaceItem.md | 11 + nethsm-api.yaml | 410 +++++++++++++++++++++++++++++++---- src/apis/default_api.rs | 228 ++++++++++++++++++- src/models/mod.rs | 2 + src/models/namespace_item.rs | 21 ++ tests/basic.rs | 66 ++++++ 10 files changed, 824 insertions(+), 60 deletions(-) create mode 100644 docs/NamespaceItem.md create mode 100644 src/models/namespace_item.rs diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index 64cc307..6e67944 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -20,6 +20,7 @@ docs/KeyRestrictions.md docs/KeyType.md docs/LogLevel.md docs/LoggingConfig.md +docs/NamespaceItem.md docs/NetworkConfig.md docs/Pcr.md docs/PrivateKey.md @@ -73,6 +74,7 @@ src/models/key_type.rs src/models/log_level.rs src/models/logging_config.rs src/models/mod.rs +src/models/namespace_item.rs src/models/network_config.rs src/models/pcr.rs src/models/private_key.rs diff --git a/.openapi-generator/VERSION b/.openapi-generator/VERSION index ecb2186..6116b14 100644 --- a/.openapi-generator/VERSION +++ b/.openapi-generator/VERSION @@ -1 +1 @@ -7.6.0-SNAPSHOT +7.8.0-SNAPSHOT diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c9b961..a9392b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,17 @@ ## Unreleased +### Features + +- Add support for namespaces by adding the `namespaces_get`, `namespaces_namespace_id_delete`, `namespaces_namespace_id_put`, `users_user_id_post` API calls + ### Bugfixes - Return `Error::ResponseError` instead of `Error::Transport` for API errors ([#21](https://github.com/Nitrokey/nethsm-sdk-rs/issues/21)) - Fix multipart requests, namely `system_restore_post` ([#20](https://github.com/Nitrokey/nethsm-sdk-rs/issues/20)) - Add authentication for `system_restore_post` ([#15](https://github.com/Nitrokey/nethsm-sdk-rs/issues/15)) -### Changes +### Other Changes - Add `AkPub` and `Pcr` schemas diff --git a/docs/DefaultApi.md b/docs/DefaultApi.md index 011fc8a..e5cbdda 100644 --- a/docs/DefaultApi.md +++ b/docs/DefaultApi.md @@ -41,6 +41,9 @@ Method | HTTP request | Description [**keys_post**](DefaultApi.md#keys_post) | **POST** /keys | [**lock_post**](DefaultApi.md#lock_post) | **POST** /lock | [**metrics_get**](DefaultApi.md#metrics_get) | **GET** /metrics | +[**namespaces_get**](DefaultApi.md#namespaces_get) | **GET** /namespaces | +[**namespaces_namespace_id_delete**](DefaultApi.md#namespaces_namespace_id_delete) | **DELETE** /namespaces/{NamespaceID} | +[**namespaces_namespace_id_put**](DefaultApi.md#namespaces_namespace_id_put) | **PUT** /namespaces/{NamespaceID} | [**provision_post**](DefaultApi.md#provision_post) | **POST** /provision | [**random_post**](DefaultApi.md#random_post) | **POST** /random | [**system_backup_post**](DefaultApi.md#system_backup_post) | **POST** /system/backup | @@ -58,6 +61,7 @@ Method | HTTP request | Description [**users_user_id_delete**](DefaultApi.md#users_user_id_delete) | **DELETE** /users/{UserID} | [**users_user_id_get**](DefaultApi.md#users_user_id_get) | **GET** /users/{UserID} | [**users_user_id_passphrase_post**](DefaultApi.md#users_user_id_passphrase_post) | **POST** /users/{UserID}/passphrase | +[**users_user_id_post**](DefaultApi.md#users_user_id_post) | **POST** /users/{UserID} | [**users_user_id_put**](DefaultApi.md#users_user_id_put) | **PUT** /users/{UserID} | [**users_user_id_tags_get**](DefaultApi.md#users_user_id_tags_get) | **GET** /users/{UserID}/tags | [**users_user_id_tags_tag_delete**](DefaultApi.md#users_user_id_tags_tag_delete) | **DELETE** /users/{UserID}/tags/{Tag} | @@ -610,7 +614,7 @@ No authorization required > crate::models::CreateResourceId keys_generate_post(key_generate_request_data) -Generate a pair of public and private key and store it in NetHSM. KeyID is optional as a parameter and will be generated by NetHSM if not present. +Generate a pair of public and private key and store it in NetHSM. KeyID is optional as a parameter and will be generated by NetHSM if not present. The key is stored in the caller's namespace. ### Parameters @@ -640,7 +644,7 @@ Name | Type | Description | Required | Notes > Vec keys_get(filter) -Get a list of the identifiers of all keys that are currently stored in NetHSM. Separate requests need to be made to request the individual key data. +Get a list of the identifiers of all keys that are currently stored in NetHSM. If the caller is in a namespace, only keys in that namespace are returned. Separate requests need to be made to request the individual key data. ### Parameters @@ -944,7 +948,7 @@ Name | Type | Description | Required | Notes > keys_key_id_put(key_id, private_key) -Import a private key into NetHSM and store it under the *KeyID* path. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. +Import a private key into NetHSM and store it under the *KeyID* path. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. The key is stored in the caller's namespace. ### Parameters @@ -1068,7 +1072,7 @@ Name | Type | Description | Required | Notes > crate::models::CreateResourceId keys_post(private_key) -Import a private key into NetHSM and let NetHSM generate a KeyID. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. +Import a private key into NetHSM and let NetHSM generate a KeyID. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. The key is stored in the caller's namespace. ### Parameters @@ -1147,6 +1151,93 @@ This endpoint does not need any parameter. [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +## namespaces_get + +> Vec namespaces_get() + + +Get a list of all created namespaces. Note that users may be assigned namespaces not present in this list, as long as the namespace has not been created. + +### Parameters + +This endpoint does not need any parameter. + +### Return type + +[**Vec**](NamespaceItem.md) + +### Authorization + +[basic](../README.md#basic) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## namespaces_namespace_id_delete + +> namespaces_namespace_id_delete(namespace_id) + + +Delete a namespace. **WARNING: all keys from that namespace are deleted.** + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**namespace_id** | **String** | | [required] | + +### Return type + + (empty response body) + +### Authorization + +[basic](../README.md#basic) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + +## namespaces_namespace_id_put + +> namespaces_namespace_id_put(namespace_id) + + +Create a namespace on keyfender. All users in that namespace can now be used, and all user management power is delegated to admins in that namespace. + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**namespace_id** | **String** | | [required] | + +### Return type + + (empty response body) + +### Authorization + +[basic](../README.md#basic) + +### HTTP request headers + +- **Content-Type**: Not defined +- **Accept**: Not defined + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + ## provision_post > provision_post(provision_request_data) @@ -1492,7 +1583,7 @@ No authorization required > Vec users_get() -Get a list of all user ids that have accounts on NetHSM. +Get a list of all user ids that have accounts on NetHSM. If the caller is in a namespace, return only users in that namespace. ### Parameters @@ -1519,7 +1610,7 @@ This endpoint does not need any parameter. > crate::models::CreateResourceId users_post(user_post_data) -Create a new user on NetHSM. The user-ID is generated by NetHSM. +Create a new user on NetHSM, inheriting the caller's namespace. The user-ID is generated by NetHSM. ### Parameters @@ -1635,12 +1726,43 @@ Name | Type | Description | Required | Notes [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +## users_user_id_post + +> crate::models::CreateResourceId users_user_id_post(user_id, user_post_data) + + +Create a new user on NetHSM, in the namespace specified as a prefix in the path with the format 'namespace~'. For example, a POST request on \"https://nethsm.local/v1/users/namespace1~\" will generate a user-ID and create that user in \"namespace1\". The namespace prefix *must* be present: for creating users without a namespace, use a POST on \"/v1/users\". + +### Parameters + + +Name | Type | Description | Required | Notes +------------- | ------------- | ------------- | ------------- | ------------- +**user_id** | **String** | | [required] | +**user_post_data** | [**UserPostData**](UserPostData.md) | | [required] | + +### Return type + +[**crate::models::CreateResourceId**](CreateResourceId.md) + +### Authorization + +[basic](../README.md#basic) + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + + ## users_user_id_put > users_user_id_put(user_id, user_post_data) -Create a user on keyfender. +Create a user on keyfender. The new user must either be in the same namespace as the caller, or be in a namespace not created yet if the caller has no namespace. ### Parameters diff --git a/docs/NamespaceItem.md b/docs/NamespaceItem.md new file mode 100644 index 0000000..c1ca5fd --- /dev/null +++ b/docs/NamespaceItem.md @@ -0,0 +1,11 @@ +# NamespaceItem + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **String** | | + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/nethsm-api.yaml b/nethsm-api.yaml index 67ac828..9f8c9fc 100644 --- a/nethsm-api.yaml +++ b/nethsm-api.yaml @@ -33,6 +33,9 @@ paths: - Unprovisioned x-annotation-role: - Public + x-annotation-caller-namespace: + - Root + - Namespaced /health/alive: get: responses: @@ -50,6 +53,9 @@ paths: x-annotation-state: - Locked - Unprovisioned + x-annotation-caller-namespace: + - Root + - Namespaced /health/ready: get: responses: @@ -65,6 +71,9 @@ paths: - Public x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced /health/state: get: responses: @@ -83,6 +92,9 @@ paths: - Locked - Operational - Unprovisioned + x-annotation-caller-namespace: + - Root + - Namespaced /metrics: get: responses: @@ -109,6 +121,9 @@ paths: - Metrics x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced /provision: post: responses: @@ -136,6 +151,9 @@ paths: - Public x-annotation-state: - Unprovisioned + x-annotation-caller-namespace: + - Root + - Namespaced /unlock: post: responses: @@ -160,6 +178,9 @@ paths: - Public x-annotation-state: - Locked + x-annotation-caller-namespace: + - Root + - Namespaced /lock: post: responses: @@ -168,7 +189,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Lock failed (access denied). + description: Lock failed (access denied, e.g. user is in a namespace). "406": description: Content type in Accept header not supported. "412": @@ -180,6 +201,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /random: post: responses: @@ -214,6 +237,9 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced /keys: get: responses: @@ -233,6 +259,7 @@ paths: description: Precondition failed (NetHSM was not *Operational*). description: | Get a list of the identifiers of all keys that are currently stored in NetHSM. + If the caller is in a namespace, only keys in that namespace are returned. Separate requests need to be made to request the individual key data. parameters: - name: filter @@ -247,6 +274,9 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced post: responses: "201": @@ -277,6 +307,7 @@ paths: Import a private key into NetHSM and let NetHSM generate a KeyID. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. + The key is stored in the caller's namespace. requestBody: content: application/json: @@ -295,6 +326,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced /keys/generate: post: responses: @@ -324,6 +358,7 @@ paths: description: | Generate a pair of public and private key and store it in NetHSM. KeyID is optional as a parameter and will be generated by NetHSM if not present. + The key is stored in the caller's namespace. requestBody: content: application/json: @@ -336,6 +371,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced /keys/{KeyID}: get: responses: @@ -408,7 +446,7 @@ paths: "403": description: Access denied. "404": - description: Key for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -421,6 +459,9 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 put: responses: @@ -442,6 +483,7 @@ paths: Import a private key into NetHSM and store it under the *KeyID* path. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. + The key is stored in the caller's namespace. requestBody: description: | For request body with content type `application/json`: @@ -504,6 +546,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: MyNewKey delete: responses: @@ -514,7 +559,7 @@ paths: "403": description: Access denied. "404": - description: Key for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -526,6 +571,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 parameters: - name: KeyID @@ -551,7 +599,7 @@ paths: "403": description: Access denied. "404": - description: Key for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -562,6 +610,9 @@ paths: x-annotation-role: - Administrator - Operator + x-annotation-caller-namespace: + - Root + - Namespaced x-annotation-state: - Operational x-test-value-KeyID: myKey1 @@ -587,7 +638,7 @@ paths: "403": description: Access denied. "404": - description: Key for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -604,6 +655,9 @@ paths: x-annotation-role: - Administrator - Operator + x-annotation-caller-namespace: + - Root + - Namespaced x-annotation-state: - Operational x-test-value-KeyID: myKey1 @@ -637,7 +691,7 @@ paths: "403": description: Access denied. "404": - description: Key for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -660,6 +714,9 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 parameters: - name: KeyID @@ -683,7 +740,7 @@ paths: "403": description: Access denied. "404": - description: Key for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -701,6 +758,9 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: MyAESKey parameters: - name: KeyID @@ -738,7 +798,7 @@ paths: "403": description: Access denied. "404": - description: Key for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -766,6 +826,9 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 parameters: - name: KeyID @@ -790,7 +853,7 @@ paths: "403": description: Access denied. "404": - description: Certificate for KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content-Type in Accept header is not supported. "412": @@ -804,6 +867,9 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 put: responses: @@ -836,6 +902,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 delete: responses: @@ -846,7 +915,7 @@ paths: "403": description: Access denied. "404": - description: There is no certificate for this KeyID. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -858,6 +927,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 parameters: - name: KeyID @@ -880,7 +952,7 @@ paths: "403": description: Access denied. "404": - description: KeyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -892,6 +964,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 x-test-value-Tag: MyNewTag delete: @@ -903,7 +978,7 @@ paths: "403": description: Access denied. "404": - description: Tag or keyID not found. + description: Key for KeyID not found in namespace. "406": description: Content type in Accept header not supported. "412": @@ -915,6 +990,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-KeyID: myKey1 x-test-value-Tag: MyTag parameters: @@ -928,6 +1006,95 @@ paths: required: true schema: type: string + /namespaces: + get: + responses: + "200": + description: "" + content: + application/json: + schema: + $ref: "#/components/schemas/NamespaceList" + "401": + description: Authentication required but none provided. + "403": + description: Access denied (e.g. the caller is in a namespace) + "406": + description: Content type in Accept header not supported. + "412": + description: Precondition failed (NetHSM was not *Operational*). + description: | + Get a list of all created namespaces. Note that users may be assigned namespaces + not present in this list, as long as the namespace has not been created. + security: + - basic: [] + x-annotation-role: + - Administrator + x-annotation-state: + - Operational + x-annotation-caller-namespace: + - Root + /namespaces/{NamespaceID}: + put: + responses: + "204": + description: Successful creation of namespace. + "400": + description: | + Bad request (specified properties are invalid, e.g. invalid namespace name + or namespace already exists). + "401": + description: Authentication required but none provided. + "403": + description: Access denied (e.g. user in a namespace or not an admin). + "406": + description: Content type in Accept header not supported. + "409": + description: Conflict - user already exists for this user-ID. + "412": + description: Precondition failed (NetHSM was not *Operational*). + description: | + Create a namespace on keyfender. All users in that namespace can now be used, and all user management + power is delegated to admins in that namespace. + security: + - basic: [] + x-annotation-role: + - Administrator + x-annotation-state: + - Operational + x-annotation-caller-namespace: + - Root + x-test-value-NamespaceID: namespace3 + delete: + responses: + "204": + description: Successful deletion of namespace. + "400": + description: Bad request. + "401": + description: Authentication required but none provided. + "403": + description: Access denied (e.g. user in a namespace or not an admin). + "404": + description: Namespace not found. + "412": + description: Precondition failed (NetHSM was not *Operational*). + description: "Delete a namespace. **WARNING: all keys from that namespace are deleted.**" + security: + - basic: [] + x-annotation-role: + - Administrator + x-annotation-state: + - Operational + x-annotation-caller-namespace: + - Root + x-test-value-NamespaceID: namespace1 + parameters: + - name: NamespaceID + in: path + required: true + schema: + type: string /users: get: responses: @@ -945,13 +1112,18 @@ paths: description: Content type in Accept header not supported. "412": description: Precondition failed (NetHSM was not *Operational*). - description: Get a list of all user ids that have accounts on NetHSM. + description: | + Get a list of all user ids that have accounts on NetHSM. + If the caller is in a namespace, return only users in that namespace. security: - basic: [] x-annotation-role: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced post: responses: "201": @@ -973,12 +1145,14 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. caller not administrator, or creating user in namespace not matching the caller's). "406": description: Content type in Accept header not supported. "412": description: Precondition failed (NetHSM was not *Operational*). - description: Create a new user on NetHSM. The user-ID is generated by NetHSM. + description: | + Create a new user on NetHSM, inheriting the caller's namespace. + The user-ID is generated by NetHSM. requestBody: content: application/json: @@ -991,6 +1165,9 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced /users/{UserID}: get: responses: @@ -1006,7 +1183,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user not in the same namespace) "404": description: User for UserID not found. "406": @@ -1021,6 +1198,12 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced + x-annotation-target-namespace: + - SameAsCaller + - CallerIsRoot x-test-value-UserID: operator put: responses: @@ -1031,14 +1214,16 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. caller not administrator, or creating user in namespace not matching the caller's). "406": description: Content type in Accept header not supported. "409": description: Conflict - user already exists for this user-ID. "412": description: Precondition failed (NetHSM was not *Operational*). - description: Create a user on keyfender. + description: | + Create a user on keyfender. The new user must either be in the same namespace as the caller, + or be in a namespace not created yet if the caller has no namespace. requestBody: content: application/json: @@ -1051,15 +1236,66 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced x-test-value-UserID: newOperator + post: + responses: + "201": + description: | + Successful creation of user. + The response contains a Location header, + e.g. "https://nethsm.local/v1/users/namespace1~I8mhHYJ1T3uk2lXrwXehVaw9KZzg7K". + headers: + location: + description: Where to find the newly added user. + schema: + type: string + content: + application/json: + schema: + $ref: "#/components/schemas/CreateResourceId" + "400": + description: Bad request (specified properties are invalid, e.g. weak passphrase). + "401": + description: Authentication required but none provided. + "403": + description: Access denied (e.g. caller not administrator, or creating user in namespace not matching the caller's). + "406": + description: Content type in Accept header not supported. + "412": + description: Precondition failed (NetHSM was not *Operational*). + description: | + Create a new user on NetHSM, in the namespace specified as a prefix in the path with the format 'namespace~'. + For example, a POST request on "https://nethsm.local/v1/users/namespace1~" will generate a user-ID and create that + user in "namespace1". The namespace prefix *must* be present: for creating users without a namespace, use a POST on "/v1/users". + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UserPostData" + required: true + security: + - basic: [] + x-test-value-UserID: namespace3~ + x-annotation-role: + - Administrator + x-annotation-state: + - Operational + x-annotation-caller-namespace: + - Root + - Namespaced delete: responses: "204": description: Successful deletion of user. + "400": + description: Bad request (cannot delete yourself) "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user not in the same namespace) "404": description: User not found for this user-ID. "406": @@ -1073,6 +1309,11 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced + x-annotation-target-namespace: + - SameAsCaller x-test-value-UserID: operator parameters: - name: UserID @@ -1090,7 +1331,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user not in the same namespace) "404": description: User not found for this user-ID. "406": @@ -1111,6 +1352,11 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced + x-annotation-target-namespace: + - SameAsCaller x-test-value-UserID: operator parameters: - name: UserID @@ -1132,7 +1378,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user not in the same namespace) "404": description: User not found for this user-ID. "406": @@ -1147,6 +1393,12 @@ paths: - Operator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced + x-annotation-target-namespace: + - SameAsCaller + - CallerIsRoot x-test-value-UserID: operator parameters: - name: UserID @@ -1166,7 +1418,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user not in the same namespace) "404": description: UserID not found. "406": @@ -1180,6 +1432,11 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced + x-annotation-target-namespace: + - SameAsCaller x-test-value-UserID: operator x-test-value-Tag: munich delete: @@ -1189,7 +1446,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user not in the same namespace) "404": description: Tag or user not found. "406": @@ -1203,6 +1460,11 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root + - Namespaced + x-annotation-target-namespace: + - SameAsCaller x-test-value-UserID: operator x-test-value-Tag: berlin parameters: @@ -1226,7 +1488,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1249,6 +1511,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/unattended-boot: get: responses: @@ -1261,7 +1525,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1273,6 +1537,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root put: responses: "204": @@ -1282,7 +1548,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1300,6 +1566,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/tls/public.pem: get: responses: @@ -1312,7 +1580,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1324,6 +1592,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/tls/cert.pem: get: responses: @@ -1336,7 +1606,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1350,6 +1620,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root put: responses: "201": @@ -1389,7 +1661,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1407,6 +1679,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/tls/generate: post: responses: @@ -1417,7 +1691,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1435,6 +1709,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/network: get: responses: @@ -1447,7 +1723,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1459,6 +1735,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root put: responses: "204": @@ -1468,7 +1746,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1486,6 +1764,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/logging: get: responses: @@ -1498,7 +1778,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1513,6 +1793,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root put: responses: "204": @@ -1522,7 +1804,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1540,6 +1822,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/backup-passphrase: put: responses: @@ -1550,7 +1834,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1575,6 +1859,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /config/time: get: responses: @@ -1587,7 +1873,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1599,6 +1885,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root put: responses: "204": @@ -1608,7 +1896,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1626,6 +1914,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/info: get: responses: @@ -1651,6 +1941,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/reboot: post: responses: @@ -1659,7 +1951,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1671,6 +1963,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/shutdown: post: responses: @@ -1679,7 +1973,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1691,6 +1985,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/factory-reset: post: responses: @@ -1699,7 +1995,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1711,6 +2007,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/update: post: responses: @@ -1725,7 +2023,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "409": @@ -1746,6 +2044,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/commit-update: post: responses: @@ -1754,7 +2054,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1768,6 +2068,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/cancel-update: post: responses: @@ -1776,7 +2078,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1790,6 +2092,8 @@ paths: - Administrator x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/backup: post: responses: @@ -1803,7 +2107,7 @@ paths: "401": description: Authentication required but none provided. "403": - description: Access denied. + description: Access denied (e.g. user is in namespace) "406": description: Content type in Accept header not supported. "412": @@ -1817,6 +2121,8 @@ paths: - Backup x-annotation-state: - Operational + x-annotation-caller-namespace: + - Root /system/restore: post: responses: @@ -1846,6 +2152,9 @@ paths: x-annotation-state: - Unprovisioned - Operational + x-annotation-caller-namespace: + - Root + - Namespaced components: schemas: AkPub: @@ -2018,6 +2327,19 @@ components: $ref: "#/components/schemas/ID" required: - user + NamespaceList: + example: + - id: namespace1 + type: array + items: + $ref: "#/components/schemas/NamespaceItem" + NamespaceItem: + type: object + properties: + id: + $ref: "#/components/schemas/ID" + required: + - id TagList: example: - berlin diff --git a/src/apis/default_api.rs b/src/apis/default_api.rs index 9c9f982..efcb41f 100644 --- a/src/apis/default_api.rs +++ b/src/apis/default_api.rs @@ -497,6 +497,42 @@ pub enum MetricsGetError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`namespaces_get`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum NamespacesGetError { + Status401(), + Status403(), + Status406(), + Status412(), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`namespaces_namespace_id_delete`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum NamespacesNamespaceIdDeleteError { + Status400(), + Status401(), + Status403(), + Status404(), + Status412(), + UnknownValue(serde_json::Value), +} + +/// struct for typed errors of method [`namespaces_namespace_id_put`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum NamespacesNamespaceIdPutError { + Status400(), + Status401(), + Status403(), + Status406(), + Status409(), + Status412(), + UnknownValue(serde_json::Value), +} + /// struct for typed errors of method [`provision_post`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -657,6 +693,7 @@ pub enum UsersPostError { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum UsersUserIdDeleteError { + Status400(), Status401(), Status403(), Status404(), @@ -691,6 +728,18 @@ pub enum UsersUserIdPassphrasePostError { UnknownValue(serde_json::Value), } +/// struct for typed errors of method [`users_user_id_post`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum UsersUserIdPostError { + Status400(), + Status401(), + Status403(), + Status406(), + Status412(), + UnknownValue(serde_json::Value), +} + /// struct for typed errors of method [`users_user_id_put`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -1458,7 +1507,7 @@ pub fn info_get( } } -/// Generate a pair of public and private key and store it in NetHSM. KeyID is optional as a parameter and will be generated by NetHSM if not present. +/// Generate a pair of public and private key and store it in NetHSM. KeyID is optional as a parameter and will be generated by NetHSM if not present. The key is stored in the caller's namespace. pub fn keys_generate_post( configuration: &configuration::Configuration, @@ -1498,7 +1547,7 @@ pub fn keys_generate_post( } } -/// Get a list of the identifiers of all keys that are currently stored in NetHSM. Separate requests need to be made to request the individual key data. +/// Get a list of the identifiers of all keys that are currently stored in NetHSM. If the caller is in a namespace, only keys in that namespace are returned. Separate requests need to be made to request the individual key data. pub fn keys_get( configuration: &configuration::Configuration, @@ -1931,7 +1980,7 @@ pub fn keys_key_id_public_pem_get( } } -/// Import a private key into NetHSM and store it under the *KeyID* path. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. +/// Import a private key into NetHSM and store it under the *KeyID* path. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. The key is stored in the caller's namespace. pub fn keys_key_id_put( configuration: &configuration::Configuration, @@ -2111,7 +2160,7 @@ pub fn keys_key_id_sign_post( } } -/// Import a private key into NetHSM and let NetHSM generate a KeyID. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. +/// Import a private key into NetHSM and let NetHSM generate a KeyID. The public key will be automatically derived. The parameters of the key can be passed as a PEM file or a JSON object. The key is stored in the caller's namespace. pub fn keys_post( configuration: &configuration::Configuration, @@ -2231,6 +2280,126 @@ pub fn metrics_get( } } +/// Get a list of all created namespaces. Note that users may be assigned namespaces not present in this list, as long as the namespace has not been created. + +pub fn namespaces_get( + configuration: &configuration::Configuration, +) -> Result>, Error> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!("{}/namespaces", local_var_configuration.base_path); + let mut local_var_req_builder = local_var_client.request("GET", local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.set("user-agent", local_var_user_agent); + } + if let Some(ref local_var_auth_conf) = local_var_configuration.basic_auth { + let value = super::basic_auth(local_var_auth_conf); + + local_var_req_builder = local_var_req_builder.set("authorization", &value); + }; + let accept_str = "application/json"; + local_var_req_builder = local_var_req_builder.set("accept", accept_str); + + let local_var_result = local_var_req_builder.call(); + + let local_var_resp = local_var_result.or_else(|err| match err { + ureq::Error::Status(_status, resp) => Ok(resp), + _ => Err(err), + })?; + + if local_var_resp.status() < 400 { + ResponseContent::deserialized(local_var_resp) + } else { + ResponseContent::deserialized(local_var_resp) + .and_then(|content| Err(Error::ResponseError(content))) + } +} + +/// Delete a namespace. **WARNING: all keys from that namespace are deleted.** + +pub fn namespaces_namespace_id_delete( + configuration: &configuration::Configuration, + namespace_id: &str, +) -> Result, Error> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/namespaces/{NamespaceID}", + local_var_configuration.base_path, + NamespaceID = crate::apis::urlencode(namespace_id) + ); + let mut local_var_req_builder = local_var_client.request("DELETE", local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.set("user-agent", local_var_user_agent); + } + if let Some(ref local_var_auth_conf) = local_var_configuration.basic_auth { + let value = super::basic_auth(local_var_auth_conf); + + local_var_req_builder = local_var_req_builder.set("authorization", &value); + }; + + let local_var_result = local_var_req_builder.call(); + + let local_var_resp = local_var_result.or_else(|err| match err { + ureq::Error::Status(_status, resp) => Ok(resp), + _ => Err(err), + })?; + + if local_var_resp.status() < 400 { + ResponseContent::unit(local_var_resp) + } else { + ResponseContent::deserialized(local_var_resp) + .and_then(|content| Err(Error::ResponseError(content))) + } +} + +/// Create a namespace on keyfender. All users in that namespace can now be used, and all user management power is delegated to admins in that namespace. + +pub fn namespaces_namespace_id_put( + configuration: &configuration::Configuration, + namespace_id: &str, +) -> Result, Error> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/namespaces/{NamespaceID}", + local_var_configuration.base_path, + NamespaceID = crate::apis::urlencode(namespace_id) + ); + let mut local_var_req_builder = local_var_client.request("PUT", local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.set("user-agent", local_var_user_agent); + } + if let Some(ref local_var_auth_conf) = local_var_configuration.basic_auth { + let value = super::basic_auth(local_var_auth_conf); + + local_var_req_builder = local_var_req_builder.set("authorization", &value); + }; + + let local_var_result = local_var_req_builder.call(); + + let local_var_resp = local_var_result.or_else(|err| match err { + ureq::Error::Status(_status, resp) => Ok(resp), + _ => Err(err), + })?; + + if local_var_resp.status() < 400 { + ResponseContent::unit(local_var_resp) + } else { + ResponseContent::deserialized(local_var_resp) + .and_then(|content| Err(Error::ResponseError(content))) + } +} + /// Initial provisioning, only available in *Unprovisioned* state. *WARNING:* The unlock passphrase can't be reset by an admin user without knowing the current value, so if the unlock passphrase is lost, neither can it be reset to a new value nor can the NetHSM be unlocked. pub fn provision_post( @@ -2700,7 +2869,7 @@ pub fn unlock_post( } } -/// Get a list of all user ids that have accounts on NetHSM. +/// Get a list of all user ids that have accounts on NetHSM. If the caller is in a namespace, return only users in that namespace. pub fn users_get( configuration: &configuration::Configuration, @@ -2738,7 +2907,7 @@ pub fn users_get( } } -/// Create a new user on NetHSM. The user-ID is generated by NetHSM. +/// Create a new user on NetHSM, inheriting the caller's namespace. The user-ID is generated by NetHSM. pub fn users_post( configuration: &configuration::Configuration, @@ -2905,7 +3074,52 @@ pub fn users_user_id_passphrase_post( } } -/// Create a user on keyfender. +/// Create a new user on NetHSM, in the namespace specified as a prefix in the path with the format 'namespace~'. For example, a POST request on \"https://nethsm.local/v1/users/namespace1~\" will generate a user-ID and create that user in \"namespace1\". The namespace prefix *must* be present: for creating users without a namespace, use a POST on \"/v1/users\". + +pub fn users_user_id_post( + configuration: &configuration::Configuration, + user_id: &str, + user_post_data: crate::models::UserPostData, +) -> Result, Error> { + let local_var_configuration = configuration; + + let local_var_client = &local_var_configuration.client; + + let local_var_uri_str = format!( + "{}/users/{UserID}", + local_var_configuration.base_path, + UserID = crate::apis::urlencode(user_id) + ); + let mut local_var_req_builder = local_var_client.request("POST", local_var_uri_str.as_str()); + + if let Some(ref local_var_user_agent) = local_var_configuration.user_agent { + local_var_req_builder = local_var_req_builder.set("user-agent", local_var_user_agent); + } + if let Some(ref local_var_auth_conf) = local_var_configuration.basic_auth { + let value = super::basic_auth(local_var_auth_conf); + + local_var_req_builder = local_var_req_builder.set("authorization", &value); + }; + let accept_str = "application/json"; + local_var_req_builder = local_var_req_builder.set("accept", accept_str); + + local_var_req_builder = local_var_req_builder.set("content-type", "application/json"); + let local_var_result = local_var_req_builder.send_json(user_post_data); + + let local_var_resp = local_var_result.or_else(|err| match err { + ureq::Error::Status(_status, resp) => Ok(resp), + _ => Err(err), + })?; + + if local_var_resp.status() < 400 { + ResponseContent::deserialized(local_var_resp) + } else { + ResponseContent::deserialized(local_var_resp) + .and_then(|content| Err(Error::ResponseError(content))) + } +} + +/// Create a user on keyfender. The new user must either be in the same namespace as the caller, or be in a namespace not created yet if the caller has no namespace. pub fn users_user_id_put( configuration: &configuration::Configuration, diff --git a/src/models/mod.rs b/src/models/mod.rs index ec0112c..756041d 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -40,6 +40,8 @@ pub mod log_level; pub use self::log_level::LogLevel; pub mod logging_config; pub use self::logging_config::LoggingConfig; +pub mod namespace_item; +pub use self::namespace_item::NamespaceItem; pub mod network_config; pub use self::network_config::NetworkConfig; pub mod pcr; diff --git a/src/models/namespace_item.rs b/src/models/namespace_item.rs new file mode 100644 index 0000000..80078f4 --- /dev/null +++ b/src/models/namespace_item.rs @@ -0,0 +1,21 @@ +/* + * NetHSM + * + * All endpoints expect exactly the specified JSON. Additional properties will cause a Bad Request Error (400). All HTTP errors contain a JSON structure with an explanation of type string. All [base64](https://tools.ietf.org/html/rfc4648#section-4) encoded values are Big Endian. + * + * The version of the OpenAPI document: v1 + * Contact: Nitrokey + * Generated by: https://openapi-generator.tech + */ + +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +pub struct NamespaceItem { + #[serde(rename = "id")] + pub id: String, +} + +impl NamespaceItem { + pub fn new(id: String) -> NamespaceItem { + NamespaceItem { id } + } +} diff --git a/tests/basic.rs b/tests/basic.rs index 20c25ab..8a91229 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -42,6 +42,63 @@ async fn test_error() { .await } +#[tokio::test] +async fn test_namespaces() { + let admin_passphrase = "adminadmin"; + let n_admin_passphrase = "admin2admin2"; + let unlock_passphrase = "unlockunlock"; + + utils::with_container(|mut config| { + let request = ProvisionRequestData { + unlock_passphrase: unlock_passphrase.to_owned(), + admin_passphrase: admin_passphrase.to_owned(), + system_time: Utc::now().to_rfc3339(), + }; + default_api::provision_post(&config, request).unwrap(); + + config.basic_auth = Some(("admin".to_owned(), Some(admin_passphrase.to_owned()))); + + let request = UserPostData { + real_name: "N-Admin".to_owned(), + role: UserRole::Administrator, + passphrase: n_admin_passphrase.to_owned(), + }; + let user_id = default_api::users_user_id_post(&config, "mynamespace~", request).unwrap().entity.id; + assert!(user_id.starts_with("mynamespace~")); + + assert_eq!(list_namespaces(&config), BTreeSet::new()); + + default_api::namespaces_namespace_id_put(&config, "mynamespace").unwrap(); + + assert_eq!(list_namespaces(&config), ["mynamespace".to_owned()].into_iter().collect()); + + config.basic_auth = Some((user_id, Some(n_admin_passphrase.to_owned()))); + + let request = KeyGenerateRequestData { + r#type: KeyType::Rsa, + length: Some(2048), + mechanisms: vec![KeyMechanism::RsaDecryptionRaw], + ..Default::default() + }; + let key_id = default_api::keys_generate_post(&config, request) + .unwrap() + .entity + .id; + let keys = BTreeSet::from([key_id.clone()]); + + assert_eq!(list_keys(&config), keys); + + config.basic_auth = Some(("admin".to_owned(), Some(admin_passphrase.to_owned()))); + + assert_eq!(list_keys(&config), BTreeSet::new()); + + default_api::namespaces_namespace_id_delete(&config, "mynamespace").unwrap(); + + assert_eq!(list_namespaces(&config), BTreeSet::new()); + }) + .await +} + #[tokio::test] async fn test_restore() { let admin_passphrase = "adminadmin"; @@ -141,3 +198,12 @@ fn list_keys(config: &Configuration) -> BTreeSet { .map(|item| item.id) .collect() } + +fn list_namespaces(config: &Configuration) -> BTreeSet { + default_api::namespaces_get(&config) + .unwrap() + .entity + .into_iter() + .map(|item| item.id) + .collect() +}