Skip to content

fix(enforcer): send checkAllTenants payload and auth header correctly (PER-15318)#134

Draft
zeevmoney wants to merge 1 commit into
mainfrom
per-15318/fix-checkalltenants-request
Draft

fix(enforcer): send checkAllTenants payload and auth header correctly (PER-15318)#134
zeevmoney wants to merge 1 commit into
mainfrom
per-15318/fix-checkalltenants-request

Conversation

@zeevmoney

Copy link
Copy Markdown
Contributor
  • What kind of change does this PR introduce?

    Bug fix (SDK source). Standalone — based on main, independent of the test-modernization stack (ci: pin actions to SHA and run full test suite on PRs #131test: comprehensive unit + e2e coverage across SDK (stacked on #132) #133). Linear: PER-15318.

  • What is the current behavior?

    Enforcer.checkAllTenants builds its request incorrectly:

    this.client.post('/allowed/all-tenants', {
      headers: { Authorization: `Bearer ${token}`, 'X-Permit-Sdk-Language': sdk },
      params: { user, action, resource, context },
    });

    { headers, params } is passed as the axios POST body (the 2nd arg). Consequences:

    • The Authorization header is never sent as a request header → the PDP cannot authenticate the call.
    • user / action / resource / context are nested under params in the body instead of being the request payload.

    The method effectively cannot work against the PDP.

  • What is the new behavior?

    Mirrors the correct check() mechanics:

    const input = { user: <normalized>, action, resource: <normalized>, context };
    this.client.post('allowed/all-tenants', input, {
      headers: { Authorization: `Bearer ${token}`, 'X-Permit-Sdk-Language': sdk },
      timeout: this.config.timeout,
    });
    • Body is the query payload; headers/timeout are the axios config (3rd) arg, so the auth header is actually sent.
    • user/resource string forms are normalized the same way check() does ('elon'{ key: 'elon' }, 'document:doc-1'{ type: 'document', key: 'doc-1' }).
    • No default-tenant injection — an all-tenants query must not be pinned to a tenant (unlike check(), which injects multiTenancy.defaultTenant). Documented inline.
    • Signature, sdk = 'node' default, return mapping, and the existing try/catch are unchanged. Single-responsibility fix.
  • Other information:

    Tests

    • AVA regression test src/tests/unit/check-all-tenants.spec.ts — mocks the enforcer's PDP axios adapter and asserts: Authorization: Bearer <token> header is sent, body shape is the normalized payload, body contains no headers/params keys (the old bug), no tenant is injected, and the return maps to the tenant objects. Covers string and object input forms.
    • Mocks the axios adapter (no network); yarn build/tsc + yarn lint clean.

    Manual test plan

    1. Point an SDK client at a running PDP. 2. await permit.checkAllTenants('user-1', 'read', 'document:doc-1'). 3. Confirm the PDP receives an authenticated request and returns the allowed tenants (previously 401/empty due to the missing auth header).

    Adjacent issues noted (out of scope, not fixed here)

    • checkAllTenants' try/catch only logs + rethrows (no PermitConnectionError wrapping like check()), and index.ts wraps the call in a second identical try/catch (double-logging on error). Candidates for a follow-up.

    Note on the test stack: PR test: comprehensive unit + e2e coverage across SDK (stacked on #132) #133 adds a Vitest characterization test asserting the current (buggy) body placement (flagged TODO(PER-15318)). Once this fix and that stack both land, that characterization test should be flipped to assert the corrected behavior — happy to do that on the test: comprehensive unit + e2e coverage across SDK (stacked on #132) #133 branch.

Generated with Claude Code

checkAllTenants passed { headers, params } as the axios POST body (2nd
arg), so the Authorization header was never sent and the query was
nested under `params` instead of being the request body — the PDP could
neither authenticate nor read the request.

Mirror check(): send the normalized { user, action, resource, context }
as the body and pass headers/timeout as the axios config arg. Normalize
the string forms of user/resource but skip default-tenant injection,
since an all-tenants query must not be pinned to a tenant. Add an AVA
regression test asserting the auth header is sent, the body shape is
correct, and no tenant is injected.

Fixes PER-15318

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@linear-code

linear-code Bot commented Jun 28, 2026

Copy link
Copy Markdown

PER-15318

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant