{"record":{"id":"4547093101e31e9c","repo":"apache/superset","slug":"user-doesn-t-have-permission-to-create-or-update-d-454709","errorCode":null,"errorMessage":"User doesn't have permission to create or update databases","messagePattern":"User doesn't have permission to create or update databases","errorType":"exception","errorClass":"ImportFailedError","httpStatus":500,"severity":"error","filePath":"superset/commands/dataset/importers/v0.py","lineNumber":227,"sourceCode":"\n\ndef import_from_dict(data: dict[str, Any], sync: Optional[list[str]] = None) -> None:\n    \"\"\"Imports databases from dictionary\"\"\"\n    if not sync:\n        sync = []\n    if isinstance(data, dict):\n        databases = data.get(DATABASES_KEY, [])\n        # This legacy path creates/updates the embedded database connections.\n        # Mirror the versioned (v1) import commands and require database write\n        # permission for the objects being created here. Only enforced when there\n        # is something to import and a request user is present, so the CLI import\n        # paths keep working.\n        if (\n            databases\n            and get_user()\n            and not security_manager.can_access(\"can_write\", \"Database\")\n        ):\n            raise ImportFailedError(\n                \"User doesn't have permission to create or update databases\"\n            )\n        logger.info(\"Importing %d %s\", len(databases), DATABASES_KEY)\n        for database in databases:\n            db_obj = Database.import_from_dict(database, sync=sync)\n            # ``import_from_dict`` sets fields via setattr, bypassing\n            # ``set_sqlalchemy_uri``.  Call it explicitly so that any plaintext\n            # password in the URI is extracted into the encrypted ``password``\n            # column and replaced with the password mask in ``sqlalchemy_uri``.\n            if db_obj is not None:\n                # Only call set_sqlalchemy_uri when the imported URI carries a real\n                # password (non-empty and not the password mask).  If the URI has no\n                # password segment — common when users keep secrets out of YAML and\n                # rely on the encrypted ``password`` column from a prior run —\n                # calling set_sqlalchemy_uri would set ``password = None`` and break\n                # existing connections.\n                parsed = make_url_safe(db_obj.sqlalchemy_uri)\n                if parsed.password and parsed.password != PASSWORD_MASK:","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/commands/dataset/importers/v0.py#L209-L245","documentation":"ImportFailedError (\"User doesn't have permission to create or update databases\") is raised by the legacy v0 dataset importer when the import bundle contains a `databases` section, a request user is present, and that user lacks the `can_write` permission on Database. The v0 path creates/updates the embedded database connections, so it mirrors the v1 import commands' requirement for database write access. When no user is present (CLI imports), the check is skipped.","triggerScenarios":"Uploading a legacy UI export (v0 format with DATABASES_KEY) to the dataset import endpoint as a non-admin without can_write on Database; a Gamma/Alpha user importing bundles that carry their own database definitions.","commonSituations":"Sharing legacy export files across teams; importing bundles produced by the old UI export into a deployment with stricter RBAC; service accounts without database-write grants used for imports.","solutions":["Grant the importing user `can_write` on Database (or perform the import as Admin).","Strip the `databases` section from the bundle so only datasets are imported — the databases must then already exist on the target.","For automated flows, use the CLI import path, which runs without a request user and is not subject to this check."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Before a UI upload, check permission when the bundle carries databases\nimport yaml\nfrom superset import security_manager\nfrom flask_login import current_user\n\ndef v0_import_allowed(contents: dict) -> bool:\n    carries_databases = any(\n        isinstance(yaml.safe_load(c), dict) and \"databases\" in yaml.safe_load(c)\n        for c in contents.values()\n    )\n    if not carries_databases:\n        return True\n    return security_manager.can_access(\"can_write\", \"Database\")","typeGuard":null,"tryCatchPattern":"from superset.commands.exceptions import ImportFailedError\ntry:\n    ImportDatasetsCommand(contents, {})\nexcept ImportFailedError as ex:\n    if \"permission to create or update databases\" in str(ex):\n        # either drop the databases section or escalate to an admin — retry unchanged is futile\n        strip_databases_and_retry(contents) or escalate_to_admin()","preventionTips":["Import as Admin, or pre-grant can_write on Database to the importing role.","Prefer v1 (versioned) bundles with database references by name instead of embedded definitions.","For automation, use the CLI import path which runs without a request user."],"tags":["import-export","authorization","rbac","legacy-v0","flask-api"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}