Files
compute/routers/authz_target_test.go
zeekay ecbde5f17c Read the target from the address, in one place
visor is moving from /v1/get-asset?id=acme/db-1 to /v1/assets/acme/db-1, and
the move cannot be made safely until this lands.

MIDDLEWARE HAS NO ROUTE PARAMETERS. Measured against the framework: a handler
on /v1/assets/:owner/:name reads owner="acme" while the same request read in
app.Use middleware reads "". Authorization runs as middleware, so a
path-addressed resource authorized through c.Param decides on an EMPTY owner —
which is a different tenant, not an error. It refuses callers the query
spelling admitted, and the handler's own tests still pass because the handler
never runs.

So the seam reads the ADDRESS, which is the one thing both halves agree on.
pathTarget is a total grammar over it:

    /v1/{kind}                      -> "",    ""
    /v1/{kind}/{owner}              -> owner, ""
    /v1/{kind}/{owner}/{name}       -> owner, name
    /v1/{kind}/{owner}/{name}/{sub} -> owner, name

That is why every owned resource is addressed the same way: a second shape
needs a table here, and a table is a thing to keep in agreement with the
router.

Handlers get the same treatment. ApiController.Id reads the path first and the
query second, and the sixteen sites that read c.Ctx.Query("id") now read it.
The query value passes through VERBATIM: not every id is owner/name — a node
pool is addressed by bare name — so parsing and rebuilding here would turn an
id the service accepts into one it does not. That is not hypothetical; it took
the node-pool tests down until the round-trip came out.

11 packages green, vet and gofmt clean.
2026-08-22 11:39:56 -07:00

47 lines
1.7 KiB
Go

// Copyright 2023 Hanzo Industries Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package routers
import "testing"
// THE ADDRESS NAMES THE TARGET, AND THE SEAM READS IT FROM THE ADDRESS.
//
// This is the fact the whole resource surface rests on. Authorization runs as
// middleware; middleware has no route parameters. A path-addressed resource
// whose seam reads c.Param therefore decides on an empty owner and REFUSES a
// caller the query spelling admitted — silently, because the handler never runs
// and its own tests still pass.
func TestPathTarget(t *testing.T) {
for _, c := range []struct{ path, owner, name string }{
// The grammar, in full.
{"/v1/assets", "", ""},
{"/v1/assets/acme", "acme", ""},
{"/v1/assets/acme/db-1", "acme", "db-1"},
{"/v1/volumes/acme/vol-1/attachment", "acme", "vol-1"},
{"/v1/sessions/acme/s-9/state", "acme", "s-9"},
// Not a target: no kind, or not this version.
{"/v1", "", ""},
{"/health", "", ""},
{"/v2/assets/acme/db-1", "", ""},
// Trailing slash is the same address.
{"/v1/assets/acme/db-1/", "acme", "db-1"},
} {
o, n := pathTarget(c.path)
if o != c.owner || n != c.name {
t.Errorf("%s -> (%q,%q), want (%q,%q)", c.path, o, n, c.owner, c.name)
}
}
}