Files
base/migrations/1790000000_auth_org_authority.go
zeekay 1575be07b1 A bound filter value stays a value, and org authority holds only in the org's Base
Filter params were pasted in one at a time, so a value spelling another
param's placeholder was rewritten inside its own quotes and the second
value's quotes closed the first one's string: a bound value could add
conditions to the filter. They are now substituted in one pass over the
filter as written.

An org admin administers, and a member reads the schema of, only a Base
opened for its org (StoreKeyTenant): a Base deployed for no org belongs to
whoever deployed it. The org plugin names the org of every Base it opens.

A saved user's record never carries org authority: @request.auth.admin and
@request.auth.role answer only for the record a verified token mints, and
the two fields are system and hidden.

The emptiness figures of an aggregate are columns of its one query, so a
grouped request counts the groups it shows and runs one query however
many figures it asks for.

A stream the host routed to its org's Base is served by that live Base,
not the instance its grant was minted on.
2026-10-18 22:21:27 -07:00

55 lines
1.7 KiB
Go

package migrations
import (
"database/sql"
"errors"
"github.com/hanzoai/base/core"
)
// The caller's authority in the org it acts in, on the auth record, so a rule
// can ask for it: `@request.auth.admin = true` (it administers the org) and
// `@request.auth.role = "owner"` (its role there: owner, admin or member).
//
// resolveJWKSToken stamps both from the verified token onto the unsaved record
// it mirrors a person into, and the rule engine reads them off that record. They
// are schema fields because a record carries only the fields its collection
// declares. They are system and hidden, and a saved user's own values are never
// read: the resolver answers them only for the record a verified token mints.
// Superusers are skipped: rules never stop them.
func init() {
core.SystemMigrations.Register(func(txApp core.App) error {
users, err := txApp.FindCollectionByNameOrId("users")
if errors.Is(err, sql.ErrNoRows) {
return nil
}
if err != nil {
return err
}
changed := false
if users.Fields.GetByName(core.FieldNameAuthAdmin) == nil {
users.Fields.Add(&core.BoolField{Name: core.FieldNameAuthAdmin, System: true, Hidden: true})
changed = true
}
if users.Fields.GetByName(core.FieldNameAuthRole) == nil {
users.Fields.Add(&core.TextField{Name: core.FieldNameAuthRole, Max: 32, System: true, Hidden: true})
changed = true
}
if !changed {
return nil
}
return txApp.Save(users)
}, func(txApp core.App) error {
users, err := txApp.FindCollectionByNameOrId("users")
if err != nil {
return nil
}
users.Fields.RemoveByName(core.FieldNameAuthAdmin)
users.Fields.RemoveByName(core.FieldNameAuthRole)
// they are system fields, which a validated save refuses to drop
return txApp.SaveNoValidate(users)
})
}