Office365-REST-Python-Client 2.5.14__py3-none-any.whl → 2.6.1__py3-none-any.whl
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.
- {Office365_REST_Python_Client-2.5.14.dist-info → Office365_REST_Python_Client-2.6.1.dist-info}/METADATA +21 -30
- {Office365_REST_Python_Client-2.5.14.dist-info → Office365_REST_Python_Client-2.6.1.dist-info}/RECORD +210 -112
- office365/admin/__init__.py +0 -0
- office365/admin/admin.py +69 -0
- office365/admin/microsoft365_apps.py +5 -0
- office365/admin/people_settings.py +5 -0
- office365/admin/report_settings.py +5 -0
- office365/{onedrive → admin}/sharepoint.py +1 -1
- office365/azure_env.py +60 -0
- office365/directory/applications/app_ids.py +6 -0
- office365/directory/authentication/authentication.py +16 -0
- office365/directory/authentication/method_target.py +2 -1
- office365/directory/authentication/methods/microsoft_authenticator.py +6 -0
- office365/directory/authentication/methods/root.py +11 -0
- office365/directory/authentication/methods/sms.py +9 -0
- office365/directory/certificates/self_signed.py +31 -2
- office365/directory/identities/conditional_access_root.py +14 -0
- office365/directory/identitygovernance/privilegedaccess/approval.py +8 -0
- office365/directory/identitygovernance/privilegedaccess/group.py +30 -0
- office365/directory/identitygovernance/privilegedaccess/group_assignment_schedule_instance.py +7 -0
- office365/directory/identitygovernance/privilegedaccess/unified_role_assignment_schedule_request.py +7 -0
- office365/directory/identitygovernance/userconsent/request_collection.py +10 -0
- office365/directory/identitygovernance/workflow/__init__.py +0 -0
- office365/directory/identitygovernance/workflow/base.py +5 -0
- office365/directory/policies/cross_tenant_access.py +13 -0
- office365/directory/policies/feature_rollout.py +16 -0
- office365/directory/policies/root.py +39 -23
- office365/directory/policies/template.py +5 -0
- office365/directory/protection/threatassessment/email_file_request.py +20 -0
- office365/directory/protection/threatassessment/request.py +25 -0
- office365/directory/rolemanagement/application.py +15 -0
- office365/directory/rolemanagement/resource_action.py +8 -4
- office365/directory/rolemanagement/role_permission.py +3 -3
- office365/directory/security/alerts/collection.py +49 -0
- office365/directory/security/scorecontrol/__init__.py +0 -0
- office365/directory/security/scorecontrol/profile.py +6 -0
- office365/directory/security/security.py +17 -4
- office365/directory/serviceprincipals/service_principal.py +36 -28
- office365/directory/tenantinformation/__init__.py +0 -0
- office365/directory/tenantinformation/multi_organization.py +11 -0
- office365/directory/tenantinformation/relationship.py +61 -0
- office365/directory/users/insights_settings.py +5 -0
- office365/graph_client.py +52 -132
- office365/graph_request.py +78 -19
- office365/intune/servicecommunications/health/health.py +1 -1
- office365/intune/servicecommunications/issues/issue.py +2 -2
- office365/onedrive/columns/definition.py +1 -1
- office365/onedrive/contenttypes/content_type.py +1 -1
- office365/onedrive/driveitems/driveItem.py +13 -1
- office365/onedrive/drives/drive.py +1 -1
- office365/onedrive/filestorage/__init__.py +0 -0
- office365/onedrive/filestorage/container.py +7 -0
- office365/onedrive/filestorage/file_storage.py +20 -0
- office365/onedrive/listitems/field_value_set.py +1 -1
- office365/onedrive/listitems/list_item.py +1 -1
- office365/onedrive/lists/list.py +1 -1
- office365/onedrive/recyclebin/__init__.py +0 -0
- office365/onedrive/recyclebin/item.py +6 -0
- office365/onedrive/recyclebin/recyclebin.py +29 -0
- office365/onedrive/recyclebin/settings.py +13 -0
- office365/onedrive/shares/drive_item.py +1 -1
- office365/onedrive/sitepages/base.py +1 -1
- office365/onedrive/sitepages/canvas_layout.py +2 -2
- office365/onedrive/sitepages/sections/__init__.py +0 -0
- office365/onedrive/sitepages/{horizontal_section.py → sections/horizontal.py} +1 -1
- office365/onedrive/sitepages/webparts/data.py +5 -0
- office365/onedrive/sitepages/webparts/position.py +5 -0
- office365/onedrive/sitepages/webparts/standard.py +9 -0
- office365/onedrive/sitepages/webparts/web_part.py +12 -0
- office365/onedrive/sites/site.py +1 -1
- office365/onedrive/storage/storage.py +10 -0
- office365/onedrive/workbooks/charts/axes.py +41 -1
- office365/onedrive/workbooks/charts/axis.py +5 -0
- office365/onedrive/workbooks/charts/chart.py +64 -5
- office365/onedrive/workbooks/charts/fill.py +5 -0
- office365/onedrive/workbooks/charts/font.py +5 -0
- office365/onedrive/workbooks/charts/legend/__init__.py +0 -0
- office365/onedrive/workbooks/charts/legend/format.py +5 -0
- office365/onedrive/workbooks/charts/legend/legend.py +26 -0
- office365/onedrive/workbooks/charts/point.py +5 -0
- office365/onedrive/workbooks/charts/series/__init__.py +0 -0
- office365/onedrive/workbooks/charts/series/format.py +5 -0
- office365/onedrive/workbooks/charts/series/series.py +31 -0
- office365/onedrive/workbooks/charts/title.py +5 -0
- office365/onedrive/workbooks/filter.py +10 -0
- office365/onedrive/workbooks/filter_criteria.py +3 -3
- office365/onedrive/workbooks/ranges/fill.py +11 -1
- office365/onedrive/workbooks/ranges/range.py +26 -0
- office365/onedrive/workbooks/ranges/view.py +14 -0
- office365/onedrive/workbooks/sort_field.py +8 -3
- office365/onedrive/workbooks/tables/sort.py +20 -0
- office365/onedrive/workbooks/worksheets/worksheet.py +11 -0
- office365/partners/billing/azure_usage.py +17 -0
- office365/partners/billing/billed_usage.py +21 -0
- office365/partners/billing/billing.py +17 -0
- office365/partners/billing/operation.py +5 -0
- office365/partners/partners.py +17 -0
- office365/reports/root.py +9 -0
- office365/reports/userregistration/feature_summary.py +5 -0
- office365/runtime/auth/auth_cookies.py +48 -0
- office365/runtime/auth/authentication_context.py +36 -19
- office365/runtime/auth/entra/__init__.py +0 -0
- office365/runtime/auth/entra/authentication_context.py +165 -0
- office365/runtime/auth/providers/acs_token_provider.py +12 -27
- office365/runtime/auth/providers/saml_token_provider.py +46 -67
- office365/runtime/auth/sts_profile.py +37 -22
- office365/runtime/auth/token_response.py +4 -0
- office365/sharepoint/administration/custom_fonts_resource.py +5 -0
- office365/sharepoint/administration/sitemove/__init__.py +0 -0
- office365/sharepoint/administration/sitemove/service.py +68 -0
- office365/sharepoint/administration/sitemove/service_info.py +11 -0
- office365/sharepoint/changes/item.py +15 -0
- office365/sharepoint/client_context.py +61 -37
- office365/sharepoint/directory/group.py +2 -0
- office365/sharepoint/directory/helper.py +2 -0
- office365/sharepoint/directory/provider/provider.py +6 -0
- office365/sharepoint/directory/user.py +20 -0
- office365/sharepoint/files/publish/__init__.py +0 -0
- office365/sharepoint/files/publish/status.py +9 -0
- office365/sharepoint/flows/connector_result.py +17 -0
- office365/sharepoint/flows/permissions.py +11 -7
- office365/sharepoint/fontpackages/__init__.py +0 -0
- office365/sharepoint/fontpackages/collection.py +26 -0
- office365/sharepoint/fontpackages/creation_parameters.py +5 -0
- office365/sharepoint/fontpackages/font_package.py +5 -0
- office365/sharepoint/fontpackages/oob_settings.py +5 -0
- office365/sharepoint/forms/customization.py +26 -0
- office365/sharepoint/ir/__init__.py +0 -0
- office365/sharepoint/ir/migration.py +5 -0
- office365/sharepoint/lists/list.py +30 -0
- office365/sharepoint/marketplace/app_metadata.py +12 -1
- office365/sharepoint/marketplace/app_metadata_collection.py +8 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/addins/instance_info.py +22 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/addins/permission_failed_info.py +5 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/addins/permission_info.py +12 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/addins/permission_response.py +15 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/addins/principals_response.py +5 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/app_request_information.py +9 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/app_response_information.py +9 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/available_addins_response.py +4 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/card_design.py +9 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/card_designs.py +9 -0
- office365/sharepoint/marketplace/corporatecuratedgallery/teams_package_download.py +14 -0
- office365/sharepoint/marketplace/tenant/appcatalog/accessor.py +62 -3
- office365/sharepoint/multigeo/services.py +14 -0
- office365/sharepoint/multigeo/storage_quota.py +9 -0
- office365/sharepoint/policy/license_utilities.py +27 -0
- office365/sharepoint/principal/principal.py +6 -0
- office365/sharepoint/request.py +95 -10
- office365/sharepoint/search/administration/document_crawl_log.py +16 -2
- office365/sharepoint/search/administration/tenant_crawl_versions_info_provider.py +1 -1
- office365/sharepoint/search/reports/abandoned_queries.py +11 -1
- office365/sharepoint/search/reports/abandonedqueries/item.py +7 -1
- office365/sharepoint/sharing/domain_restriction_settings.py +5 -0
- office365/sharepoint/sharing/information.py +12 -0
- office365/sharepoint/sitehealth/result.py +4 -4
- office365/sharepoint/sitehealth/summary.py +5 -5
- office365/sharepoint/sites/copy_job_progress.py +5 -0
- office365/sharepoint/sites/copy_migration_iInfo.py +5 -0
- office365/sharepoint/sites/home/__init__.py +0 -0
- office365/sharepoint/sites/{sph_site.py → home/site.py} +1 -1
- office365/sharepoint/sites/site.py +26 -1
- office365/sharepoint/tenant/administration/app_service_principal_public.py +17 -0
- office365/sharepoint/tenant/administration/audit/data.py +12 -0
- office365/sharepoint/tenant/administration/audit/unified_record.py +18 -0
- office365/sharepoint/tenant/administration/coms/__init__.py +0 -0
- office365/sharepoint/tenant/administration/coms/messages_fields_data.py +5 -0
- office365/sharepoint/tenant/administration/coms/messages_service_proxy.py +16 -0
- office365/sharepoint/tenant/administration/copilot/__init__.py +0 -0
- office365/sharepoint/tenant/administration/copilot/adaptive_card_config.py +11 -0
- office365/sharepoint/tenant/administration/datagovernance/__init__.py +0 -0
- office365/sharepoint/tenant/administration/datagovernance/client_base.py +9 -0
- office365/sharepoint/tenant/administration/datagovernance/insight_client.py +82 -0
- office365/sharepoint/tenant/administration/datagovernance/insight_metadata.py +5 -0
- office365/sharepoint/tenant/administration/default_time_zone_id.py +11 -0
- office365/sharepoint/tenant/administration/endpoints.py +53 -2
- office365/sharepoint/tenant/administration/insights/onedrive_site_sharing.py +4 -0
- office365/sharepoint/tenant/administration/internal/appservice/permission_request.py +4 -0
- office365/sharepoint/tenant/administration/malware/__init__.py +0 -0
- office365/sharepoint/tenant/administration/malware/file.py +18 -0
- office365/sharepoint/tenant/administration/modified_property.py +5 -0
- office365/sharepoint/tenant/administration/policies/content_security_configuration.py +9 -0
- office365/sharepoint/tenant/administration/recent_action.py +9 -0
- office365/sharepoint/tenant/administration/reports/__init__.py +0 -0
- office365/sharepoint/tenant/administration/settings_service.py +38 -5
- office365/sharepoint/tenant/administration/sites/properties.py +78 -0
- office365/sharepoint/tenant/administration/sites/properties_collection.py +19 -0
- office365/sharepoint/tenant/administration/tenant.py +134 -7
- office365/sharepoint/tenant/administration/types.py +76 -1
- office365/sharepoint/tenant/insights/report_manager.py +29 -0
- office365/sharepoint/tenant/management/office365_tenant.py +7 -4
- office365/sharepoint/translation/job_status.py +22 -0
- office365/sharepoint/userprofiles/user_profile.py +6 -0
- office365/sharepoint/webparts/tile_data.py +21 -0
- office365/sharepoint/webs/dataleakage_prevention_status_info.py +5 -0
- office365/sharepoint/webs/web.py +80 -1
- office365/teams/chats/event_message_detail.py +8 -0
- office365/teams/deleted.py +24 -0
- office365/teams/work.py +35 -0
- office365/directory/tenant_relationship.py +0 -23
- office365/onedrive/admin.py +0 -32
- office365/onedrive/workbooks/charts/legend.py +0 -7
- office365/onedrive/workbooks/charts/series.py +0 -5
- {Office365_REST_Python_Client-2.5.14.dist-info → Office365_REST_Python_Client-2.6.1.dist-info}/LICENSE +0 -0
- {Office365_REST_Python_Client-2.5.14.dist-info → Office365_REST_Python_Client-2.6.1.dist-info}/WHEEL +0 -0
- {Office365_REST_Python_Client-2.5.14.dist-info → Office365_REST_Python_Client-2.6.1.dist-info}/top_level.txt +0 -0
- /office365/{onedrive → admin}/sharepoint_settings.py +0 -0
- /office365/directory/{tenant_information.py → tenantinformation/information.py} +0 -0
- /office365/{base_item.py → onedrive/base_item.py} +0 -0
- /office365/onedrive/sitepages/{horizontal_section_column.py → sections/horizontal_column.py} +0 -0
- /office365/onedrive/sitepages/{vertical_section.py → sections/vertical.py} +0 -0
- /office365/sharepoint/sites/{home_sites_details.py → home/details.py} +0 -0
- /office365/sharepoint/sites/{home_site_reference.py → home/reference.py} +0 -0
- /office365/sharepoint/tenant/administration/{app_billing_properties.py → policies/app_billing_properties.py} +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from office365.azure_env import AzureEnvironment
|
|
4
|
+
from office365.runtime.auth.token_response import TokenResponse
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AuthenticationContext(object):
|
|
8
|
+
"""Provides authentication context for Microsoft Graph client"""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
tenant=None,
|
|
13
|
+
scopes=None,
|
|
14
|
+
token_cache=None,
|
|
15
|
+
environment=AzureEnvironment.Global,
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
:param str tenant: Tenant name, for example: contoso.onmicrosoft.com
|
|
19
|
+
:param list[str] or None scopes: Scopes requested to access an API
|
|
20
|
+
:param Any token_cache: Default cache is in memory only,
|
|
21
|
+
Refer https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
|
|
22
|
+
"""
|
|
23
|
+
self._tenant = tenant
|
|
24
|
+
if scopes is None:
|
|
25
|
+
scopes = [
|
|
26
|
+
"{0}/.default".format(AzureEnvironment.get_graph_authority(environment))
|
|
27
|
+
]
|
|
28
|
+
self._scopes = scopes
|
|
29
|
+
self._token_cache = token_cache
|
|
30
|
+
self._token_callback = None
|
|
31
|
+
self._environment = environment
|
|
32
|
+
|
|
33
|
+
def acquire_token(self):
|
|
34
|
+
# type: () -> TokenResponse
|
|
35
|
+
"""Acquire access token"""
|
|
36
|
+
if not self._token_callback:
|
|
37
|
+
raise ValueError("Token callback is not set.")
|
|
38
|
+
token_resp = self._token_callback()
|
|
39
|
+
token = TokenResponse.from_json(token_resp)
|
|
40
|
+
return token
|
|
41
|
+
|
|
42
|
+
def with_access_token(self, token_callback):
|
|
43
|
+
""""""
|
|
44
|
+
self._token_callback = token_callback
|
|
45
|
+
return self
|
|
46
|
+
|
|
47
|
+
def with_certificate(self, client_id, thumbprint, private_key):
|
|
48
|
+
"""
|
|
49
|
+
Initializes the confidential client with client certificate
|
|
50
|
+
|
|
51
|
+
:param str client_id: The OAuth client id of the calling application.
|
|
52
|
+
:param str thumbprint: Thumbprint
|
|
53
|
+
:param str private_key: Private key
|
|
54
|
+
"""
|
|
55
|
+
import msal
|
|
56
|
+
|
|
57
|
+
app = msal.ConfidentialClientApplication(
|
|
58
|
+
client_id,
|
|
59
|
+
authority=self.authority_url,
|
|
60
|
+
client_credential={
|
|
61
|
+
"thumbprint": thumbprint,
|
|
62
|
+
"private_key": private_key,
|
|
63
|
+
},
|
|
64
|
+
token_cache=self._token_cache, # Default cache is in memory only.
|
|
65
|
+
# You can learn how to use SerializableTokenCache from
|
|
66
|
+
# https://msal-python.readthedocs.io/en/latest/#msal.SerializableTokenCache
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def _acquire_token():
|
|
70
|
+
return app.acquire_token_for_client(scopes=self._scopes)
|
|
71
|
+
|
|
72
|
+
return self.with_access_token(_acquire_token)
|
|
73
|
+
|
|
74
|
+
def with_client_secret(self, client_id, client_secret):
|
|
75
|
+
# type: (str, str) -> "AuthenticationContext"
|
|
76
|
+
"""
|
|
77
|
+
Initializes the confidential client with client secret
|
|
78
|
+
|
|
79
|
+
:param str client_id: The OAuth client id of the calling application.
|
|
80
|
+
:param str client_secret: Client secret
|
|
81
|
+
"""
|
|
82
|
+
import msal
|
|
83
|
+
|
|
84
|
+
app = msal.ConfidentialClientApplication(
|
|
85
|
+
client_id,
|
|
86
|
+
authority=self.authority_url,
|
|
87
|
+
client_credential=client_secret,
|
|
88
|
+
token_cache=self._token_cache,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def _acquire_token():
|
|
92
|
+
return app.acquire_token_for_client(scopes=self._scopes)
|
|
93
|
+
|
|
94
|
+
return self.with_access_token(_acquire_token)
|
|
95
|
+
|
|
96
|
+
def with_token_interactive(self, client_id, username=None):
|
|
97
|
+
# type: (str, Optional[str]) -> "AuthenticationContext"
|
|
98
|
+
"""
|
|
99
|
+
Initializes the client via user credentials
|
|
100
|
+
Note: only works if your app is registered with redirect_uri as http://localhost
|
|
101
|
+
|
|
102
|
+
:param str client_id: The OAuth client id of the calling application.
|
|
103
|
+
:param str username: Typically a UPN in the form of an email address.
|
|
104
|
+
"""
|
|
105
|
+
import msal
|
|
106
|
+
|
|
107
|
+
app = msal.PublicClientApplication(client_id, authority=self.authority_url)
|
|
108
|
+
|
|
109
|
+
def _acquire_token():
|
|
110
|
+
# The pattern to acquire a token looks like this.
|
|
111
|
+
result = None
|
|
112
|
+
|
|
113
|
+
# Firstly, check the cache to see if this end user has signed in before
|
|
114
|
+
accounts = app.get_accounts(username=username)
|
|
115
|
+
if accounts:
|
|
116
|
+
chosen = accounts[0] # Assuming the end user chose this one to proceed
|
|
117
|
+
# Now let's try to find a token in cache for this account
|
|
118
|
+
result = app.acquire_token_silent(self._scopes, account=chosen)
|
|
119
|
+
|
|
120
|
+
if not result:
|
|
121
|
+
result = app.acquire_token_interactive(
|
|
122
|
+
self._scopes,
|
|
123
|
+
login_hint=username,
|
|
124
|
+
)
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
return self.with_access_token(_acquire_token)
|
|
128
|
+
|
|
129
|
+
def with_username_and_password(self, client_id, username, password):
|
|
130
|
+
# type: (str, str, str) -> "AuthenticationContext"
|
|
131
|
+
"""
|
|
132
|
+
Initializes the client via user credentials
|
|
133
|
+
|
|
134
|
+
:param str client_id: The OAuth client id of the calling application.
|
|
135
|
+
:param str username: Typically a UPN in the form of an email address.
|
|
136
|
+
:param str password: The password.
|
|
137
|
+
"""
|
|
138
|
+
import msal
|
|
139
|
+
|
|
140
|
+
app = msal.PublicClientApplication(
|
|
141
|
+
authority=self.authority_url,
|
|
142
|
+
client_id=client_id,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def _acquire_token():
|
|
146
|
+
result = None
|
|
147
|
+
accounts = app.get_accounts(username=username)
|
|
148
|
+
if accounts:
|
|
149
|
+
result = app.acquire_token_silent(self._scopes, account=accounts[0])
|
|
150
|
+
|
|
151
|
+
if not result:
|
|
152
|
+
result = app.acquire_token_by_username_password(
|
|
153
|
+
username=username,
|
|
154
|
+
password=password,
|
|
155
|
+
scopes=self._scopes,
|
|
156
|
+
)
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
return self.with_access_token(_acquire_token)
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def authority_url(self):
|
|
163
|
+
return "{0}/{1}".format(
|
|
164
|
+
AzureEnvironment.get_login_authority(self._environment), self._tenant
|
|
165
|
+
)
|
|
@@ -3,6 +3,7 @@ from typing import Optional
|
|
|
3
3
|
import requests
|
|
4
4
|
|
|
5
5
|
import office365.logger
|
|
6
|
+
from office365.azure_env import AzureEnvironment
|
|
6
7
|
from office365.runtime.auth.authentication_provider import AuthenticationProvider
|
|
7
8
|
from office365.runtime.auth.token_response import TokenResponse
|
|
8
9
|
from office365.runtime.compat import urlparse
|
|
@@ -10,7 +11,7 @@ from office365.runtime.http.request_options import RequestOptions
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
13
|
-
def __init__(self, url, client_id, client_secret, environment=
|
|
14
|
+
def __init__(self, url, client_id, client_secret, environment=None):
|
|
14
15
|
"""
|
|
15
16
|
Provider to acquire the access token from a Microsoft Azure Access Control Service (ACS)
|
|
16
17
|
|
|
@@ -18,7 +19,7 @@ class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
18
19
|
:param str client_secret: Secret string that the application uses to prove its identity when requesting a token
|
|
19
20
|
:param str url: SharePoint web or site url
|
|
20
21
|
:param str environment: The Office 365 Cloud Environment endpoint used for authentication
|
|
21
|
-
defaults to '
|
|
22
|
+
defaults to 'Azure Global'.
|
|
22
23
|
"""
|
|
23
24
|
self.url = url
|
|
24
25
|
self.redirect_url = None
|
|
@@ -26,18 +27,14 @@ class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
26
27
|
self.SharePointPrincipal = "00000003-0000-0ff1-ce00-000000000000"
|
|
27
28
|
self._client_id = client_id
|
|
28
29
|
self._client_secret = client_secret
|
|
29
|
-
self._cached_token = None
|
|
30
|
+
self._cached_token = None # type: Optional[TokenResponse]
|
|
30
31
|
self._environment = environment
|
|
31
32
|
|
|
32
33
|
def authenticate_request(self, request):
|
|
33
34
|
# type: (RequestOptions) -> None
|
|
34
|
-
self.ensure_app_only_access_token()
|
|
35
|
-
request.set_header("Authorization", self._get_authorization_header())
|
|
36
|
-
|
|
37
|
-
def ensure_app_only_access_token(self):
|
|
38
35
|
if self._cached_token is None:
|
|
39
36
|
self._cached_token = self.get_app_only_access_token()
|
|
40
|
-
|
|
37
|
+
request.set_header("Authorization", self._cached_token.authorization_header)
|
|
41
38
|
|
|
42
39
|
def get_app_only_access_token(self):
|
|
43
40
|
"""Retrieves an app-only access token from ACS"""
|
|
@@ -65,9 +62,7 @@ class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
65
62
|
self.SharePointPrincipal, target_host, target_realm
|
|
66
63
|
)
|
|
67
64
|
principal_id = self.get_formatted_principal(self._client_id, None, target_realm)
|
|
68
|
-
sts_url = self.get_security_token_service_url(
|
|
69
|
-
target_realm, environment=self._environment
|
|
70
|
-
)
|
|
65
|
+
sts_url = self.get_security_token_service_url(target_realm)
|
|
71
66
|
oauth2_request = {
|
|
72
67
|
"grant_type": "client_credentials",
|
|
73
68
|
"client_id": principal_id,
|
|
@@ -86,11 +81,6 @@ class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
86
81
|
def _get_realm_from_target_url(self):
|
|
87
82
|
"""Get the realm for the URL"""
|
|
88
83
|
response = requests.head(url=self.url, headers={"Authorization": "Bearer"})
|
|
89
|
-
return self.process_realm_response(response)
|
|
90
|
-
|
|
91
|
-
@staticmethod
|
|
92
|
-
def process_realm_response(response):
|
|
93
|
-
# type: (requests.Response) -> Optional[str]
|
|
94
84
|
header_key = "WWW-Authenticate"
|
|
95
85
|
if header_key in response.headers:
|
|
96
86
|
auth_values = response.headers[header_key].split(",")
|
|
@@ -105,20 +95,15 @@ class ACSTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
105
95
|
return "{0}/{1}@{2}".format(principal_name, host_name, realm)
|
|
106
96
|
return "{0}@{1}".format(principal_name, realm)
|
|
107
97
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
98
|
+
def get_security_token_service_url(self, realm):
|
|
99
|
+
# type: (str) -> str
|
|
100
|
+
if self._environment:
|
|
101
|
+
return "{0}/{1}/tokens/OAuth/2".format(
|
|
102
|
+
AzureEnvironment.get_login_authority(self._environment), realm
|
|
103
|
+
)
|
|
113
104
|
else:
|
|
114
105
|
return (
|
|
115
106
|
"https://accounts.accesscontrol.windows.net/{0}/tokens/OAuth/2".format(
|
|
116
107
|
realm
|
|
117
108
|
)
|
|
118
109
|
)
|
|
119
|
-
|
|
120
|
-
def _get_authorization_header(self):
|
|
121
|
-
return "Bearer {0}".format(self._cached_token.accessToken)
|
|
122
|
-
|
|
123
|
-
def get_last_error(self):
|
|
124
|
-
return self.error
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import uuid
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import Optional
|
|
3
5
|
from xml.dom import minidom
|
|
4
6
|
from xml.etree import ElementTree
|
|
5
7
|
|
|
@@ -7,6 +9,7 @@ import requests
|
|
|
7
9
|
import requests.utils
|
|
8
10
|
|
|
9
11
|
import office365.logger
|
|
12
|
+
from office365.runtime.auth.auth_cookies import AuthCookies
|
|
10
13
|
from office365.runtime.auth.authentication_provider import AuthenticationProvider
|
|
11
14
|
from office365.runtime.auth.sts_profile import STSProfile
|
|
12
15
|
from office365.runtime.auth.user_realm_info import UserRealmInfo
|
|
@@ -14,33 +17,21 @@ from office365.runtime.auth.user_realm_info import UserRealmInfo
|
|
|
14
17
|
office365.logger.ensure_debug_secrets()
|
|
15
18
|
|
|
16
19
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
def string_escape(value):
|
|
21
|
+
value = value.replace("&", "&")
|
|
22
|
+
value = value.replace("<", "<")
|
|
23
|
+
value = value.replace(">", ">")
|
|
24
|
+
value = value.replace('"', """)
|
|
25
|
+
value = value.replace("'", "'")
|
|
26
|
+
return value
|
|
21
27
|
|
|
22
28
|
|
|
23
|
-
def
|
|
24
|
-
|
|
25
|
-
s_val = s_val.replace("<", "<")
|
|
26
|
-
s_val = s_val.replace(">", ">")
|
|
27
|
-
s_val = s_val.replace('"', """)
|
|
28
|
-
s_val = s_val.replace("'", "'")
|
|
29
|
-
return s_val
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def is_valid_auth_cookies(values):
|
|
33
|
-
"""
|
|
34
|
-
Validates authorization cookies
|
|
35
|
-
"""
|
|
36
|
-
return any(values) and (
|
|
37
|
-
values.get("FedAuth", None) is not None
|
|
38
|
-
or values.get("SPOIDCRL", None) is not None
|
|
39
|
-
)
|
|
29
|
+
def datetime_escape(value):
|
|
30
|
+
return value.isoformat("T")[:-9] + "Z"
|
|
40
31
|
|
|
41
32
|
|
|
42
33
|
class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
43
|
-
def __init__(self, url, username, password, browser_mode, environment=
|
|
34
|
+
def __init__(self, url, username, password, browser_mode, environment=None):
|
|
44
35
|
"""
|
|
45
36
|
SAML Security Token Service provider (claims-based authentication)
|
|
46
37
|
|
|
@@ -49,18 +40,16 @@ class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
49
40
|
:param str password: The password
|
|
50
41
|
:param bool browser_mode:
|
|
51
42
|
:param str environment: The Office 365 Cloud Environment endpoint used for authentication.
|
|
52
|
-
By default, this will be set to commercial ('commercial', 'GCCH')
|
|
53
43
|
"""
|
|
54
44
|
# Security Token Service info
|
|
55
|
-
self._sts_profile = STSProfile(
|
|
45
|
+
self._sts_profile = STSProfile(url, environment)
|
|
56
46
|
# Obtain authentication cookies, using the browser mode
|
|
57
47
|
self._browser_mode = browser_mode
|
|
58
|
-
self._environment = environment
|
|
59
48
|
# Last occurred error
|
|
60
49
|
self.error = ""
|
|
61
50
|
self._username = username
|
|
62
51
|
self._password = password
|
|
63
|
-
self._cached_auth_cookies = None
|
|
52
|
+
self._cached_auth_cookies = None # type: Optional[AuthCookies]
|
|
64
53
|
self.__ns_prefixes = {
|
|
65
54
|
"S": "{http://www.w3.org/2003/05/soap-envelope}",
|
|
66
55
|
"s": "{http://www.w3.org/2003/05/soap-envelope}",
|
|
@@ -82,24 +71,20 @@ class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
82
71
|
Authenticate request handler
|
|
83
72
|
"""
|
|
84
73
|
logger = self.logger(self.authenticate_request.__name__)
|
|
85
|
-
self.ensure_authentication_cookie()
|
|
86
|
-
logger.debug_secrets(self._cached_auth_cookies)
|
|
87
|
-
cookie_header_value = "; ".join(
|
|
88
|
-
[
|
|
89
|
-
"=".join([key, str(val)])
|
|
90
|
-
for key, val in self._cached_auth_cookies.items()
|
|
91
|
-
]
|
|
92
|
-
)
|
|
93
|
-
request.set_header("Cookie", cookie_header_value)
|
|
94
74
|
|
|
95
|
-
|
|
96
|
-
if
|
|
75
|
+
request_time = datetime.now(timezone.utc)
|
|
76
|
+
if (
|
|
77
|
+
self._cached_auth_cookies is None
|
|
78
|
+
or request_time >= self._sts_profile.expires
|
|
79
|
+
):
|
|
80
|
+
self._sts_profile.reset()
|
|
97
81
|
self._cached_auth_cookies = self.get_authentication_cookie()
|
|
98
|
-
|
|
82
|
+
logger.debug_secrets(self._cached_auth_cookies)
|
|
83
|
+
request.set_header("Cookie", self._cached_auth_cookies.cookie_header)
|
|
99
84
|
|
|
100
85
|
def get_authentication_cookie(self):
|
|
101
86
|
"""Acquire authentication cookie"""
|
|
102
|
-
logger = self.logger(self.
|
|
87
|
+
logger = self.logger(self.get_authentication_cookie.__name__)
|
|
103
88
|
logger.debug("get_authentication_cookie called")
|
|
104
89
|
|
|
105
90
|
try:
|
|
@@ -132,9 +117,6 @@ class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
132
117
|
return info
|
|
133
118
|
return None
|
|
134
119
|
|
|
135
|
-
def get_last_error(self):
|
|
136
|
-
return self.error
|
|
137
|
-
|
|
138
120
|
def _acquire_service_token_from_adfs(self, adfs_url):
|
|
139
121
|
logger = self.logger(self._acquire_service_token_from_adfs.__name__)
|
|
140
122
|
|
|
@@ -143,11 +125,11 @@ class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
143
125
|
{
|
|
144
126
|
"auth_url": adfs_url,
|
|
145
127
|
"message_id": str(uuid.uuid4()),
|
|
146
|
-
"username":
|
|
147
|
-
"password":
|
|
148
|
-
"created": self._sts_profile.created,
|
|
149
|
-
"expires": self._sts_profile.expires,
|
|
150
|
-
"issuer": self._sts_profile.
|
|
128
|
+
"username": string_escape(self._username),
|
|
129
|
+
"password": string_escape(self._password),
|
|
130
|
+
"created": datetime_escape(self._sts_profile.created),
|
|
131
|
+
"expires": datetime_escape(self._sts_profile.expires),
|
|
132
|
+
"issuer": self._sts_profile.token_issuer,
|
|
151
133
|
},
|
|
152
134
|
)
|
|
153
135
|
|
|
@@ -194,13 +176,13 @@ class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
194
176
|
payload = self._prepare_request_from_template(
|
|
195
177
|
"SAML.xml",
|
|
196
178
|
{
|
|
197
|
-
"auth_url": self._sts_profile.
|
|
198
|
-
"username":
|
|
199
|
-
"password":
|
|
179
|
+
"auth_url": self._sts_profile.site_url,
|
|
180
|
+
"username": string_escape(self._username),
|
|
181
|
+
"password": string_escape(self._password),
|
|
200
182
|
"message_id": str(uuid.uuid4()),
|
|
201
|
-
"created": self._sts_profile.created,
|
|
202
|
-
"expires": self._sts_profile.expires,
|
|
203
|
-
"issuer": self._sts_profile.
|
|
183
|
+
"created": datetime_escape(self._sts_profile.created),
|
|
184
|
+
"expires": datetime_escape(self._sts_profile.expires),
|
|
185
|
+
"issuer": self._sts_profile.token_issuer,
|
|
204
186
|
},
|
|
205
187
|
)
|
|
206
188
|
logger.debug_secrets("options: %s", payload)
|
|
@@ -300,9 +282,9 @@ class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
300
282
|
},
|
|
301
283
|
)
|
|
302
284
|
logger.debug_secrets("session.cookies: %s", session.cookies)
|
|
303
|
-
cookies = requests.utils.dict_from_cookiejar(session.cookies)
|
|
285
|
+
cookies = AuthCookies(requests.utils.dict_from_cookiejar(session.cookies))
|
|
304
286
|
logger.debug_secrets("cookies: %s", cookies)
|
|
305
|
-
if not
|
|
287
|
+
if not cookies.is_valid:
|
|
306
288
|
self.error = (
|
|
307
289
|
"An error occurred while retrieving auth cookies from {0}".format(
|
|
308
290
|
self._sts_profile.signin_page_url
|
|
@@ -312,19 +294,16 @@ class SamlTokenProvider(AuthenticationProvider, office365.logger.LoggerContext):
|
|
|
312
294
|
raise ValueError(self.error)
|
|
313
295
|
return cookies
|
|
314
296
|
|
|
315
|
-
|
|
316
|
-
def _prepare_request_from_template(template_name, params):
|
|
297
|
+
def _prepare_request_from_template(self, template_name, params):
|
|
317
298
|
"""Construct the request body to acquire security token from STS endpoint"""
|
|
318
|
-
logger =
|
|
299
|
+
logger = self.logger(self._prepare_request_from_template.__name__)
|
|
319
300
|
logger.debug_secrets("params: %s", params)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
301
|
+
|
|
302
|
+
template_path = os.path.join(
|
|
303
|
+
os.path.dirname(__file__), "templates", template_name
|
|
323
304
|
)
|
|
324
|
-
|
|
305
|
+
|
|
306
|
+
with open(template_path, encoding="utf8") as f:
|
|
325
307
|
data = f.read()
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
return data
|
|
329
|
-
finally:
|
|
330
|
-
f.close()
|
|
308
|
+
|
|
309
|
+
return data.format(**params)
|
|
@@ -1,40 +1,55 @@
|
|
|
1
1
|
from datetime import datetime, timedelta
|
|
2
2
|
|
|
3
|
+
from office365.azure_env import AzureEnvironment
|
|
3
4
|
from office365.runtime.compat import timezone, urlparse
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class STSProfile(object):
|
|
7
|
-
def __init__(self,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
self.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
self.
|
|
17
|
-
self.
|
|
18
|
-
self.tokenIssuer = "urn:federation:MicrosoftOnline"
|
|
19
|
-
now = datetime.now(tz=timezone.utc)
|
|
20
|
-
self.created = now.astimezone(timezone.utc).isoformat("T")[:-9] + "Z"
|
|
21
|
-
self.expires = (now + timedelta(minutes=10)).astimezone(timezone.utc).isoformat(
|
|
22
|
-
"T"
|
|
23
|
-
)[:-9] + "Z"
|
|
24
|
-
self.signInPage = "_forms/default.aspx?wa=wsignin1.0"
|
|
8
|
+
def __init__(self, site_url, environment=None):
|
|
9
|
+
# type: (str, str) -> None
|
|
10
|
+
self._site_url = site_url
|
|
11
|
+
self._environment = environment
|
|
12
|
+
self._created = datetime.now(tz=timezone.utc)
|
|
13
|
+
self._expires = self._created + timedelta(minutes=30)
|
|
14
|
+
|
|
15
|
+
def reset(self):
|
|
16
|
+
"""Renew the expiration time."""
|
|
17
|
+
self._created = datetime.now(tz=timezone.utc)
|
|
18
|
+
self._expires = self._created + timedelta(minutes=30)
|
|
25
19
|
|
|
26
20
|
@property
|
|
27
21
|
def tenant(self):
|
|
28
|
-
return urlparse(self.
|
|
22
|
+
return urlparse(self._site_url).netloc
|
|
29
23
|
|
|
30
24
|
@property
|
|
31
25
|
def security_token_service_url(self):
|
|
32
|
-
return "/".
|
|
26
|
+
return "{0}/extSTS.srf".format(
|
|
27
|
+
AzureEnvironment.get_login_authority(self._environment),
|
|
28
|
+
)
|
|
33
29
|
|
|
34
30
|
@property
|
|
35
31
|
def signin_page_url(self):
|
|
36
|
-
|
|
32
|
+
site_info = urlparse(self._site_url)
|
|
33
|
+
return "{0}://{1}/_forms/default.aspx?wa=wsignin1.0".format(site_info.scheme, site_info.netloc)
|
|
37
34
|
|
|
38
35
|
@property
|
|
39
36
|
def user_realm_service_url(self):
|
|
40
|
-
return "/".
|
|
37
|
+
return "{0}/GetUserRealm.srf".format(
|
|
38
|
+
AzureEnvironment.get_login_authority(self._environment),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def token_issuer(self):
|
|
43
|
+
return "urn:federation:MicrosoftOnline"
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def created(self):
|
|
47
|
+
return self._created
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def expires(self):
|
|
51
|
+
return self._expires
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def site_url(self):
|
|
55
|
+
return self._site_url
|
|
@@ -9,6 +9,10 @@ class TokenResponse(object):
|
|
|
9
9
|
def is_valid(self):
|
|
10
10
|
return self.accessToken is not None and self.tokenType == "Bearer"
|
|
11
11
|
|
|
12
|
+
@property
|
|
13
|
+
def authorization_header(self):
|
|
14
|
+
return "Bearer {0}".format(self.accessToken)
|
|
15
|
+
|
|
12
16
|
@staticmethod
|
|
13
17
|
def from_json(value):
|
|
14
18
|
error = value.get("error", None)
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from office365.runtime.client_result import ClientResult
|
|
4
|
+
from office365.runtime.client_value import ClientValue
|
|
5
|
+
from office365.runtime.paths.service_operation import ServiceOperationPath
|
|
6
|
+
from office365.runtime.queries.service_operation import ServiceOperationQuery
|
|
7
|
+
from office365.sharepoint.entity import Entity
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from office365.sharepoint.client_context import ClientContext
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SystemSiteLockExpirationResult(ClientValue):
|
|
14
|
+
""""""
|
|
15
|
+
|
|
16
|
+
def __init__(self, error=None, expiration=None):
|
|
17
|
+
self.Error = error
|
|
18
|
+
self.Expiration = expiration
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SiteMoveService(Entity):
|
|
22
|
+
""" """
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
context,
|
|
27
|
+
site_id,
|
|
28
|
+
site_subscription_id=None,
|
|
29
|
+
source_database_id=None,
|
|
30
|
+
target_database_id=None,
|
|
31
|
+
):
|
|
32
|
+
# type: (ClientContext, str, str, str, str) -> None
|
|
33
|
+
""""""
|
|
34
|
+
static_path = ServiceOperationPath(
|
|
35
|
+
"Microsoft.SharePoint.Administration.SiteMove.Service.SiteMoveService",
|
|
36
|
+
{
|
|
37
|
+
"siteId": site_id,
|
|
38
|
+
"siteSubscriptionId": site_subscription_id,
|
|
39
|
+
"sourceDatabaseId": source_database_id,
|
|
40
|
+
"targetDatabaseId": target_database_id,
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
super(SiteMoveService, self).__init__(context, static_path)
|
|
44
|
+
|
|
45
|
+
def acquire_system_site_lock(
|
|
46
|
+
self, lock_requestor, lock_type, lease_duration_in_minutes
|
|
47
|
+
):
|
|
48
|
+
""""""
|
|
49
|
+
return_type = ClientResult(self.context, SystemSiteLockExpirationResult())
|
|
50
|
+
payload = {
|
|
51
|
+
"lockRequestor": lock_requestor,
|
|
52
|
+
"lockType": lock_type,
|
|
53
|
+
"leaseDurationInMinutes": lease_duration_in_minutes,
|
|
54
|
+
}
|
|
55
|
+
qry = ServiceOperationQuery(
|
|
56
|
+
self,
|
|
57
|
+
"AcquireSystemSiteLock",
|
|
58
|
+
None,
|
|
59
|
+
payload,
|
|
60
|
+
None,
|
|
61
|
+
return_type,
|
|
62
|
+
)
|
|
63
|
+
self.context.add_query(qry)
|
|
64
|
+
return return_type
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def entity_type_name(self):
|
|
68
|
+
return "Microsoft.SharePoint.Administration.SiteMove.Service.SiteMoveService"
|
|
@@ -20,6 +20,21 @@ class ChangeItem(Change):
|
|
|
20
20
|
"""Specifies an identifier for the content type"""
|
|
21
21
|
return self.properties.get("ContentTypeId", ContentTypeId())
|
|
22
22
|
|
|
23
|
+
@property
|
|
24
|
+
def editor(self):
|
|
25
|
+
"""Specifies the editor of the changed item."""
|
|
26
|
+
return self.properties.get("Editor", None)
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def editor_email_hint(self):
|
|
30
|
+
"""Returns the email corresponding to Editor."""
|
|
31
|
+
return self.properties.get("EditorEmailHint", None)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def editor_login_name(self):
|
|
35
|
+
"""Returns login name of the Editor."""
|
|
36
|
+
return self.properties.get("EditorLoginName", None)
|
|
37
|
+
|
|
23
38
|
@property
|
|
24
39
|
def file_type(self):
|
|
25
40
|
# type: () -> Optional[str]
|