Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / internal/platform/orgsettings/policy.go |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | internal/platform/orgsettings/policy.go |
| Original SHA-256 | a405d66d4aa37ab25098aa337a28b8df3d0aad365f3c6793b95df8dbae87dcd0 |
| Git blob SHA | 1c56f293cfd64d77c9dfaa8f5f88e8ae9d237384 |
| Reading-copy SHA-256 | a405d66d4aa37ab25098aa337a28b8df3d0aad365f3c6793b95df8dbae87dcd0 |
Recovered source transcript
Download complete source text ↓92 lines · 4.2 KB
package orgsettings
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/missionworkforce/missionmatch/internal/platform/security"
"github.com/missionworkforce/missionmatch/internal/platform/tenant"
)
// FIELD SENSITIVITY, RESOLVED FOR THE CALLER, AND THE ONE PLACE IT IS ENFORCED.
//
// security.CanSeeField is the pure decision. This is the thing that actually asks it: it reads the
// organization's configured sensitivity, reads the caller's role from the validated session, and hands back
// three plain booleans that a response assembler can act on without knowing anything about roles or settings.
//
// IT EXISTS BECAUSE THE SETTING WAS DECORATIVE. CanSeeField had exactly one caller, GET /org/policy, which
// REPORTS the answers so an interface can hide a column. Nothing consulted it on a data path, so every
// candidate search returned every email address and every placement returned its margin, to every role, no
// matter what the owner had configured. An access control that only exists in the browser is not an access
// control: the answer was one curl away, and the settings screen was making a promise the API did not keep.
//
// ONE RESOLVER RATHER THAN ONE PER MODULE, deliberately. Contacts live in matching and rates live in
// recruiting, and two implementations of the same rule are two chances to get it different. This is the
// seam both call.
// Policy is what THIS caller may see, already decided. It carries no configuration: a consumer needs the
// answer, not the setting, and handing round the setting invites somebody to re derive the answer badly.
type Policy struct {
CanSeeRates bool
CanSeeMargins bool
CanSeeContacts bool
}
// Unrestricted reports that nothing needs hiding, so a caller can skip a redaction walk entirely on the
// common path where an owner or an admin is reading.
func (p Policy) Unrestricted() bool { return p.CanSeeRates && p.CanSeeMargins && p.CanSeeContacts }
// ResolvePolicyTx resolves the caller's policy inside an existing tenant transaction.
//
// A NIL STORE RESOLVES TO THE SHIPPED DEFAULTS, not to permit everything and not to deny everything. Permit
// would reintroduce the hole this file closes. Deny would black out rates and contacts on a node that simply
// was not built with a settings store, which is a working configuration and must not become an empty product.
// The defaults are the same values the migration writes, so a node without a store behaves like a freshly
// provisioned organization, which is the only answer that is both safe and true.
//
// AN UNREADABLE SETTINGS ROW ALSO FALLS TO THE DEFAULTS rather than returning an error, and that is the
// deliberate choice: this is called from inside response assembly, where an error would turn a transient
// database hiccup into a failed page load. Falling to defaults degrades toward the restrictive side, which is
// the direction a failure in an access control is allowed to go.
func (s *Store) ResolvePolicyTx(ctx context.Context, tx pgx.Tx) Policy {
st := Defaults()
if s != nil {
if loaded, err := s.LoadTx(ctx, tx); err == nil {
st = loaded
}
}
return policyFor(ctx, st)
}
// ResolvePolicy resolves the caller's policy in its own tenant transaction, for a caller that does not
// already hold one.
func (s *Store) ResolvePolicy(ctx context.Context) Policy {
st := Defaults()
if s != nil {
if loaded, err := s.Load(ctx); err == nil {
st = loaded
}
}
return policyFor(ctx, st)
}
// policyFor asks the pure decision three times against the caller's role.
//
// NO PRINCIPAL DENIES EVERYTHING. That path should be unreachable, because every route that reaches here is
// behind the session resolver, but "should be unreachable" is not a reason to widen. An unauthenticated
// caller who somehow arrives here sees a record with no rate, no margin and no contact details rather than
// one with all three.
func policyFor(ctx context.Context, st Settings) Policy {
p, ok := tenant.UserFromContext(ctx)
if !ok {
return Policy{}
}
role := security.Role(p.Role)
return Policy{
CanSeeRates: security.CanSeeField(role, st.RateVisibility),
CanSeeMargins: security.CanSeeField(role, st.MarginVisibility),
CanSeeContacts: security.CanSeeField(role, st.ContactVisibility),
}
}