cumulusci-plus 5.0.35__py3-none-any.whl → 5.0.43__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.
- cumulusci/__about__.py +1 -1
- cumulusci/cli/task.py +9 -10
- cumulusci/cli/tests/test_org.py +5 -0
- cumulusci/cli/tests/test_task.py +34 -0
- cumulusci/core/config/__init__.py +1 -0
- cumulusci/core/config/org_config.py +2 -1
- cumulusci/core/config/project_config.py +12 -0
- cumulusci/core/config/scratch_org_config.py +12 -0
- cumulusci/core/config/tests/test_config.py +1 -0
- cumulusci/core/dependencies/base.py +4 -0
- cumulusci/cumulusci.yml +18 -1
- cumulusci/schema/cumulusci.jsonschema.json +5 -0
- cumulusci/tasks/apex/testrunner.py +7 -4
- cumulusci/tasks/bulkdata/tests/test_select_utils.py +20 -0
- cumulusci/tasks/metadata_etl/__init__.py +2 -0
- cumulusci/tasks/metadata_etl/applications.py +256 -0
- cumulusci/tasks/metadata_etl/tests/test_applications.py +710 -0
- cumulusci/tasks/salesforce/insert_record.py +18 -19
- cumulusci/tasks/salesforce/tests/test_enable_prediction.py +4 -2
- cumulusci/tasks/salesforce/tests/test_update_external_auth_identity_provider.py +927 -0
- cumulusci/tasks/salesforce/tests/test_update_external_credential.py +523 -8
- cumulusci/tasks/salesforce/tests/test_update_record.py +512 -0
- cumulusci/tasks/salesforce/update_external_auth_identity_provider.py +551 -0
- cumulusci/tasks/salesforce/update_external_credential.py +89 -4
- cumulusci/tasks/salesforce/update_record.py +217 -0
- cumulusci/tasks/sfdmu/sfdmu.py +14 -1
- cumulusci/tasks/utility/credentialManager.py +58 -12
- cumulusci/tasks/utility/secretsToEnv.py +2 -2
- cumulusci/tasks/utility/tests/test_credentialManager.py +586 -0
- cumulusci/tasks/utility/tests/test_secretsToEnv.py +42 -15
- cumulusci/utils/yaml/cumulusci_yml.py +1 -0
- {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.43.dist-info}/METADATA +6 -7
- {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.43.dist-info}/RECORD +37 -31
- {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.43.dist-info}/WHEEL +1 -1
- {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.43.dist-info}/entry_points.txt +0 -0
- {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/AUTHORS.rst +0 -0
- {cumulusci_plus-5.0.35.dist-info → cumulusci_plus-5.0.43.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic.v1 import root_validator
|
|
5
|
+
|
|
6
|
+
from cumulusci.core.exceptions import SalesforceDXException
|
|
7
|
+
from cumulusci.salesforce_api.utils import get_simple_salesforce_connection
|
|
8
|
+
from cumulusci.tasks.salesforce import BaseSalesforceApiTask
|
|
9
|
+
from cumulusci.utils.options import CCIOptions, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExtParameter(CCIOptions):
|
|
13
|
+
"""External Auth Identity Provider Parameter options"""
|
|
14
|
+
|
|
15
|
+
name: str = Field(
|
|
16
|
+
None,
|
|
17
|
+
description="Parameter name. [default to None]",
|
|
18
|
+
)
|
|
19
|
+
value: str = Field(
|
|
20
|
+
None,
|
|
21
|
+
description="Parameter value. [default to None]",
|
|
22
|
+
)
|
|
23
|
+
sequence_number: int = Field(
|
|
24
|
+
None,
|
|
25
|
+
description="Sequence number. [default to None]",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ExternalAuthIdentityProviderCredential(CCIOptions):
|
|
30
|
+
"""External Auth Identity Provider Credential options"""
|
|
31
|
+
|
|
32
|
+
name: str = Field(
|
|
33
|
+
None,
|
|
34
|
+
description="Credential name. [default to None]",
|
|
35
|
+
)
|
|
36
|
+
client_id: str = Field(
|
|
37
|
+
None,
|
|
38
|
+
description="Client ID. [default to None]",
|
|
39
|
+
)
|
|
40
|
+
client_secret: str = Field(
|
|
41
|
+
None,
|
|
42
|
+
description="Client secret. [default to None]",
|
|
43
|
+
)
|
|
44
|
+
auth_protocol: str = Field(
|
|
45
|
+
"OAuth",
|
|
46
|
+
description="Authentication protocol. [default to OAuth]",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ExternalAuthIdentityProviderParameter(CCIOptions):
|
|
51
|
+
"""External Auth Identity Provider Parameter options"""
|
|
52
|
+
|
|
53
|
+
authorize_url: str = Field(
|
|
54
|
+
None,
|
|
55
|
+
description="Authorize URL. [default to None]",
|
|
56
|
+
)
|
|
57
|
+
token_url: str = Field(
|
|
58
|
+
None,
|
|
59
|
+
description="Token URL. [default to None]",
|
|
60
|
+
)
|
|
61
|
+
user_info_url: str = Field(
|
|
62
|
+
None,
|
|
63
|
+
description="User info URL. [default to None]",
|
|
64
|
+
)
|
|
65
|
+
jwks_url: str = Field(
|
|
66
|
+
None,
|
|
67
|
+
description="JWKS URL (for OpenID Connect). [default to None]",
|
|
68
|
+
)
|
|
69
|
+
issuer_url: str = Field(
|
|
70
|
+
None,
|
|
71
|
+
description="Issuer URL (for OpenID Connect). [default to None]",
|
|
72
|
+
)
|
|
73
|
+
client_authentication: str = Field(
|
|
74
|
+
None,
|
|
75
|
+
description="Client authentication method (e.g., ClientSecretBasic, ClientSecretPost, ClientSecretJwt). [default to None]",
|
|
76
|
+
)
|
|
77
|
+
custom_parameter: ExtParameter = Field(
|
|
78
|
+
None,
|
|
79
|
+
description="Custom parameter. [default to None]",
|
|
80
|
+
)
|
|
81
|
+
identity_provider_option: ExtParameter = Field(
|
|
82
|
+
None,
|
|
83
|
+
description="Identity provider option (e.g., PkceEnabled, UserinfoEnabled). [default to None]",
|
|
84
|
+
)
|
|
85
|
+
credential: ExternalAuthIdentityProviderCredential = Field(
|
|
86
|
+
None,
|
|
87
|
+
description="Credential to update. [default to None]",
|
|
88
|
+
)
|
|
89
|
+
secret: bool = Field(
|
|
90
|
+
False,
|
|
91
|
+
description="Is the value a secret. [default to False]",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@root_validator
|
|
95
|
+
def check_parameters(cls, values):
|
|
96
|
+
"""Check if at least one parameter is provided"""
|
|
97
|
+
param_fields = [
|
|
98
|
+
"authorize_url",
|
|
99
|
+
"token_url",
|
|
100
|
+
"user_info_url",
|
|
101
|
+
"jwks_url",
|
|
102
|
+
"issuer_url",
|
|
103
|
+
"client_authentication",
|
|
104
|
+
"custom_parameter",
|
|
105
|
+
"identity_provider_option",
|
|
106
|
+
"credential",
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
provided_params = [
|
|
110
|
+
field for field in param_fields if values.get(field) is not None
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
if len(provided_params) == 0:
|
|
114
|
+
raise ValueError("At least and only one parameter must be provided.")
|
|
115
|
+
|
|
116
|
+
if len(provided_params) > 1:
|
|
117
|
+
raise ValueError("At least and only one parameter must be provided.")
|
|
118
|
+
|
|
119
|
+
return values
|
|
120
|
+
|
|
121
|
+
def get_external_auth_identity_provider_parameter(self):
|
|
122
|
+
"""Get the external auth identity provider parameter based on which field is set"""
|
|
123
|
+
ext_auth_param = {}
|
|
124
|
+
|
|
125
|
+
if self.authorize_url is not None:
|
|
126
|
+
ext_auth_param["parameterType"] = "AuthorizeUrl"
|
|
127
|
+
ext_auth_param["parameterName"] = "AuthorizeUrl"
|
|
128
|
+
ext_auth_param["parameterValue"] = self.authorize_url
|
|
129
|
+
|
|
130
|
+
if self.token_url is not None:
|
|
131
|
+
ext_auth_param["parameterType"] = "TokenUrl"
|
|
132
|
+
ext_auth_param["parameterName"] = "TokenUrl"
|
|
133
|
+
ext_auth_param["parameterValue"] = self.token_url
|
|
134
|
+
|
|
135
|
+
if self.user_info_url is not None:
|
|
136
|
+
ext_auth_param["parameterType"] = "UserInfoUrl"
|
|
137
|
+
ext_auth_param["parameterName"] = "UserInfoUrl"
|
|
138
|
+
ext_auth_param["parameterValue"] = self.user_info_url
|
|
139
|
+
|
|
140
|
+
if self.jwks_url is not None:
|
|
141
|
+
ext_auth_param["parameterType"] = "JwksUrl"
|
|
142
|
+
ext_auth_param["parameterName"] = "JwksUrl"
|
|
143
|
+
ext_auth_param["parameterValue"] = self.jwks_url
|
|
144
|
+
|
|
145
|
+
if self.issuer_url is not None:
|
|
146
|
+
ext_auth_param["parameterType"] = "IssuerUrl"
|
|
147
|
+
ext_auth_param["parameterName"] = "IssuerUrl"
|
|
148
|
+
ext_auth_param["parameterValue"] = self.issuer_url
|
|
149
|
+
|
|
150
|
+
if self.client_authentication is not None:
|
|
151
|
+
ext_auth_param["parameterType"] = "ClientAuthentication"
|
|
152
|
+
ext_auth_param["parameterName"] = "ClientAuthentication"
|
|
153
|
+
ext_auth_param["parameterValue"] = self.client_authentication
|
|
154
|
+
|
|
155
|
+
if self.custom_parameter is not None:
|
|
156
|
+
ext_auth_param["parameterType"] = "CustomParameter"
|
|
157
|
+
ext_auth_param["parameterName"] = self.custom_parameter.name
|
|
158
|
+
ext_auth_param["parameterValue"] = self.custom_parameter.value
|
|
159
|
+
if self.custom_parameter.sequence_number is not None:
|
|
160
|
+
ext_auth_param["sequenceNumber"] = self.custom_parameter.sequence_number
|
|
161
|
+
|
|
162
|
+
if self.identity_provider_option is not None:
|
|
163
|
+
ext_auth_param["parameterType"] = "IdentityProviderOptions"
|
|
164
|
+
ext_auth_param["parameterName"] = self.identity_provider_option.name
|
|
165
|
+
ext_auth_param["parameterValue"] = self.identity_provider_option.value
|
|
166
|
+
|
|
167
|
+
return ext_auth_param
|
|
168
|
+
|
|
169
|
+
def get_credential(self, ext_auth_identity_provider_full_name: str):
|
|
170
|
+
"""Get the credential to update"""
|
|
171
|
+
if self.credential is None:
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
"credentials": [
|
|
176
|
+
{
|
|
177
|
+
"credentialName": "clientId",
|
|
178
|
+
"credentialValue": self.credential.client_id,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"credentialName": "clientSecret",
|
|
182
|
+
"credentialValue": self.credential.client_secret,
|
|
183
|
+
},
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class TransformExternalAuthIdentityProviderParameter(
|
|
189
|
+
ExternalAuthIdentityProviderParameter
|
|
190
|
+
):
|
|
191
|
+
"""Transform External Auth Identity Provider Parameter with environment variable support"""
|
|
192
|
+
|
|
193
|
+
def get_external_auth_identity_provider_parameter(self):
|
|
194
|
+
ret = super().get_external_auth_identity_provider_parameter()
|
|
195
|
+
if ret.get("parameterValue", None) is not None:
|
|
196
|
+
ret["parameterValue"] = os.getenv(
|
|
197
|
+
ret.get("parameterValue"), ret.get("parameterValue")
|
|
198
|
+
)
|
|
199
|
+
return ret
|
|
200
|
+
|
|
201
|
+
def get_credential(self, ext_auth_identity_provider_full_name: str):
|
|
202
|
+
"""Get the credential with values from environment variables"""
|
|
203
|
+
value = super().get_credential(ext_auth_identity_provider_full_name)
|
|
204
|
+
|
|
205
|
+
if value is None:
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
for credential in value["credentials"]:
|
|
209
|
+
if credential["credentialValue"]:
|
|
210
|
+
credential["credentialValue"] = os.getenv(
|
|
211
|
+
credential["credentialValue"], credential["credentialValue"]
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return value
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
ExternalAuthIdentityProviderParameter.update_forward_refs()
|
|
218
|
+
TransformExternalAuthIdentityProviderParameter.update_forward_refs()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class UpdateExternalAuthIdentityProvider(BaseSalesforceApiTask):
|
|
222
|
+
"""Custom task to update external auth identity provider parameters.
|
|
223
|
+
This task updates External Auth Identity Provider parameters and credentials using
|
|
224
|
+
the Tooling API and Connect API.
|
|
225
|
+
|
|
226
|
+
Reference:
|
|
227
|
+
- https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/tooling_api_objects_externalauthidentityprovider.htm
|
|
228
|
+
- https://developer.salesforce.com/docs/atlas.en-us.chatterapi.meta/chatterapi/connect_resources_named_credentials_external_auth_identity_provider_credentials.htm
|
|
229
|
+
"""
|
|
230
|
+
|
|
231
|
+
class Options(CCIOptions):
|
|
232
|
+
name: str = Field(
|
|
233
|
+
..., description="Name of the external auth identity provider to update."
|
|
234
|
+
)
|
|
235
|
+
namespace: str = Field(
|
|
236
|
+
"",
|
|
237
|
+
description="Namespace of the external auth identity provider to update. [default to empty string]",
|
|
238
|
+
)
|
|
239
|
+
# External auth identity provider parameters
|
|
240
|
+
parameters: List[ExternalAuthIdentityProviderParameter] = Field(
|
|
241
|
+
[],
|
|
242
|
+
description="Parameters to update. [default to empty list]",
|
|
243
|
+
)
|
|
244
|
+
# Transform parameters (from environment variables)
|
|
245
|
+
transform_parameters: List[
|
|
246
|
+
TransformExternalAuthIdentityProviderParameter
|
|
247
|
+
] = Field(
|
|
248
|
+
[],
|
|
249
|
+
description="Parameters to transform from environment variables. [default to empty list]",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
parsed_options: Options
|
|
253
|
+
|
|
254
|
+
def _init_task(self):
|
|
255
|
+
self.tooling = get_simple_salesforce_connection(
|
|
256
|
+
self.project_config,
|
|
257
|
+
self.org_config,
|
|
258
|
+
api_version=self.project_config.project__package__api_version,
|
|
259
|
+
base_url="tooling",
|
|
260
|
+
)
|
|
261
|
+
self.connect = get_simple_salesforce_connection(
|
|
262
|
+
self.project_config,
|
|
263
|
+
self.org_config,
|
|
264
|
+
api_version=self.project_config.project__package__api_version,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
def _run_task(self):
|
|
268
|
+
# Step 1: Get the external auth identity provider id from the name
|
|
269
|
+
ext_auth_id = self._get_external_auth_identity_provider_id()
|
|
270
|
+
|
|
271
|
+
if not ext_auth_id:
|
|
272
|
+
msg = f"External auth identity provider '{self.parsed_options.name}' not found"
|
|
273
|
+
raise SalesforceDXException(msg)
|
|
274
|
+
|
|
275
|
+
# Step 2: Get the external auth identity provider object
|
|
276
|
+
ext_auth_provider = self._get_external_auth_identity_provider_object(
|
|
277
|
+
ext_auth_id
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if not ext_auth_provider:
|
|
281
|
+
msg = f"Failed to retrieve external auth identity provider object for '{self.parsed_options.name}'"
|
|
282
|
+
raise SalesforceDXException(msg)
|
|
283
|
+
|
|
284
|
+
# Step 3: Update the external auth identity provider parameters
|
|
285
|
+
self._update_external_auth_identity_provider_parameters(ext_auth_provider)
|
|
286
|
+
|
|
287
|
+
updated_ext_auth_provider = self._update_external_auth_identity_provider_object(
|
|
288
|
+
ext_auth_id, ext_auth_provider
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
if not updated_ext_auth_provider:
|
|
292
|
+
msg = f"Failed to update external auth identity provider object for '{self.parsed_options.name}'"
|
|
293
|
+
raise SalesforceDXException(msg)
|
|
294
|
+
|
|
295
|
+
# Step 4: Update credentials if specified
|
|
296
|
+
response = self._update_credential()
|
|
297
|
+
|
|
298
|
+
if not response:
|
|
299
|
+
raise SalesforceDXException(
|
|
300
|
+
f"Failed to update credentials for external auth identity provider '{self.parsed_options.name}'"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
self.logger.info(
|
|
304
|
+
f"Successfully updated external auth identity provider '{self.parsed_options.name}'"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def _get_external_auth_identity_provider_id(self) -> Optional[str]:
|
|
308
|
+
"""Get the external auth identity provider ID from the name"""
|
|
309
|
+
query = f"SELECT Id FROM ExternalAuthIdentityProvider WHERE DeveloperName='{self.parsed_options.name}'"
|
|
310
|
+
|
|
311
|
+
if self.parsed_options.namespace:
|
|
312
|
+
query += f" AND NamespacePrefix='{self.parsed_options.namespace}'"
|
|
313
|
+
|
|
314
|
+
query += " LIMIT 1"
|
|
315
|
+
|
|
316
|
+
try:
|
|
317
|
+
res = self.tooling.query(query)
|
|
318
|
+
if res["size"] == 0:
|
|
319
|
+
return None
|
|
320
|
+
return res["records"][0]["Id"]
|
|
321
|
+
except Exception as e:
|
|
322
|
+
self.logger.error(
|
|
323
|
+
f"Error querying external auth identity provider: {str(e)}"
|
|
324
|
+
)
|
|
325
|
+
return None
|
|
326
|
+
|
|
327
|
+
def _get_external_auth_identity_provider_object(
|
|
328
|
+
self, ext_auth_id: str
|
|
329
|
+
) -> Optional[Dict[str, Any]]:
|
|
330
|
+
"""Get the external auth identity provider object using Tooling API"""
|
|
331
|
+
try:
|
|
332
|
+
# Use Tooling API to get the external auth identity provider metadata
|
|
333
|
+
result = self.tooling._call_salesforce(
|
|
334
|
+
method="GET",
|
|
335
|
+
url=f"{self.tooling.base_url}sobjects/ExternalAuthIdentityProvider/{ext_auth_id}",
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
if result.status_code != 200:
|
|
339
|
+
self.logger.error(
|
|
340
|
+
f"Error retrieving external auth identity provider object: {result.json()}"
|
|
341
|
+
)
|
|
342
|
+
return None
|
|
343
|
+
|
|
344
|
+
return result.json().get("Metadata", None)
|
|
345
|
+
|
|
346
|
+
except Exception as e:
|
|
347
|
+
self.logger.error(
|
|
348
|
+
f"Error retrieving external auth identity provider object: {str(e)}"
|
|
349
|
+
)
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
def _update_external_auth_identity_provider_object(
|
|
353
|
+
self, ext_auth_id: str, ext_auth_provider: Dict[str, Any]
|
|
354
|
+
) -> Optional[bool]:
|
|
355
|
+
"""Update the external auth identity provider object"""
|
|
356
|
+
try:
|
|
357
|
+
result_update = self.tooling._call_salesforce(
|
|
358
|
+
method="PATCH",
|
|
359
|
+
url=f"{self.tooling.base_url}sobjects/ExternalAuthIdentityProvider/{ext_auth_id}",
|
|
360
|
+
json={"Metadata": ext_auth_provider},
|
|
361
|
+
)
|
|
362
|
+
if not result_update.ok:
|
|
363
|
+
self.logger.error(
|
|
364
|
+
f"Error updating external auth identity provider object: {result_update.json()}"
|
|
365
|
+
)
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
return result_update.ok
|
|
369
|
+
except Exception as e:
|
|
370
|
+
self.logger.error(
|
|
371
|
+
f"Error updating external auth identity provider object: {str(e)}"
|
|
372
|
+
)
|
|
373
|
+
return None
|
|
374
|
+
|
|
375
|
+
def _update_external_auth_identity_provider_parameters(
|
|
376
|
+
self, ext_auth_provider: Dict[str, Any]
|
|
377
|
+
):
|
|
378
|
+
"""Update the external auth identity provider parameters"""
|
|
379
|
+
try:
|
|
380
|
+
# Get template parameter for new parameters
|
|
381
|
+
template_param = (
|
|
382
|
+
self._get_external_auth_identity_provider_template_parameter()
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Update regular parameters
|
|
386
|
+
self._update_parameters(
|
|
387
|
+
ext_auth_provider, self.parsed_options.parameters, template_param
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
# Update transform parameters (from environment variables)
|
|
391
|
+
self._update_parameters(
|
|
392
|
+
ext_auth_provider,
|
|
393
|
+
self.parsed_options.transform_parameters,
|
|
394
|
+
template_param,
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
except Exception as e:
|
|
398
|
+
raise SalesforceDXException(f"Failed to update parameters: {str(e)}")
|
|
399
|
+
|
|
400
|
+
def _update_parameters(
|
|
401
|
+
self,
|
|
402
|
+
ext_auth_provider: Dict[str, Any],
|
|
403
|
+
ext_auth_parameters: List[ExternalAuthIdentityProviderParameter],
|
|
404
|
+
template_param: Dict[str, Any],
|
|
405
|
+
):
|
|
406
|
+
"""Update the parameters"""
|
|
407
|
+
for param_input in ext_auth_parameters:
|
|
408
|
+
# Skip credential-only updates
|
|
409
|
+
if param_input.credential is not None and all(
|
|
410
|
+
getattr(param_input, field) is None
|
|
411
|
+
for field in [
|
|
412
|
+
"authorize_url",
|
|
413
|
+
"token_url",
|
|
414
|
+
"user_info_url",
|
|
415
|
+
"jwks_url",
|
|
416
|
+
"issuer_url",
|
|
417
|
+
"client_authentication",
|
|
418
|
+
"custom_parameter",
|
|
419
|
+
"identity_provider_option",
|
|
420
|
+
]
|
|
421
|
+
):
|
|
422
|
+
continue
|
|
423
|
+
|
|
424
|
+
param_to_update = (
|
|
425
|
+
param_input.get_external_auth_identity_provider_parameter()
|
|
426
|
+
)
|
|
427
|
+
secret = (
|
|
428
|
+
param_to_update.pop("secret", False)
|
|
429
|
+
if "secret" in param_to_update
|
|
430
|
+
else False
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
# Create a copy for matching (without parameterValue and parameterName)
|
|
434
|
+
param_to_match = {
|
|
435
|
+
k: v
|
|
436
|
+
for k, v in param_to_update.items()
|
|
437
|
+
if k != "parameterValue" and k != "parameterName"
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
# Find existing parameter
|
|
441
|
+
auth_param = next(
|
|
442
|
+
(
|
|
443
|
+
param
|
|
444
|
+
for param in ext_auth_provider.get(
|
|
445
|
+
"externalAuthIdentityProviderParameters", []
|
|
446
|
+
)
|
|
447
|
+
if param_to_match.items() <= param.items()
|
|
448
|
+
),
|
|
449
|
+
None,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
if auth_param:
|
|
453
|
+
# Update existing parameter
|
|
454
|
+
auth_param.update(param_to_update)
|
|
455
|
+
self.logger.info(
|
|
456
|
+
f"Updated parameter {auth_param['parameterType']}"
|
|
457
|
+
+ (
|
|
458
|
+
f"-{auth_param.get('parameterName', 'N/A')}"
|
|
459
|
+
if auth_param.get("parameterName")
|
|
460
|
+
else ""
|
|
461
|
+
)
|
|
462
|
+
+ f" with new value {param_to_update['parameterValue'] if not secret else '********'}"
|
|
463
|
+
)
|
|
464
|
+
else:
|
|
465
|
+
# Add new parameter
|
|
466
|
+
copy_template_param = template_param.copy()
|
|
467
|
+
copy_template_param.update(param_to_update)
|
|
468
|
+
if "externalAuthIdentityProviderParameters" not in ext_auth_provider:
|
|
469
|
+
ext_auth_provider["externalAuthIdentityProviderParameters"] = []
|
|
470
|
+
ext_auth_provider["externalAuthIdentityProviderParameters"].append(
|
|
471
|
+
copy_template_param
|
|
472
|
+
)
|
|
473
|
+
self.logger.info(
|
|
474
|
+
f"Added parameter {copy_template_param['parameterType']}"
|
|
475
|
+
+ (
|
|
476
|
+
f"-{copy_template_param.get('parameterName', 'N/A')}"
|
|
477
|
+
if copy_template_param.get("parameterName")
|
|
478
|
+
else ""
|
|
479
|
+
)
|
|
480
|
+
+ f" with new value {param_to_update['parameterValue'] if not secret else '********'}"
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
def _get_external_auth_identity_provider_template_parameter(
|
|
484
|
+
self,
|
|
485
|
+
) -> Dict[str, Any]:
|
|
486
|
+
"""Get the external auth identity provider template parameter"""
|
|
487
|
+
return {
|
|
488
|
+
"description": None,
|
|
489
|
+
"parameterName": None,
|
|
490
|
+
"parameterType": None,
|
|
491
|
+
"parameterValue": None,
|
|
492
|
+
"sequenceNumber": None,
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
def _update_credential(self):
|
|
496
|
+
"""Update the credential using Connect API"""
|
|
497
|
+
for param in (
|
|
498
|
+
self.parsed_options.parameters + self.parsed_options.transform_parameters
|
|
499
|
+
):
|
|
500
|
+
if param.credential is None or (
|
|
501
|
+
param.credential.client_secret is None
|
|
502
|
+
and param.credential.client_id is None
|
|
503
|
+
):
|
|
504
|
+
continue
|
|
505
|
+
|
|
506
|
+
namespace = (
|
|
507
|
+
f"{self.parsed_options.namespace}__"
|
|
508
|
+
if self.parsed_options.namespace
|
|
509
|
+
else ""
|
|
510
|
+
)
|
|
511
|
+
ext_auth_full_name = f"{namespace}{self.parsed_options.name}"
|
|
512
|
+
|
|
513
|
+
self.logger.info(
|
|
514
|
+
f"Managing credential for external auth identity provider {self.parsed_options.name}..."
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
# Get current credential
|
|
518
|
+
credential_response = self.connect._call_salesforce(
|
|
519
|
+
method="GET",
|
|
520
|
+
url=f"{self.connect.base_url}named-credentials/external-auth-identity-provider-credentials/{ext_auth_full_name}",
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
if not credential_response.ok:
|
|
524
|
+
msg = f"Failed to retrieve credential for {self.parsed_options.name}: {credential_response.json()}"
|
|
525
|
+
raise SalesforceDXException(msg)
|
|
526
|
+
|
|
527
|
+
credential = credential_response.json()
|
|
528
|
+
http_verb = "PUT" if credential.get("credentials") else "POST"
|
|
529
|
+
|
|
530
|
+
# Update credential data
|
|
531
|
+
credential_data = param.get_credential(ext_auth_full_name)
|
|
532
|
+
credential["credentials"] = credential_data["credentials"]
|
|
533
|
+
|
|
534
|
+
# Update credential via Connect API
|
|
535
|
+
try:
|
|
536
|
+
response = self.connect._call_salesforce(
|
|
537
|
+
method=http_verb,
|
|
538
|
+
url=f"{self.connect.base_url}named-credentials/external-auth-identity-provider-credentials/{ext_auth_full_name}",
|
|
539
|
+
json=credential,
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
return response.ok
|
|
543
|
+
|
|
544
|
+
except Exception as e:
|
|
545
|
+
self.logger.error(
|
|
546
|
+
f"Error updating credential for {self.parsed_options.name}: {str(e)}"
|
|
547
|
+
)
|
|
548
|
+
return False
|
|
549
|
+
|
|
550
|
+
# Return True if no credentials to update or all updates succeeded
|
|
551
|
+
return True
|