Documentation
¶
Index ¶
- func FindMatch[T any](in []T, pred func(T) bool) (T, bool)
- func Preload(database *gorm.DB, filts map[string]bool) *gorm.DB
- func PreloadAll(database *gorm.DB, names ...string) *gorm.DB
- func QueryToPBs[O pbConvertible[P], P any](ctx context.Context, query *gorm.DB, single bool) ([]*P, error)
- func ScopeByHost(query *gorm.DB, joinTable, objectFK, hostFK string, hostIDs *gorm.DB) *gorm.DB
- func ScopeBySource(query *gorm.DB, joinTable, objectFK, tool string) *gorm.DB
- func ToPBs[O pbConvertible[P], P any](ctx context.Context, in []O) ([]*P, error)
- func WrapDBError(err error) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func FindMatch ¶
FindMatch returns the first element of in for which pred is true, and whether one was found. It is the generic form of the per-domain "find the matching row in this slice" helpers (identity/absorbable/same-host lookups) so they need not each re-spell the loop.
func Preload ¶
Preload loads a database with the base clause.Associations preload plus every association named with a true value in filts (a false entry is skipped, so callers can gate a preload on a request flag). It is the map-driven form used when the set of associations is conditional; PreloadAll is the plainer variadic form for the unconditional case.
func PreloadAll ¶
PreloadAll loads a database with the base clause.Associations preload plus each named association, unconditionally. It is the single place association preloading is expressed, so the per-domain servers do not each re-implement the clause.Associations + range loop.
func QueryToPBs ¶ added in v0.3.0
func QueryToPBs[O pbConvertible[P], P any](ctx context.Context, query *gorm.DB, single bool) ([]*P, error)
QueryToPBs runs a built query and returns the matching rows as their protobuf twins. It captures the tail every domain server's Read/List repeats verbatim: run First (single==true) or Find (single==false), treat gorm.ErrRecordNotFound as an empty result rather than an error (an unmatched filter is a valid "nothing here" answer the caller's len==0 branch renders, not a failure), then convert the ORM rows via ToPBs. The caller still owns everything type-specific — building/scoping/preloading the query and marshalling the typed request/response — so only the identical middle is shared. O is the ORM row type (e.g. *host.HostORM), P its protobuf twin.
func ScopeByHost ¶ added in v0.3.0
ScopeByHost restricts a query to the objects attached to a given host — the second query-scoping axis, orthogonal to (and freely composable with) ScopeBySource: "only MY objects" narrows by the tool that contributed them, "only objects on THIS host" narrows by where they live. Chaining both answers "services contributed by nmap, on 10.0.0.0/24"-style questions in one round trip, and both shrink the result set (and so the wire payload) rather than changing what an object means.
Domains differ in how their objects reach a host, so the join is a parameter rather than baked in: joinTable is a table carrying BOTH a column referencing the queried object (objectFK) and one referencing its host (hostFK) — for services that is `ports` (service_id + host_id). hostIDs is the subquery selecting the host ids to scope to, as returned by host.IDsMatching; nil means the caller sent no host filter and the query is returned untouched, so a request's Host field can be threaded through unconditionally.
Not every domain fits this shape. Credentials reach a host through their provenance sources rather than a two-FK join table (sources.service_id -> ports.host_id), and use the Metasploit-style credential.WhereLoggedInHost scope instead; it is the same axis expressed through that domain's own path, not a second mechanism.
func ScopeBySource ¶
ScopeBySource restricts a query to the objects contributed by the named tool, via that object's provenance m2m join (e.g. "host_sources"/"host_id"). An empty tool is a no-op, so callers can thread a request's Source filter through unconditionally. It is the one-liner wrapper the per-domain servers share instead of each repeating the same Scopes(...) block.
func ToPBs ¶
ToPBs converts a slice of ORM rows to pointers to their protobuf twins, returning the first conversion error rather than swallowing it. It replaces the identical ORM→PB range loop the domain servers each hand-rolled (and which discarded the ToPB error with `pb, _ :=`).
func WrapDBError ¶ added in v0.3.0
WrapDBError maps a raw database (or other server-internal) error to a coded gRPC status, so a client never receives an ungraded/Unknown-coded gorm/sql error off a server/<domain> return site (audit finding R4). It is deliberately a single, simple policy: nil passes through unchanged; an error that already carries a gRPC status (e.g. an InvalidArgument a caller raised earlier in the same method) passes through unchanged too, so WrapDBError is safe to call defensively without risking a double-wrap or downgrading a status a caller deliberately chose; everything else — a failed query, a broken write, an ORM<->PB conversion error — becomes codes.Internal, since it reflects a server-side failure rather than a caller mistake.
gorm.ErrRecordNotFound is deliberately NOT special-cased to codes.NotFound here. The established convention on filtered Read/List paths (see QueryToPBs) is to swallow ErrRecordNotFound internally and return an empty, successful result: an unmatched filter is a valid "nothing here" answer the caller's len==0 branch renders, not a failure. QueryToPBs already implements that swallow, so any error reaching WrapDBError from a Read/List path is a real failure worth a coded status. A call site resolving a *required* row (e.g. a delete/update target that must already exist) may still special-case ErrRecordNotFound to codes.NotFound itself before calling WrapDBError, rather than letting it fall through to codes.Internal.
Types ¶
This section is empty.