workos 5.23.0__tar.gz → 5.26.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.
- {workos-5.23.0 → workos-5.26.0}/PKG-INFO +1 -1
- workos-5.26.0/tests/test_organization_domains.py +136 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_organizations.py +13 -9
- workos-5.26.0/tests/test_vault.py +460 -0
- {workos-5.23.0 → workos-5.26.0}/workos/__about__.py +1 -1
- {workos-5.23.0 → workos-5.26.0}/workos/_base_client.py +5 -0
- {workos-5.23.0 → workos-5.26.0}/workos/async_client.py +16 -0
- {workos-5.23.0 → workos-5.26.0}/workos/client.py +16 -0
- workos-5.26.0/workos/organization_domains.py +179 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/event.py +16 -1
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/event_model.py +1 -1
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/event_type.py +3 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/organization_domain_verification_failed_payload.py +1 -1
- {workos-5.23.0 → workos-5.26.0}/workos/types/list_resource.py +2 -0
- workos-5.26.0/workos/types/organization_domains/__init__.py +1 -0
- {workos-5.23.0/workos/types/organizations → workos-5.26.0/workos/types/organization_domains}/organization_domain.py +3 -0
- workos-5.26.0/workos/types/organizations/__init__.py +6 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/organizations/organization.py +1 -1
- {workos-5.23.0 → workos-5.26.0}/workos/types/organizations/organization_common.py +1 -1
- workos-5.26.0/workos/types/vault/__init__.py +2 -0
- workos-5.26.0/workos/types/vault/key.py +25 -0
- workos-5.26.0/workos/types/vault/object.py +38 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/webhooks/webhook.py +1 -1
- workos-5.26.0/workos/utils/crypto_provider.py +39 -0
- workos-5.26.0/workos/vault.py +515 -0
- {workos-5.23.0 → workos-5.26.0}/workos.egg-info/PKG-INFO +1 -1
- {workos-5.23.0 → workos-5.26.0}/workos.egg-info/SOURCES.txt +10 -1
- workos-5.23.0/workos/types/organizations/__init__.py +0 -4
- {workos-5.23.0 → workos-5.26.0}/LICENSE +0 -0
- {workos-5.23.0 → workos-5.26.0}/README.md +0 -0
- {workos-5.23.0 → workos-5.26.0}/setup.cfg +0 -0
- {workos-5.23.0 → workos-5.26.0}/setup.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_async_http_client.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_audit_logs.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_client.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_directory_sync.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_events.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_fga.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_mfa.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_passwordless.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_portal.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_session.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_sso.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_sync_http_client.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_user_management.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_webhooks.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/tests/test_widgets.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/_client_configuration.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/audit_logs.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/directory_sync.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/events.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/exceptions.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/fga.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/mfa.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/organizations.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/passwordless.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/portal.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/py.typed +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/session.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/sso.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/audit_logs/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/audit_logs/audit_log_event.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/audit_logs/audit_log_event_actor.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/audit_logs/audit_log_event_context.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/audit_logs/audit_log_event_target.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/audit_logs/audit_log_export.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/audit_logs/audit_log_metadata.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/directory_sync/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/directory_sync/directory.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/directory_sync/directory_group.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/directory_sync/directory_state.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/directory_sync/directory_type.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/directory_sync/directory_user.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/directory_sync/list_filters.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/authentication_payload.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/connection_payload_with_legacy_fields.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/directory_group_membership_payload.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/directory_group_with_previous_attributes.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/directory_payload.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/directory_payload_with_legacy_fields.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/directory_user_with_previous_attributes.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/list_filters.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/previous_attributes.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/events/session_created_payload.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/fga/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/fga/authorization_resource_types.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/fga/authorization_resources.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/fga/check.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/fga/list_filters.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/fga/warnings.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/fga/warrant.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/metadata.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/mfa/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/mfa/authentication_challenge.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/mfa/authentication_challenge_verification_response.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/mfa/authentication_factor.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/mfa/authentication_factor_totp_and_challenge_response.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/mfa/enroll_authentication_factor_type.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/organizations/domain_data_input.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/organizations/list_filters.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/passwordless/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/passwordless/passwordless_session.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/passwordless/passwordless_session_type.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/portal/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/portal/portal_link.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/portal/portal_link_intent.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/portal/portal_link_intent_options.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/roles/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/roles/role.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/sso/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/sso/connection.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/sso/connection_domain.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/sso/profile.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/sso/sso_provider_type.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/authenticate_with_common.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/authentication_response.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/email_verification.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/impersonator.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/invitation.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/list_filters.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/magic_auth.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/oauth_tokens.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/organization_membership.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/password_hash_type.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/password_reset.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/screen_hint.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/session.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/user.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/user_management/user_management_provider_type.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/webhooks/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/webhooks/webhook_model.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/webhooks/webhook_payload.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/widgets/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/widgets/widget_scope.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/widgets/widget_token_response.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/types/workos_model.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/typing/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/typing/literals.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/typing/sync_or_async.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/typing/untyped_literal.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/typing/webhooks.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/user_management.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/utils/__init__.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/utils/_base_http_client.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/utils/http_client.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/utils/pagination_order.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/utils/request_helper.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/webhooks.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos/widgets.py +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos.egg-info/dependency_links.txt +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos.egg-info/not-zip-safe +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos.egg-info/requires.txt +0 -0
- {workos-5.23.0 → workos-5.26.0}/workos.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import pytest
|
|
3
|
+
from tests.utils.syncify import syncify
|
|
4
|
+
from workos.organization_domains import AsyncOrganizationDomains, OrganizationDomains
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.sync_and_async(OrganizationDomains, AsyncOrganizationDomains)
|
|
8
|
+
class TestOrganizationDomains:
|
|
9
|
+
@pytest.fixture(autouse=True)
|
|
10
|
+
def setup(
|
|
11
|
+
self, module_instance: Union[OrganizationDomains, AsyncOrganizationDomains]
|
|
12
|
+
):
|
|
13
|
+
self.http_client = module_instance._http_client
|
|
14
|
+
self.organization_domains = module_instance
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def mock_organization_domain(self):
|
|
18
|
+
return {
|
|
19
|
+
"object": "organization_domain",
|
|
20
|
+
"id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8",
|
|
21
|
+
"organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
|
|
22
|
+
"domain": "example.com",
|
|
23
|
+
"state": "pending",
|
|
24
|
+
"verification_strategy": "dns",
|
|
25
|
+
"verification_token": "workos_example_verification_token_12345",
|
|
26
|
+
"verification_prefix": "_workos-challenge",
|
|
27
|
+
"created_at": "2023-01-01T12:00:00.000Z",
|
|
28
|
+
"updated_at": "2023-01-01T12:00:00.000Z",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def mock_organization_domain_verified(self):
|
|
33
|
+
return {
|
|
34
|
+
"object": "organization_domain",
|
|
35
|
+
"id": "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8",
|
|
36
|
+
"organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
|
|
37
|
+
"domain": "example.com",
|
|
38
|
+
"state": "verified",
|
|
39
|
+
"verification_strategy": "dns",
|
|
40
|
+
"verification_token": "workos_example_verification_token_12345",
|
|
41
|
+
"verification_prefix": "_workos-challenge",
|
|
42
|
+
"created_at": "2023-01-01T12:00:00.000Z",
|
|
43
|
+
"updated_at": "2023-01-01T12:00:00.000Z",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def test_get_organization_domain(
|
|
47
|
+
self, capture_and_mock_http_client_request, mock_organization_domain
|
|
48
|
+
):
|
|
49
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
50
|
+
self.http_client,
|
|
51
|
+
mock_organization_domain,
|
|
52
|
+
200,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
organization_domain = syncify(
|
|
56
|
+
self.organization_domains.get_organization_domain(
|
|
57
|
+
organization_domain_id="org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
assert request_kwargs["url"].endswith(
|
|
62
|
+
"/organization_domains/org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
63
|
+
)
|
|
64
|
+
assert request_kwargs["method"] == "get"
|
|
65
|
+
assert organization_domain.id == "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
66
|
+
assert organization_domain.domain == "example.com"
|
|
67
|
+
assert organization_domain.state == "pending"
|
|
68
|
+
assert organization_domain.verification_strategy == "dns"
|
|
69
|
+
|
|
70
|
+
def test_create_organization_domain(
|
|
71
|
+
self, capture_and_mock_http_client_request, mock_organization_domain
|
|
72
|
+
):
|
|
73
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
74
|
+
self.http_client,
|
|
75
|
+
mock_organization_domain,
|
|
76
|
+
201,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
organization_domain = syncify(
|
|
80
|
+
self.organization_domains.create_organization_domain(
|
|
81
|
+
organization_id="org_01EHT88Z8J8795GZNQ4ZP1J81T",
|
|
82
|
+
domain="example.com",
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
assert request_kwargs["url"].endswith("/organization_domains")
|
|
87
|
+
assert request_kwargs["method"] == "post"
|
|
88
|
+
assert request_kwargs["json"] == {
|
|
89
|
+
"organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
|
|
90
|
+
"domain": "example.com",
|
|
91
|
+
}
|
|
92
|
+
assert organization_domain.id == "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
93
|
+
assert organization_domain.domain == "example.com"
|
|
94
|
+
assert organization_domain.organization_id == "org_01EHT88Z8J8795GZNQ4ZP1J81T"
|
|
95
|
+
|
|
96
|
+
def test_verify_organization_domain(
|
|
97
|
+
self, capture_and_mock_http_client_request, mock_organization_domain_verified
|
|
98
|
+
):
|
|
99
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
100
|
+
self.http_client,
|
|
101
|
+
mock_organization_domain_verified,
|
|
102
|
+
200,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
organization_domain = syncify(
|
|
106
|
+
self.organization_domains.verify_organization_domain(
|
|
107
|
+
organization_domain_id="org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
assert request_kwargs["url"].endswith(
|
|
112
|
+
"/organization_domains/org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8/verify"
|
|
113
|
+
)
|
|
114
|
+
assert request_kwargs["method"] == "post"
|
|
115
|
+
assert organization_domain.id == "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
116
|
+
assert organization_domain.state == "verified"
|
|
117
|
+
|
|
118
|
+
def test_delete_organization_domain(self, capture_and_mock_http_client_request):
|
|
119
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
120
|
+
self.http_client,
|
|
121
|
+
None,
|
|
122
|
+
204,
|
|
123
|
+
headers={"content-type": "text/plain; charset=utf-8"},
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
response = syncify(
|
|
127
|
+
self.organization_domains.delete_organization_domain(
|
|
128
|
+
organization_domain_id="org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
assert request_kwargs["url"].endswith(
|
|
133
|
+
"/organization_domains/org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
134
|
+
)
|
|
135
|
+
assert request_kwargs["method"] == "delete"
|
|
136
|
+
assert response is None
|
|
@@ -38,6 +38,8 @@ class TestOrganizations:
|
|
|
38
38
|
"organization_id": "org_01EHT88Z8J8795GZNQ4ZP1J81T",
|
|
39
39
|
"verification_strategy": "dns",
|
|
40
40
|
"verification_token": "token",
|
|
41
|
+
"created_at": datetime.datetime.now().isoformat(),
|
|
42
|
+
"updated_at": datetime.datetime.now().isoformat(),
|
|
41
43
|
}
|
|
42
44
|
],
|
|
43
45
|
}
|
|
@@ -189,15 +191,17 @@ class TestOrganizations:
|
|
|
189
191
|
}
|
|
190
192
|
assert updated_organization.id == "org_01EHT88Z8J8795GZNQ4ZP1J81T"
|
|
191
193
|
assert updated_organization.name == "Example Organization"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
domain = updated_organization.domains[0]
|
|
195
|
+
assert domain.domain == "example.io"
|
|
196
|
+
assert domain.object == "organization_domain"
|
|
197
|
+
assert domain.id == "org_domain_01EHT88Z8WZEFWYPM6EC9BX2R8"
|
|
198
|
+
assert domain.state == "verified"
|
|
199
|
+
assert domain.organization_id == "org_01EHT88Z8J8795GZNQ4ZP1J81T"
|
|
200
|
+
assert domain.verification_strategy == "dns"
|
|
201
|
+
assert domain.verification_token == "token"
|
|
202
|
+
assert domain.verification_prefix is None
|
|
203
|
+
assert isinstance(domain.created_at, str)
|
|
204
|
+
assert isinstance(domain.updated_at, str)
|
|
201
205
|
|
|
202
206
|
def test_delete_organization(self, capture_and_mock_http_client_request):
|
|
203
207
|
request_kwargs = capture_and_mock_http_client_request(
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from tests.utils.fixtures.mock_vault_object import (
|
|
3
|
+
MockVaultObject,
|
|
4
|
+
MockObjectVersion,
|
|
5
|
+
MockObjectDigest,
|
|
6
|
+
MockObjectMetadata,
|
|
7
|
+
)
|
|
8
|
+
from tests.utils.list_resource import list_response_of
|
|
9
|
+
from tests.utils.syncify import syncify
|
|
10
|
+
from workos.vault import Vault
|
|
11
|
+
from workos.types.vault.key import KeyContext
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TestVault:
|
|
15
|
+
@pytest.fixture(autouse=True)
|
|
16
|
+
def setup(self, sync_http_client_for_test):
|
|
17
|
+
self.http_client = sync_http_client_for_test
|
|
18
|
+
self.vault = Vault(http_client=self.http_client)
|
|
19
|
+
|
|
20
|
+
@pytest.fixture
|
|
21
|
+
def mock_vault_object(self):
|
|
22
|
+
return MockVaultObject(
|
|
23
|
+
"vault_01234567890abcdef", "test-secret", "secret-value"
|
|
24
|
+
).dict()
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def mock_object_digest(self):
|
|
28
|
+
return MockObjectDigest("vault_01234567890abcdef", "test-secret").dict()
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def mock_object_metadata(self):
|
|
32
|
+
return MockObjectMetadata("vault_01234567890abcdef").dict()
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def mock_vault_object_no_value(self):
|
|
36
|
+
mock_obj = MockVaultObject("vault_01234567890abcdef", "test-secret")
|
|
37
|
+
mock_obj.value = None
|
|
38
|
+
return mock_obj.dict()
|
|
39
|
+
|
|
40
|
+
@pytest.fixture
|
|
41
|
+
def mock_vault_objects_list(self):
|
|
42
|
+
vault_objects = [
|
|
43
|
+
MockObjectDigest(f"vault_{i}", f"secret-{i}").dict() for i in range(5)
|
|
44
|
+
]
|
|
45
|
+
return {
|
|
46
|
+
"data": vault_objects,
|
|
47
|
+
"list_metadata": {"before": None, "after": None},
|
|
48
|
+
"object": "list",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@pytest.fixture
|
|
52
|
+
def mock_vault_objects_multiple_pages(self):
|
|
53
|
+
vault_objects = [
|
|
54
|
+
MockObjectDigest(f"vault_{i}", f"secret-{i}").dict() for i in range(25)
|
|
55
|
+
]
|
|
56
|
+
return list_response_of(data=vault_objects)
|
|
57
|
+
|
|
58
|
+
@pytest.fixture
|
|
59
|
+
def mock_object_versions(self):
|
|
60
|
+
versions = [
|
|
61
|
+
MockObjectVersion(f"version_{i}", current_version=(i == 0)).dict()
|
|
62
|
+
for i in range(3)
|
|
63
|
+
]
|
|
64
|
+
return {"data": versions}
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def mock_data_key(self):
|
|
68
|
+
return {
|
|
69
|
+
"id": "key_01234567890abcdef",
|
|
70
|
+
"data_key": "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@pytest.fixture
|
|
74
|
+
def mock_data_key_pair(self):
|
|
75
|
+
return {
|
|
76
|
+
"context": {"key": "test-key"},
|
|
77
|
+
"id": "key_01234567890abcdef",
|
|
78
|
+
"data_key": "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=",
|
|
79
|
+
"encrypted_keys": "ZW5jcnlwdGVkX2tleXNfZGF0YQ==",
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def test_read_object_success(
|
|
83
|
+
self, mock_vault_object, capture_and_mock_http_client_request
|
|
84
|
+
):
|
|
85
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
86
|
+
self.http_client, mock_vault_object, 200
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
vault_object = self.vault.read_object(object_id="vault_01234567890abcdef")
|
|
90
|
+
|
|
91
|
+
assert request_kwargs["method"] == "get"
|
|
92
|
+
assert request_kwargs["url"].endswith("/vault/v1/kv/vault_01234567890abcdef")
|
|
93
|
+
assert vault_object.id == "vault_01234567890abcdef"
|
|
94
|
+
assert vault_object.name == "test-secret"
|
|
95
|
+
assert vault_object.value == "secret-value"
|
|
96
|
+
assert vault_object.metadata.environment_id == "env_01234567890abcdef"
|
|
97
|
+
|
|
98
|
+
def test_read_object_missing_object_id(self):
|
|
99
|
+
with pytest.raises(
|
|
100
|
+
ValueError, match="Incomplete arguments: 'object_id' is a required argument"
|
|
101
|
+
):
|
|
102
|
+
self.vault.read_object(object_id="")
|
|
103
|
+
|
|
104
|
+
def test_read_object_none_object_id(self):
|
|
105
|
+
with pytest.raises(
|
|
106
|
+
ValueError, match="Incomplete arguments: 'object_id' is a required argument"
|
|
107
|
+
):
|
|
108
|
+
self.vault.read_object(object_id=None)
|
|
109
|
+
|
|
110
|
+
def test_list_objects_default_params(
|
|
111
|
+
self, mock_vault_objects_list, capture_and_mock_http_client_request
|
|
112
|
+
):
|
|
113
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
114
|
+
self.http_client, mock_vault_objects_list, 200
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
vault_objects = self.vault.list_objects()
|
|
118
|
+
|
|
119
|
+
assert request_kwargs["method"] == "get"
|
|
120
|
+
assert request_kwargs["url"].endswith("/vault/v1/kv")
|
|
121
|
+
assert request_kwargs["params"]["limit"] == 10
|
|
122
|
+
assert "before" not in request_kwargs["params"]
|
|
123
|
+
assert "after" not in request_kwargs["params"]
|
|
124
|
+
assert len(vault_objects.data) == 5
|
|
125
|
+
assert vault_objects.data[0].id == "vault_0"
|
|
126
|
+
assert vault_objects.data[0].name == "secret-0"
|
|
127
|
+
|
|
128
|
+
def test_list_objects_with_params(
|
|
129
|
+
self, mock_vault_objects_list, capture_and_mock_http_client_request
|
|
130
|
+
):
|
|
131
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
132
|
+
self.http_client, mock_vault_objects_list, 200
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
vault_objects = self.vault.list_objects(
|
|
136
|
+
limit=5, before="vault_before", after="vault_after"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
assert request_kwargs["method"] == "get"
|
|
140
|
+
assert request_kwargs["url"].endswith("/vault/v1/kv")
|
|
141
|
+
assert request_kwargs["params"]["limit"] == 5
|
|
142
|
+
assert request_kwargs["params"]["before"] == "vault_before"
|
|
143
|
+
assert request_kwargs["params"]["after"] == "vault_after"
|
|
144
|
+
|
|
145
|
+
def test_list_objects_auto_pagination(
|
|
146
|
+
self, mock_vault_objects_multiple_pages, test_auto_pagination
|
|
147
|
+
):
|
|
148
|
+
test_auto_pagination(
|
|
149
|
+
http_client=self.http_client,
|
|
150
|
+
list_function=self.vault.list_objects,
|
|
151
|
+
expected_all_page_data=mock_vault_objects_multiple_pages["data"],
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def test_list_object_versions_success(
|
|
155
|
+
self, mock_object_versions, capture_and_mock_http_client_request
|
|
156
|
+
):
|
|
157
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
158
|
+
self.http_client, mock_object_versions, 200
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
versions = self.vault.list_object_versions(object_id="vault_01234567890abcdef")
|
|
162
|
+
|
|
163
|
+
assert request_kwargs["method"] == "get"
|
|
164
|
+
assert request_kwargs["url"].endswith(
|
|
165
|
+
"/vault/v1/kv/vault_01234567890abcdef/versions"
|
|
166
|
+
)
|
|
167
|
+
assert len(versions) == 3
|
|
168
|
+
assert versions[0].id == "version_0"
|
|
169
|
+
assert versions[0].current_version is True
|
|
170
|
+
assert versions[1].current_version is False
|
|
171
|
+
|
|
172
|
+
def test_list_object_versions_empty_data(
|
|
173
|
+
self, capture_and_mock_http_client_request
|
|
174
|
+
):
|
|
175
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
176
|
+
self.http_client, {"data": []}, 200
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
versions = self.vault.list_object_versions(object_id="vault_01234567890abcdef")
|
|
180
|
+
|
|
181
|
+
assert request_kwargs["method"] == "get"
|
|
182
|
+
assert len(versions) == 0
|
|
183
|
+
|
|
184
|
+
def test_create_object_success(
|
|
185
|
+
self, mock_object_metadata, capture_and_mock_http_client_request
|
|
186
|
+
):
|
|
187
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
188
|
+
self.http_client, mock_object_metadata, 200
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
object_metadata = self.vault.create_object(
|
|
192
|
+
name="test-secret",
|
|
193
|
+
value="secret-value",
|
|
194
|
+
key_context=KeyContext({"key": "test-key"}),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
assert request_kwargs["method"] == "post"
|
|
198
|
+
assert request_kwargs["url"].endswith("/vault/v1/kv")
|
|
199
|
+
assert request_kwargs["json"]["name"] == "test-secret"
|
|
200
|
+
assert request_kwargs["json"]["value"] == "secret-value"
|
|
201
|
+
assert request_kwargs["json"]["key_context"] == KeyContext({"key": "test-key"})
|
|
202
|
+
assert object_metadata.id == "vault_01234567890abcdef"
|
|
203
|
+
|
|
204
|
+
def test_create_object_missing_name(self):
|
|
205
|
+
with pytest.raises(
|
|
206
|
+
ValueError,
|
|
207
|
+
match="Incomplete arguments: 'name' and 'value' are required arguments",
|
|
208
|
+
):
|
|
209
|
+
self.vault.create_object(
|
|
210
|
+
name="",
|
|
211
|
+
value="secret-value",
|
|
212
|
+
key_context=KeyContext({"key": "test-key"}),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def test_create_object_missing_value(self):
|
|
216
|
+
with pytest.raises(
|
|
217
|
+
ValueError,
|
|
218
|
+
match="Incomplete arguments: 'name' and 'value' are required arguments",
|
|
219
|
+
):
|
|
220
|
+
self.vault.create_object(
|
|
221
|
+
name="test-secret",
|
|
222
|
+
value="",
|
|
223
|
+
key_context=KeyContext({"key": "test-key"}),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def test_create_object_missing_both(self):
|
|
227
|
+
with pytest.raises(
|
|
228
|
+
ValueError,
|
|
229
|
+
match="Incomplete arguments: 'name' and 'value' are required arguments",
|
|
230
|
+
):
|
|
231
|
+
self.vault.create_object(
|
|
232
|
+
name="", value="", key_context=KeyContext({"key": "test-key"})
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def test_update_object_with_value(
|
|
236
|
+
self, mock_vault_object, capture_and_mock_http_client_request
|
|
237
|
+
):
|
|
238
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
239
|
+
self.http_client, mock_vault_object, 200
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
vault_object = self.vault.update_object(
|
|
243
|
+
object_id="vault_01234567890abcdef",
|
|
244
|
+
value="updated-value",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
assert request_kwargs["method"] == "put"
|
|
248
|
+
assert request_kwargs["url"].endswith("/vault/v1/kv/vault_01234567890abcdef")
|
|
249
|
+
assert request_kwargs["json"]["value"] == "updated-value"
|
|
250
|
+
assert "version_check" not in request_kwargs["json"]
|
|
251
|
+
assert vault_object.id == "vault_01234567890abcdef"
|
|
252
|
+
|
|
253
|
+
def test_update_object_with_version_check(
|
|
254
|
+
self, mock_vault_object, capture_and_mock_http_client_request
|
|
255
|
+
):
|
|
256
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
257
|
+
self.http_client, mock_vault_object, 200
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
vault_object = self.vault.update_object(
|
|
261
|
+
object_id="vault_01234567890abcdef",
|
|
262
|
+
value="updated-value",
|
|
263
|
+
version_check="version_123",
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
assert request_kwargs["method"] == "put"
|
|
267
|
+
assert request_kwargs["json"]["value"] == "updated-value"
|
|
268
|
+
assert request_kwargs["json"]["version_check"] == "version_123"
|
|
269
|
+
|
|
270
|
+
def test_update_object_missing_value(self):
|
|
271
|
+
with pytest.raises(
|
|
272
|
+
TypeError, match="missing 1 required keyword-only argument: 'value'"
|
|
273
|
+
):
|
|
274
|
+
self.vault.update_object(object_id="vault_01234567890abcdef")
|
|
275
|
+
|
|
276
|
+
def test_update_object_missing_object_id(self):
|
|
277
|
+
with pytest.raises(
|
|
278
|
+
ValueError, match="Incomplete arguments: 'object_id' is a required argument"
|
|
279
|
+
):
|
|
280
|
+
self.vault.update_object(object_id="", value="test-value")
|
|
281
|
+
|
|
282
|
+
def test_update_object_none_object_id(self):
|
|
283
|
+
with pytest.raises(
|
|
284
|
+
ValueError,
|
|
285
|
+
match="Incomplete arguments: 'object_id' is a required argument",
|
|
286
|
+
):
|
|
287
|
+
self.vault.update_object(object_id=None, value="updated-value")
|
|
288
|
+
|
|
289
|
+
def test_delete_object_success(self, capture_and_mock_http_client_request):
|
|
290
|
+
request_kwargs = capture_and_mock_http_client_request(self.http_client, {}, 204)
|
|
291
|
+
|
|
292
|
+
result = self.vault.delete_object(object_id="vault_01234567890abcdef")
|
|
293
|
+
|
|
294
|
+
assert request_kwargs["method"] == "delete"
|
|
295
|
+
assert request_kwargs["url"].endswith("/vault/v1/kv/vault_01234567890abcdef")
|
|
296
|
+
assert result is None
|
|
297
|
+
|
|
298
|
+
def test_delete_object_missing_object_id(self):
|
|
299
|
+
with pytest.raises(
|
|
300
|
+
ValueError, match="Incomplete arguments: 'object_id' is a required argument"
|
|
301
|
+
):
|
|
302
|
+
self.vault.delete_object(object_id="")
|
|
303
|
+
|
|
304
|
+
def test_delete_object_none_object_id(self):
|
|
305
|
+
with pytest.raises(
|
|
306
|
+
ValueError, match="Incomplete arguments: 'object_id' is a required argument"
|
|
307
|
+
):
|
|
308
|
+
self.vault.delete_object(object_id=None)
|
|
309
|
+
|
|
310
|
+
def test_create_data_key_success(
|
|
311
|
+
self, mock_data_key_pair, capture_and_mock_http_client_request
|
|
312
|
+
):
|
|
313
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
314
|
+
self.http_client, mock_data_key_pair, 200
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
data_key_pair = self.vault.create_data_key(
|
|
318
|
+
key_context=KeyContext({"key": "test-key"})
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
assert request_kwargs["method"] == "post"
|
|
322
|
+
assert request_kwargs["url"].endswith("/vault/v1/keys/data-key")
|
|
323
|
+
assert request_kwargs["json"]["context"] == KeyContext({"key": "test-key"})
|
|
324
|
+
assert data_key_pair.data_key.id == "key_01234567890abcdef"
|
|
325
|
+
assert data_key_pair.encrypted_keys == "ZW5jcnlwdGVkX2tleXNfZGF0YQ=="
|
|
326
|
+
|
|
327
|
+
def test_decrypt_data_key_success(
|
|
328
|
+
self, mock_data_key, capture_and_mock_http_client_request
|
|
329
|
+
):
|
|
330
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
331
|
+
self.http_client, mock_data_key, 200
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
data_key = self.vault.decrypt_data_key(keys="ZW5jcnlwdGVkX2tleXNfZGF0YQ==")
|
|
335
|
+
|
|
336
|
+
assert request_kwargs["method"] == "post"
|
|
337
|
+
assert request_kwargs["url"].endswith("/vault/v1/keys/decrypt")
|
|
338
|
+
assert request_kwargs["json"]["keys"] == "ZW5jcnlwdGVkX2tleXNfZGF0YQ=="
|
|
339
|
+
assert data_key.id == "key_01234567890abcdef"
|
|
340
|
+
assert data_key.key == "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY="
|
|
341
|
+
|
|
342
|
+
def test_encrypt_success(
|
|
343
|
+
self, mock_data_key_pair, capture_and_mock_http_client_request
|
|
344
|
+
):
|
|
345
|
+
# Mock the create_data_key call
|
|
346
|
+
request_kwargs = capture_and_mock_http_client_request(
|
|
347
|
+
self.http_client, mock_data_key_pair, 200
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
plaintext = "Hello, World!"
|
|
351
|
+
context = KeyContext({"key": "test-key"})
|
|
352
|
+
|
|
353
|
+
encrypted_data = self.vault.encrypt(data=plaintext, key_context=context)
|
|
354
|
+
|
|
355
|
+
# Verify create_data_key was called
|
|
356
|
+
assert request_kwargs["method"] == "post"
|
|
357
|
+
assert request_kwargs["url"].endswith("/vault/v1/keys/data-key")
|
|
358
|
+
assert request_kwargs["json"]["context"] == KeyContext({"key": "test-key"})
|
|
359
|
+
|
|
360
|
+
# Verify we got encrypted data back
|
|
361
|
+
assert isinstance(encrypted_data, str)
|
|
362
|
+
assert len(encrypted_data) > 0
|
|
363
|
+
|
|
364
|
+
def test_encrypt_with_associated_data(
|
|
365
|
+
self, mock_data_key_pair, capture_and_mock_http_client_request
|
|
366
|
+
):
|
|
367
|
+
# Mock the create_data_key call
|
|
368
|
+
capture_and_mock_http_client_request(self.http_client, mock_data_key_pair, 200)
|
|
369
|
+
|
|
370
|
+
plaintext = "Hello, World!"
|
|
371
|
+
context = KeyContext({"key": "test-key"})
|
|
372
|
+
associated_data = "additional-context"
|
|
373
|
+
|
|
374
|
+
encrypted_data = self.vault.encrypt(
|
|
375
|
+
data=plaintext, key_context=context, associated_data=associated_data
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Verify we got encrypted data back
|
|
379
|
+
assert isinstance(encrypted_data, str)
|
|
380
|
+
assert len(encrypted_data) > 0
|
|
381
|
+
|
|
382
|
+
def test_decrypt_success(self, mock_data_key, capture_and_mock_http_client_request):
|
|
383
|
+
# First encrypt some data to get a valid encrypted payload
|
|
384
|
+
mock_data_key_pair = {
|
|
385
|
+
"context": {"key": "test-key"},
|
|
386
|
+
"id": "key_01234567890abcdef",
|
|
387
|
+
"data_key": "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=",
|
|
388
|
+
"encrypted_keys": "ZW5jcnlwdGVkX2tleXNfZGF0YQ==",
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
# Mock create_data_key for encryption
|
|
392
|
+
capture_and_mock_http_client_request(self.http_client, mock_data_key_pair, 200)
|
|
393
|
+
|
|
394
|
+
plaintext = "Hello, World!"
|
|
395
|
+
context = KeyContext({"key": "test-key"})
|
|
396
|
+
encrypted_data = self.vault.encrypt(data=plaintext, key_context=context)
|
|
397
|
+
|
|
398
|
+
# Now mock decrypt_data_key for decryption
|
|
399
|
+
capture_and_mock_http_client_request(self.http_client, mock_data_key, 200)
|
|
400
|
+
|
|
401
|
+
# Decrypt the data
|
|
402
|
+
decrypted_text = self.vault.decrypt(encrypted_data=encrypted_data)
|
|
403
|
+
|
|
404
|
+
# Verify decryption worked
|
|
405
|
+
assert decrypted_text == plaintext
|
|
406
|
+
|
|
407
|
+
def test_decrypt_with_associated_data(
|
|
408
|
+
self, mock_data_key, capture_and_mock_http_client_request
|
|
409
|
+
):
|
|
410
|
+
# First encrypt some data with associated data
|
|
411
|
+
mock_data_key_pair = {
|
|
412
|
+
"context": {"key": "test-key"},
|
|
413
|
+
"id": "key_01234567890abcdef",
|
|
414
|
+
"data_key": "MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=",
|
|
415
|
+
"encrypted_keys": "ZW5jcnlwdGVkX2tleXNfZGF0YQ==",
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
# Mock create_data_key for encryption
|
|
419
|
+
capture_and_mock_http_client_request(self.http_client, mock_data_key_pair, 200)
|
|
420
|
+
|
|
421
|
+
plaintext = "Hello, World!"
|
|
422
|
+
context = KeyContext({"key": "test-key"})
|
|
423
|
+
associated_data = "additional-context"
|
|
424
|
+
encrypted_data = self.vault.encrypt(
|
|
425
|
+
data=plaintext, key_context=context, associated_data=associated_data
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Now mock decrypt_data_key for decryption
|
|
429
|
+
capture_and_mock_http_client_request(self.http_client, mock_data_key, 200)
|
|
430
|
+
|
|
431
|
+
# Decrypt the data with the same associated data
|
|
432
|
+
decrypted_text = self.vault.decrypt(
|
|
433
|
+
encrypted_data=encrypted_data, associated_data=associated_data
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
# Verify decryption worked
|
|
437
|
+
assert decrypted_text == plaintext
|
|
438
|
+
|
|
439
|
+
def test_encrypt_decrypt_roundtrip(
|
|
440
|
+
self, mock_data_key_pair, mock_data_key, capture_and_mock_http_client_request
|
|
441
|
+
):
|
|
442
|
+
"""Test that encrypt/decrypt works correctly together"""
|
|
443
|
+
|
|
444
|
+
# Mock create_data_key for encryption
|
|
445
|
+
capture_and_mock_http_client_request(self.http_client, mock_data_key_pair, 200)
|
|
446
|
+
|
|
447
|
+
plaintext = "This is a test message for encryption!"
|
|
448
|
+
context = KeyContext({"env": "test", "service": "vault"})
|
|
449
|
+
|
|
450
|
+
# Encrypt the data
|
|
451
|
+
encrypted_data = self.vault.encrypt(data=plaintext, key_context=context)
|
|
452
|
+
|
|
453
|
+
# Mock decrypt_data_key for decryption
|
|
454
|
+
capture_and_mock_http_client_request(self.http_client, mock_data_key, 200)
|
|
455
|
+
|
|
456
|
+
# Decrypt the data
|
|
457
|
+
decrypted_text = self.vault.decrypt(encrypted_data=encrypted_data)
|
|
458
|
+
|
|
459
|
+
# Verify roundtrip worked
|
|
460
|
+
assert decrypted_text == plaintext
|
|
@@ -11,6 +11,7 @@ from workos.directory_sync import DirectorySyncModule
|
|
|
11
11
|
from workos.events import EventsModule
|
|
12
12
|
from workos.mfa import MFAModule
|
|
13
13
|
from workos.organizations import OrganizationsModule
|
|
14
|
+
from workos.organization_domains import OrganizationDomainsModule
|
|
14
15
|
from workos.passwordless import PasswordlessModule
|
|
15
16
|
from workos.portal import PortalModule
|
|
16
17
|
from workos.sso import SSOModule
|
|
@@ -88,6 +89,10 @@ class BaseClient(ClientConfiguration):
|
|
|
88
89
|
@abstractmethod
|
|
89
90
|
def organizations(self) -> OrganizationsModule: ...
|
|
90
91
|
|
|
92
|
+
@property
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def organization_domains(self) -> OrganizationDomainsModule: ...
|
|
95
|
+
|
|
91
96
|
@property
|
|
92
97
|
@abstractmethod
|
|
93
98
|
def passwordless(self) -> PasswordlessModule: ...
|