sadpandajoe commented on code in PR #45870:
URL: https://github.com/apache/superset/pull/45870#discussion_r4450408493


##########
superset/commands/database/importers/v1/utils.py:
##########
@@ -96,6 +83,46 @@ def _refuse_stored_secret_reuse(existing: Database, config: 
dict[str, Any]) -> N
         )
 
 
+def _refuse_incomplete_embedded_credentials(
+    database_name: str,
+    encrypted_extra: dict[str, Any],
+    engine_spec: type[BaseEngineSpec],
+) -> None:
+    """
+    Refuse a half-filled embedded guest credential, as the dialog and API do.
+
+    Only the secrets that arrive masked are revealed from the stored 
credential,
+    so one the file leaves out entirely (e.g. a key-pair credential without its
+    private key) would replace the stored credential with one no embedded
+    viewer can log in with.
+    """
+    if not (
+        is_feature_enabled("EMBEDDED_CREDENTIAL_FALLBACK")
+        and is_feature_enabled("EMBEDDED_SUPERSET")
+    ):
+        return
+
+    credentials = encrypted_extra.get(EMBEDDED_CREDENTIALS_KEY)
+    if not embedded_credentials_incomplete(

Review Comment:
   A bundle whose `embedded_credentials` is a JSON string, e.g. 
`"{\"username\":\"svc\",\"password\":\"pw\"}"`, passes this check because 
`accept_json_string` defaults to `True`, but the importer stores the string 
as-is. Query time then rejects a non-dict value, so embedded viewers get the 
missing-credential refusal and an overwrite replaces a working credential with 
one that can't be used. The save schema passes `accept_json_string=False` for 
this reason. Should the import path do the same?



##########
superset/commands/database/importers/v1/utils.py:
##########
@@ -186,10 +214,40 @@ def import_database(  # noqa: C901
                 new_config,
                 sensitive_fields,
             )
+            # A database export carries all of Secure Extra, so what it omits 
is
+            # removed. Anything else keeps what it does not carry.
+            if partial_secure_extra:
+                revealed = {**old_config, **revealed}
             config["encrypted_extra"] = json.dumps(revealed)
         else:
             config["encrypted_extra"] = masked_encrypted_extra
 
+    if encrypted_extra := config.get("encrypted_extra"):
+        # The incoming URI decides the engine: an overwrite can change it, and 
the
+        # stored engine's rules would miss the new engine's secrets and fields.
+        engine_spec = Database.get_db_engine_spec(
+            make_url_safe(config["sqlalchemy_uri"])
+        )
+        payload = json.loads(encrypted_extra)
+
+        # Never store a mask as if it were the secret: the schema asks for any
+        # value that cannot be revealed, so one left here is a bug, not input.
+        if masked_encrypted_extra and (

Review Comment:
   This still-masked check only runs when the bundle carried 
`masked_encrypted_extra`. A file using the plain `encrypted_extra` key skips it 
(the import schema has no rename like the API schemas), and 
`_refuse_incomplete_embedded_credentials` counts `PASSWORD_MASK` as a present 
password. Database export writes plain `encrypted_extra` when the stored value 
already equals its masked form, so exporting such a connection and importing it 
with both flags on would store `XXXXXXXXXX` as the guest password. Should the 
mask check also run for the plain `encrypted_extra` path?



##########
superset-frontend/src/features/databases/DatabaseModal/index.tsx:
##########
@@ -553,11 +561,26 @@ export function dbReducer(
       return {
         ...trimmedState,
       };
-    case ActionType.EditorChange:
-      return {
+    case ActionType.EditorChange: {
+      const edited = {
         ...trimmedState,
         [action.payload.name]: action.payload.json,
       };
+      if (
+        action.payload.name === 'masked_encrypted_extra' &&
+        edited.parameters
+      ) {
+        // `onSave` copies these typed blocks over Secure Extra, which would 
undo
+        // this later edit of them.
+        const {
+          embedded_credentials: _typedCredentials,

Review Comment:
   Dropping `oauth2_client_info` from `parameters` on any Secure Extra edit 
loses OAuth2 values that only live there. `OAuth2ClientField.handleChange` only 
calls `onParametersChange`, so a typed client ID never reaches 
`masked_encrypted_extra`. Changing the ID to `B` in the OAuth2 sub-form and 
then typing in the Secure Extra editor resets the inputs to the stored `A`, and 
Save keeps `A`. Should only `embedded_credentials` be stripped here, or should 
the OAuth2 field also copy into Secure Extra?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to