{"record":{"id":"5b2e72da13715c82","repo":"apolloconfig/apollo","slug":"metadata-read-permission-is-required-5b2e72","errorCode":null,"errorMessage":"Metadata read permission is required","messagePattern":"Metadata read permission is required","errorType":"exception","errorClass":"AccessDeniedException","httpStatus":403,"severity":"error","filePath":"apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/OrganizationController.java","lineNumber":52,"sourceCode":"  private final OrganizationOpenApiService organizationOpenApiService;\n  private final UnifiedPermissionValidator unifiedPermissionValidator;\n\n  public OrganizationController(OrganizationOpenApiService organizationOpenApiService,\n      UnifiedPermissionValidator unifiedPermissionValidator) {\n    this.organizationOpenApiService = organizationOpenApiService;\n    this.unifiedPermissionValidator = unifiedPermissionValidator;\n  }\n\n  @Override\n  public ResponseEntity<List<OpenOrganizationDto>> getOrganization() {\n    requireMetadataReadPermissionForUserToken();\n    return ResponseEntity.ok(organizationOpenApiService.getOrganizations());\n  }\n\n  private void requireMetadataReadPermissionForUserToken() {\n    if (UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType())\n        && !unifiedPermissionValidator.hasAnyUserTokenOperation(UserTokenOperation.METADATA_READ)) {\n      throw new AccessDeniedException(\"Metadata read permission is required\");\n    }\n  }\n}\n","sourceCodeStart":34,"sourceCodeEnd":56,"githubUrl":"https://github.com/apolloconfig/apollo/blob/d95fc18d112589efc09ddcbe1507047584d55251/apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/OrganizationController.java#L34-L56","documentation":"HTTP 403 (AccessDeniedException). Thrown by OrganizationController.requireMetadataReadPermissionForUserToken: auth type USER_TOKEN and UnifiedPermissionValidator.hasAnyUserTokenOperation(UserTokenOperation.METADATA_READ) is false. The user-token lacks the dedicated METADATA_READ user-token operation grant, so it may not list organizations through the OpenAPI.","triggerScenarios":"GET /openapi/v1/organizations (getOrganization) using a user-token that was not granted the METADATA_READ operation. Portal SSO users (USER) bypass this gate; CONSUMER tokens are handled separately.","commonSituations":"A user-token created for config read/write being reused to fetch org metadata without the METADATA_READ operation; token operations scoped down after auditing; new integration assuming all user-tokens can read metadata.","solutions":["Grant the user-token the METADATA_READ user-token operation and retry.","Use a portal USER session to fetch organizations (USER auth bypasses this gate).","Confirm the token's allowed operations include METADATA_READ before calling.","Avoid calling the org metadata endpoint with a narrowly scoped token."],"exampleFix":"// before: user-token without METADATA_READ\nclient.withUserToken(token).getOrganizations(); // 403\n\n// after: add METADATA_READ op to the token, or use portal USER\nadmin.grantUserTokenOperation(tokenId, UserTokenOperation.METADATA_READ);\nclient.withUserToken(token).getOrganizations();","handlingStrategy":"validation","validationCode":"// For USER_TOKEN: confirm METADATA_READ operation is granted before fetching orgs.\nboolean ok = tokenHasOperation(token, \"METADATA_READ\");\nif (!ok) { /* grant op or use portal USER; do not call getOrganization */ }","typeGuard":"null","tryCatchPattern":"try {\n  client.withUserToken(token).getOrganizations();\n} catch (HttpServerErrorException.Forbidden e) {\n  // Metadata read permission is required -> add METADATA_READ op or use USER\n}","preventionTips":["Grant the METADATA_READ user-token operation to tokens that read org metadata.","Use a portal USER session for ad-hoc metadata queries.","Audit user-token operations when reusing tokens for new endpoints."],"tags":["apollo-portal","openapi","permission","access-denied","organization","metadata","user-token"],"backgroundTag":null,"analyzedSha":"d95fc18d112589efc09ddcbe1507047584d55251","analyzedAt":"2026-08-14T04:00:05.477Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}