{"record":{"id":"e4b7444145caf05c","repo":"apache/pulsar","slug":"unauthorized-to-validatebothsuperuserandclusterope","errorCode":null,"errorMessage":"Unauthorized to validateBothSuperuserAndClusterOperation for originalPrincipal [${principal}] and clientAppId [${clientAppId}] about operation [${operation}] on cluster [${cluster}]","messagePattern":"Unauthorized to validateBothSuperuserAndClusterOperation for originalPrincipal \\[(.+?)\\] and clientAppId \\[(.+?)\\] about operation \\[(.+?)\\] on cluster \\[(.+?)\\]","errorType":"http","errorClass":"RestException","httpStatus":401,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java","lineNumber":1253,"sourceCode":"                    Throwable superUserValidationException = null;\n                    try {\n                        superUserAccessValidation.join();\n                    } catch (Throwable ex) {\n                        superUserValidationException = FutureUtil.unwrapCompletionException(ex);\n                    }\n                    Throwable clusterOperationValidationException = null;\n                    try {\n                        clusterOperationValidation.join();\n                    } catch (Throwable ex) {\n                        clusterOperationValidationException = FutureUtil.unwrapCompletionException(ex);\n                    }\n                    log.debug().attr(\"originalPrincipal\", originalPrincipal())\n                            .attr(\"operation\", operation.toString())\n                            .attr(\"cluster\", clusterName)\n                            .attr(\"superuserValidationError\", superUserValidationException)\n                            .attr(\"clusterOperationValidationError\", clusterOperationValidationException)\n                            .log(\"validateBothSuperuserAndClusterOperation failed\");\n                    throw new RestException(Status.UNAUTHORIZED,\n                            String.format(\"Unauthorized to validateBothSuperuserAndClusterOperation for\"\n                                          + \" originalPrincipal [%s] and clientAppId [%s] \"\n                                          + \"about operation [%s] on cluster [%s]\",\n                                    originalPrincipal(), clientAppId(), operation.toString(), clusterName));\n                });\n    }\n\n    private CompletableFuture<Void> validateBothSuperuserAndClusterPolicyOperation(String clusterName, PolicyName name,\n                                                                                   PolicyOperation operation) {\n        final var superUserAccessValidation = validateSuperUserAccessAsync();\n        final var clusterOperationValidation = validateClusterPolicyOperationAsync(clusterName, name, operation);\n        return FutureUtil.waitForAll(List.of(superUserAccessValidation, clusterOperationValidation))\n                .handle((result, err) -> {\n                    if (!superUserAccessValidation.isCompletedExceptionally()\n                        || !clusterOperationValidation.isCompletedExceptionally()) {\n                        return null;\n                    }\n                    Throwable superUserValidationException = null;","sourceCodeStart":1235,"sourceCodeEnd":1271,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java#L1235-L1271","documentation":"HTTP 401 (UNAUTHORIZED) thrown by validateBothSuperuserAndClusterOperation when the caller fails both authorization paths required for a cluster admin operation: the originalPrincipal is not a superuser AND fails the cluster-level operation permission check. Used by basic cluster CRUD endpoints (get/create/update/delete cluster, peer cluster management). The message includes principal, clientAppId, operation, and cluster for diagnosis.","triggerScenarios":"Any of GET/POST/PUT/DELETE /admin/v3/clusters/{cluster} (or /peers) executed with credentials whose role is neither a superuser nor granted the cluster operation (e.g. admin/produce/consume per policy); expired or wrong client token; proxy-forwarded request where originalPrincipal lost superuser rights.","commonSituations":"Tokens minted for a tenant-level role used for cluster administration; superuser roles edited out of broker config (superUserRoles); missing originalPrincipal authentication data when going through a proxy; version changes in authorization providers.","solutions":["Authenticate with a role listed in superUserRoles in broker.conf, or grant the role the required cluster operation permission.","Verify the client token/credentials are valid, unexpired, and map to the intended role (check clientAppId in the error message).","If calling through a proxy, ensure the originalPrincipal is correctly forwarded and authorized on the broker.","For automation, provision a dedicated admin role with the minimal required cluster permissions instead of reusing user tokens."],"exampleFix":"// before\nPulsarAdmin admin = PulsarAdmin.builder().serviceHttpUrl(url)\n        .authentication(AuthenticationFactory.token(userToken)).build();\nadmin.clusters().getCluster(\"c1\"); // 401\n// after\nPulsarAdmin admin = PulsarAdmin.builder().serviceHttpUrl(url)\n        .authentication(AuthenticationFactory.token(adminToken)).build(); // token role in superUserRoles\nadmin.clusters().getCluster(\"c1\");","handlingStrategy":"validation","validationCode":"// client-side sanity check before calling cluster admin APIs\nString role = currentTokenRole(); // however roles are derived from your token\nif (!superUserRoles.contains(role) && !hasClusterPermission(role, cluster, operation)) {\n    throw new SecurityException(\"Role \" + role + \" lacks cluster \" + operation + \" on \" + cluster);\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.clusters().createCluster(cluster, clusterData);\n} catch (PulsarAdminException.NotAuthorizedException e) {\n    // principal/clientAppId in message lacks superuser and cluster permission:\n    // switch to an authorized admin credential\n}","preventionTips":["Use dedicated admin credentials (role in superUserRoles) for cluster administration.","Confirm token role and expiry before running admin automation.","When proxying, preserve originalPrincipal and ensure it is authorized on the broker.","Grant minimal required cluster permissions to automation roles instead of sharing user tokens."],"tags":["authorization","unauthorized","superuser","cluster-admin","security"],"backgroundTag":"insufficient-permissions","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}