fleet/server/fleet/datastore.go

715 lines
37 KiB
Go

package fleet
import (
"context"
"encoding/json"
"errors"
"io"
"time"
"github.com/fleetdm/fleet/v4/server/config"
"github.com/fleetdm/fleet/v4/server/health"
)
type CarveStore interface {
NewCarve(ctx context.Context, metadata *CarveMetadata) (*CarveMetadata, error)
UpdateCarve(ctx context.Context, metadata *CarveMetadata) error
Carve(ctx context.Context, carveId int64) (*CarveMetadata, error)
CarveBySessionId(ctx context.Context, sessionId string) (*CarveMetadata, error)
CarveByName(ctx context.Context, name string) (*CarveMetadata, error)
ListCarves(ctx context.Context, opt CarveListOptions) ([]*CarveMetadata, error)
NewBlock(ctx context.Context, metadata *CarveMetadata, blockId int64, data []byte) error
GetBlock(ctx context.Context, metadata *CarveMetadata, blockId int64) ([]byte, error)
// CleanupCarves will mark carves older than 24 hours expired, and delete the associated data blocks. This behaves
// differently for carves stored in S3 (check the implementation godoc comment for more details)
CleanupCarves(ctx context.Context, now time.Time) (expired int, err error)
}
// InstallerStore is used to communicate to a blob storage containing pre-built
// fleet-osquery installers
type InstallerStore interface {
Get(ctx context.Context, installer Installer) (io.ReadCloser, int64, error)
Put(ctx context.Context, installer Installer) (string, error)
Exists(ctx context.Context, installer Installer) (bool, error)
}
// Datastore combines all the interfaces in the Fleet DAL
type Datastore interface {
health.Checker
CarveStore
///////////////////////////////////////////////////////////////////////////////
// UserStore contains methods for managing users in a datastore
NewUser(ctx context.Context, user *User) (*User, error)
ListUsers(ctx context.Context, opt UserListOptions) ([]*User, error)
UserByEmail(ctx context.Context, email string) (*User, error)
UserByID(ctx context.Context, id uint) (*User, error)
SaveUser(ctx context.Context, user *User) error
SaveUsers(ctx context.Context, users []*User) error
// DeleteUser permanently deletes the user identified by the provided ID.
DeleteUser(ctx context.Context, id uint) error
// PendingEmailChange creates a record with a pending email change for a user identified by uid. The change record
// is keyed by a unique token. The token is emailed to the user with a link that they can use to confirm the change.
PendingEmailChange(ctx context.Context, userID uint, newEmail, token string) error
// ConfirmPendingEmailChange will confirm new email address identified by token is valid. The new email will be
// written to user record. userID is the ID of the user whose e-mail is being changed.
ConfirmPendingEmailChange(ctx context.Context, userID uint, token string) (string, error)
///////////////////////////////////////////////////////////////////////////////
// QueryStore
// ApplyQueries applies a list of queries (likely from a yaml file) to the datastore. Existing queries are updated,
// and new queries are created.
ApplyQueries(ctx context.Context, authorID uint, queries []*Query) error
// NewQuery creates a new query object in thie datastore. The returned query should have the ID updated.
NewQuery(ctx context.Context, query *Query, opts ...OptionalArg) (*Query, error)
// SaveQuery saves changes to an existing query object.
SaveQuery(ctx context.Context, query *Query) error
// DeleteQuery deletes an existing query object.
DeleteQuery(ctx context.Context, name string) error
// DeleteQueries deletes the existing query objects with the provided IDs. The number of deleted queries is returned
// along with any error.
DeleteQueries(ctx context.Context, ids []uint) (uint, error)
// Query returns the query associated with the provided ID. Associated packs should also be loaded.
Query(ctx context.Context, id uint) (*Query, error)
// ListQueries returns a list of queries with the provided sorting and paging options. Associated packs should also
// be loaded.
ListQueries(ctx context.Context, opt ListQueryOptions) ([]*Query, error)
// QueryByName looks up a query by name.
QueryByName(ctx context.Context, name string, opts ...OptionalArg) (*Query, error)
// ObserverCanRunQuery returns whether a user with an observer role is permitted to run the
// identified query
ObserverCanRunQuery(ctx context.Context, queryID uint) (bool, error)
///////////////////////////////////////////////////////////////////////////////
// CampaignStore defines the distributed query campaign related datastore methods
// NewDistributedQueryCampaign creates a new distributed query campaign
NewDistributedQueryCampaign(ctx context.Context, camp *DistributedQueryCampaign) (*DistributedQueryCampaign, error)
// DistributedQueryCampaign loads a distributed query campaign by ID
DistributedQueryCampaign(ctx context.Context, id uint) (*DistributedQueryCampaign, error)
// SaveDistributedQueryCampaign updates an existing distributed query campaign
SaveDistributedQueryCampaign(ctx context.Context, camp *DistributedQueryCampaign) error
// DistributedQueryCampaignTargetIDs gets the IDs of the targets for the query campaign of the provided ID
DistributedQueryCampaignTargetIDs(ctx context.Context, id uint) (targets *HostTargets, err error)
// NewDistributedQueryCampaignTarget adds a new target to an existing distributed query campaign
NewDistributedQueryCampaignTarget(ctx context.Context, target *DistributedQueryCampaignTarget) (*DistributedQueryCampaignTarget, error)
// CleanupDistributedQueryCampaigns will clean and trim metadata for old distributed query campaigns. Any campaign
// in the QueryWaiting state will be moved to QueryComplete after one minute. Any campaign in the QueryRunning state
// will be moved to QueryComplete after one day. Times are from creation time. The now parameter makes this method
// easier to test. The return values indicate how many campaigns were expired and any error.
CleanupDistributedQueryCampaigns(ctx context.Context, now time.Time) (expired uint, err error)
DistributedQueryCampaignsForQuery(ctx context.Context, queryID uint) ([]*DistributedQueryCampaign, error)
///////////////////////////////////////////////////////////////////////////////
// PackStore is the datastore interface for managing query packs.
// ApplyPackSpecs applies a list of PackSpecs to the datastore, creating and updating packs as necessary.
ApplyPackSpecs(ctx context.Context, specs []*PackSpec) error
// GetPackSpecs returns all of the stored PackSpecs.
GetPackSpecs(ctx context.Context) ([]*PackSpec, error)
// GetPackSpec returns the spec for the named pack.
GetPackSpec(ctx context.Context, name string) (*PackSpec, error)
// NewPack creates a new pack in the datastore.
NewPack(ctx context.Context, pack *Pack, opts ...OptionalArg) (*Pack, error)
// SavePack updates an existing pack in the datastore.
SavePack(ctx context.Context, pack *Pack) error
// DeletePack deletes a pack record from the datastore.
DeletePack(ctx context.Context, name string) error
// Pack retrieves a pack from the datastore by ID.
Pack(ctx context.Context, pid uint) (*Pack, error)
// ListPacks lists all packs in the datastore.
ListPacks(ctx context.Context, opt PackListOptions) ([]*Pack, error)
// PackByName fetches pack if it exists, if the pack exists the bool return value is true
PackByName(ctx context.Context, name string, opts ...OptionalArg) (*Pack, bool, error)
// ListPacksForHost lists the packs that a host should execute.
ListPacksForHost(ctx context.Context, hid uint) (packs []*Pack, err error)
// EnsureGlobalPack gets or inserts a pack with type global
EnsureGlobalPack(ctx context.Context) (*Pack, error)
// EnsureTeamPack gets or inserts a pack with type global
EnsureTeamPack(ctx context.Context, teamID uint) (*Pack, error)
///////////////////////////////////////////////////////////////////////////////
// LabelStore
// ApplyLabelSpecs applies a list of LabelSpecs to the datastore, creating and updating labels as necessary.
ApplyLabelSpecs(ctx context.Context, specs []*LabelSpec) error
// GetLabelSpecs returns all of the stored LabelSpecs.
GetLabelSpecs(ctx context.Context) ([]*LabelSpec, error)
// GetLabelSpec returns the spec for the named label.
GetLabelSpec(ctx context.Context, name string) (*LabelSpec, error)
NewLabel(ctx context.Context, Label *Label, opts ...OptionalArg) (*Label, error)
SaveLabel(ctx context.Context, label *Label) (*Label, error)
DeleteLabel(ctx context.Context, name string) error
Label(ctx context.Context, lid uint) (*Label, error)
ListLabels(ctx context.Context, filter TeamFilter, opt ListOptions) ([]*Label, error)
LabelsSummary(ctx context.Context) ([]*LabelSummary, error)
// LabelQueriesForHost returns the label queries that should be executed for the given host.
// Results are returned in a map of label id -> query
LabelQueriesForHost(ctx context.Context, host *Host) (map[string]string, error)
// ListLabelsForHost returns the labels that the given host is in.
ListLabelsForHost(ctx context.Context, hid uint) ([]*Label, error)
// ListHostsInLabel returns a slice of hosts in the label with the given ID.
ListHostsInLabel(ctx context.Context, filter TeamFilter, lid uint, opt HostListOptions) ([]*Host, error)
// ListUniqueHostsInLabels returns a slice of all of the hosts in the given label IDs. A host will only appear once
// in the results even if it is in multiple of the provided labels.
ListUniqueHostsInLabels(ctx context.Context, filter TeamFilter, labels []uint) ([]*Host, error)
SearchLabels(ctx context.Context, filter TeamFilter, query string, omit ...uint) ([]*Label, error)
// LabelIDsByName Retrieve the IDs associated with the given labels
LabelIDsByName(ctx context.Context, labels []string) ([]uint, error)
// Methods used for async processing of host label query results.
AsyncBatchInsertLabelMembership(ctx context.Context, batch [][2]uint) error
AsyncBatchDeleteLabelMembership(ctx context.Context, batch [][2]uint) error
AsyncBatchUpdateLabelTimestamp(ctx context.Context, ids []uint, ts time.Time) error
///////////////////////////////////////////////////////////////////////////////
// HostStore
// NewHost is deprecated and will be removed. Hosts should always be enrolled via EnrollHost.
NewHost(ctx context.Context, host *Host) (*Host, error)
DeleteHost(ctx context.Context, hid uint) error
Host(ctx context.Context, id uint) (*Host, error)
ListHosts(ctx context.Context, filter TeamFilter, opt HostListOptions) ([]*Host, error)
MarkHostsSeen(ctx context.Context, hostIDs []uint, t time.Time) error
SearchHosts(ctx context.Context, filter TeamFilter, query string, omit ...uint) ([]*Host, error)
// EnrolledHostIDs returns the full list of enrolled host IDs.
EnrolledHostIDs(ctx context.Context) ([]uint, error)
CountEnrolledHosts(ctx context.Context) (int, error)
// CleanupIncomingHosts deletes hosts that have enrolled but never updated their status details. This clears dead
// "incoming hosts" that never complete their registration.
// A host is considered incoming if both the hostname and osquery_version fields are empty. This means that multiple
// different osquery queries failed to populate details.
CleanupIncomingHosts(ctx context.Context, now time.Time) ([]uint, error)
// GenerateHostStatusStatistics retrieves the count of online, offline, MIA and new hosts.
GenerateHostStatusStatistics(ctx context.Context, filter TeamFilter, now time.Time, platform *string) (*HostSummary, error)
// HostIDsByName Retrieve the IDs associated with the given hostnames
HostIDsByName(ctx context.Context, filter TeamFilter, hostnames []string) ([]uint, error)
// HostIDsByOSVersion retrieves the IDs of all host matching osVersion
HostIDsByOSVersion(ctx context.Context, osVersion OSVersion, offset int, limit int) ([]uint, error)
// HostByIdentifier returns one host matching the provided identifier. Possible matches can be on
// osquery_host_identifier, node_key, UUID, or hostname.
HostByIdentifier(ctx context.Context, identifier string) (*Host, error)
// AddHostsToTeam adds hosts to an existing team, clearing their team settings if teamID is nil.
AddHostsToTeam(ctx context.Context, teamID *uint, hostIDs []uint) error
TotalAndUnseenHostsSince(ctx context.Context, daysCount int) (total int, unseen int, err error)
// DeleteHosts deletes associated tables for multiple hosts.
//
// It atomically deletes each host but if it returns an error, some of the hosts may be
// deleted and others not.
DeleteHosts(ctx context.Context, ids []uint) error
CountHosts(ctx context.Context, filter TeamFilter, opt HostListOptions) (int, error)
CountHostsInLabel(ctx context.Context, filter TeamFilter, lid uint, opt HostListOptions) (int, error)
ListHostDeviceMapping(ctx context.Context, id uint) ([]*HostDeviceMapping, error)
// ListHostBatteries returns the list of batteries for the given host ID.
ListHostBatteries(ctx context.Context, id uint) ([]*HostBattery, error)
// LoadHostByDeviceAuthToken loads the host identified by the device auth token.
// If the token is invalid it returns a NotFoundError.
LoadHostByDeviceAuthToken(ctx context.Context, authToken string) (*Host, error)
// SetOrUpdateDeviceAuthToken inserts or updates the auth token for a host.
SetOrUpdateDeviceAuthToken(ctx context.Context, hostID uint, authToken string) error
// ListPoliciesForHost lists the policies that a host will check and whether they are passing
ListPoliciesForHost(ctx context.Context, host *Host) ([]*HostPolicy, error)
GetMunkiVersion(ctx context.Context, hostID uint) (string, error)
GetMunkiIssues(ctx context.Context, hostID uint) ([]*HostMunkiIssue, error)
GetMDM(ctx context.Context, hostID uint) (*HostMDM, error)
AggregatedMunkiVersion(ctx context.Context, teamID *uint) ([]AggregatedMunkiVersion, time.Time, error)
AggregatedMunkiIssues(ctx context.Context, teamID *uint) ([]AggregatedMunkiIssue, time.Time, error)
AggregatedMDMStatus(ctx context.Context, teamID *uint) (AggregatedMDMStatus, time.Time, error)
AggregatedMDMSolutions(ctx context.Context, teamID *uint) ([]AggregatedMDMSolutions, time.Time, error)
GenerateAggregatedMunkiAndMDM(ctx context.Context) error
OSVersions(ctx context.Context, teamID *uint, platform *string, name *string, version *string) (*OSVersions, error)
UpdateOSVersions(ctx context.Context) error
///////////////////////////////////////////////////////////////////////////////
// TargetStore
// CountHostsInTargets returns the metrics of the hosts in the provided labels, teams, and explicit host IDs.
CountHostsInTargets(ctx context.Context, filter TeamFilter, targets HostTargets, now time.Time) (TargetMetrics, error)
// HostIDsInTargets returns the host IDs of the hosts in the provided labels, teams, and explicit host IDs. The
// returned host IDs should be sorted in ascending order.
HostIDsInTargets(ctx context.Context, filter TeamFilter, targets HostTargets) ([]uint, error)
///////////////////////////////////////////////////////////////////////////////
// PasswordResetStore manages password resets in the Datastore
NewPasswordResetRequest(ctx context.Context, req *PasswordResetRequest) (*PasswordResetRequest, error)
DeletePasswordResetRequestsForUser(ctx context.Context, userID uint) error
FindPasswordResetByToken(ctx context.Context, token string) (*PasswordResetRequest, error)
///////////////////////////////////////////////////////////////////////////////
// SessionStore is the abstract interface that all session backends must conform to.
// SessionByKey returns, given a session key, a session object or an error if one could not be found for the given
// key
SessionByKey(ctx context.Context, key string) (*Session, error)
// SessionByID returns, given a session id, find and return a session object or an error if one could not be found
// for the given id
SessionByID(ctx context.Context, id uint) (*Session, error)
// ListSessionsForUser finds all the active sessions for a given user
ListSessionsForUser(ctx context.Context, id uint) ([]*Session, error)
// NewSession stores a new session struct
NewSession(ctx context.Context, userID uint, sessionKey string) (*Session, error)
// DestroySession destroys the currently tracked session
DestroySession(ctx context.Context, session *Session) error
// DestroyAllSessionsForUser destroys all of the sessions for a given user
DestroyAllSessionsForUser(ctx context.Context, id uint) error
// MarkSessionAccessed marks the currently tracked session as access to extend expiration
MarkSessionAccessed(ctx context.Context, session *Session) error
///////////////////////////////////////////////////////////////////////////////
// AppConfigStore contains method for saving and retrieving application configuration
NewAppConfig(ctx context.Context, info *AppConfig) (*AppConfig, error)
AppConfig(ctx context.Context) (*AppConfig, error)
SaveAppConfig(ctx context.Context, info *AppConfig) error
// GetEnrollSecrets gets the enroll secrets for a team (or global if teamID is nil).
GetEnrollSecrets(ctx context.Context, teamID *uint) ([]*EnrollSecret, error)
// ApplyEnrollSecrets replaces the current enroll secrets for a team with the provided secrets.
ApplyEnrollSecrets(ctx context.Context, teamID *uint, secrets []*EnrollSecret) error
///////////////////////////////////////////////////////////////////////////////
// InviteStore contains the methods for managing user invites in a datastore.
// NewInvite creates and stores a new invitation in a DB.
NewInvite(ctx context.Context, i *Invite) (*Invite, error)
// ListInvites lists all invites in the datastore.
ListInvites(ctx context.Context, opt ListOptions) ([]*Invite, error)
// Invite retrieves an invite by its ID.
Invite(ctx context.Context, id uint) (*Invite, error)
// InviteByEmail retrieves an invite for a specific email address.
InviteByEmail(ctx context.Context, email string) (*Invite, error)
// InviteByToken retrieves and invite using the token string.
InviteByToken(ctx context.Context, token string) (*Invite, error)
// DeleteInvite deletes an invitation.
DeleteInvite(ctx context.Context, id uint) error
UpdateInvite(ctx context.Context, id uint, i *Invite) (*Invite, error)
///////////////////////////////////////////////////////////////////////////////
// ScheduledQueryStore
// ListScheduledQueriesInPackWithStats loads a pack's scheduled queries and its aggregated stats.
ListScheduledQueriesInPackWithStats(ctx context.Context, id uint, opts ListOptions) ([]*ScheduledQuery, error)
NewScheduledQuery(ctx context.Context, sq *ScheduledQuery, opts ...OptionalArg) (*ScheduledQuery, error)
SaveScheduledQuery(ctx context.Context, sq *ScheduledQuery) (*ScheduledQuery, error)
DeleteScheduledQuery(ctx context.Context, id uint) error
ScheduledQuery(ctx context.Context, id uint) (*ScheduledQuery, error)
CleanupExpiredHosts(ctx context.Context) ([]uint, error)
// ScheduledQueryIDsByName loads the IDs associated with the given pack and
// query names. It returns a slice of IDs in the same order as
// packAndSchedQueryNames, with the ID set to 0 if the corresponding
// scheduled query did not exist.
ScheduledQueryIDsByName(ctx context.Context, batchSize int, packAndSchedQueryNames ...[2]string) ([]uint, error)
///////////////////////////////////////////////////////////////////////////////
// TeamStore
// NewTeam creates a new Team object in the store.
NewTeam(ctx context.Context, team *Team) (*Team, error)
// SaveTeam saves any changes to the team.
SaveTeam(ctx context.Context, team *Team) (*Team, error)
// Team retrieves the Team by ID.
Team(ctx context.Context, tid uint) (*Team, error)
// Team deletes the Team by ID.
DeleteTeam(ctx context.Context, tid uint) error
// TeamByName retrieves the Team by Name.
TeamByName(ctx context.Context, name string) (*Team, error)
// ListTeams lists teams with the ordering and filters in the provided options.
ListTeams(ctx context.Context, filter TeamFilter, opt ListOptions) ([]*Team, error)
// TeamsSummary lists id, name and description for all teams.
TeamsSummary(ctx context.Context) ([]*TeamSummary, error)
// SearchTeams searches teams using the provided query and ommitting the provided existing selection.
SearchTeams(ctx context.Context, filter TeamFilter, matchQuery string, omit ...uint) ([]*Team, error)
// TeamEnrollSecrets lists the enroll secrets for the team.
TeamEnrollSecrets(ctx context.Context, teamID uint) ([]*EnrollSecret, error)
// DeleteIntegrationsFromTeams deletes integrations used by teams, as they
// are being deleted from the global configuration.
DeleteIntegrationsFromTeams(ctx context.Context, deletedIntgs Integrations) error
///////////////////////////////////////////////////////////////////////////////
// SoftwareStore
// ListSoftwareForVulnDetection returns all software for the given hostID with only the fields
// used for vulnerability detection populated (id, name, version, cpe_id, cpe)
ListSoftwareForVulnDetection(ctx context.Context, hostID uint) ([]Software, error)
ListSoftwareVulnerabilities(ctx context.Context, hostIDs []uint) (map[uint][]SoftwareVulnerability, error)
LoadHostSoftware(ctx context.Context, host *Host, includeCVEScores bool) error
AllSoftwareWithoutCPEIterator(ctx context.Context, excludedPlatforms []string) (SoftwareIterator, error)
AddCPEForSoftware(ctx context.Context, software Software, cpe string) error
ListSoftwareCPEs(ctx context.Context) ([]SoftwareCPE, error)
// InsertVulnerabilities inserts the given vulnerabilities in the datastore, returns the number
// of rows inserted. If a vulnerability already exists in the datastore, then it will be ignored.
InsertVulnerabilities(ctx context.Context, vulns []SoftwareVulnerability, source VulnerabilitySource) (int64, error)
SoftwareByID(ctx context.Context, id uint, includeCVEScores bool) (*Software, error)
// ListSoftwareByHostIDShort lists software by host ID, but does not include CPEs or vulnerabilites.
// It is meant to be used when only minimal software fields are required eg when updating host software.
ListSoftwareByHostIDShort(ctx context.Context, hostID uint) ([]Software, error)
// SyncHostsSoftware calculates the number of hosts having each
// software installed and stores that information in the software_host_counts
// table.
//
// After aggregation, it cleans up unused software (e.g. software installed
// on removed hosts, software uninstalled on hosts, etc.)
SyncHostsSoftware(ctx context.Context, updatedAt time.Time) error
HostsBySoftwareIDs(ctx context.Context, softwareIDs []uint) ([]*HostShort, error)
HostsByCVE(ctx context.Context, cve string) ([]*HostShort, error)
InsertCVEMeta(ctx context.Context, cveMeta []CVEMeta) error
ListCVEs(ctx context.Context, maxAge time.Duration) ([]CVEMeta, error)
///////////////////////////////////////////////////////////////////////////////
// OperatingSystemsStore
// ListOperationsSystems returns all operating systems (id, name, version)
ListOperatingSystems(ctx context.Context) ([]OperatingSystem, error)
// UpdateHostOperatingSystem updates the `host_operating_system` table
// for the given host ID with the ID of the operating system associated
// with the given name, version, arch, and kernel version in the
// `operating_systems` table.
//
// If the `operating_systems` table does not already include a record
// associated with the given name, version, arch, and kernel version,
// a new record is also created.
UpdateHostOperatingSystem(ctx context.Context, hostID uint, hostOS OperatingSystem) error
// CleanupHostOperatingSystems removes records from the host_operating_system table that are
// associated with any non-existent host (e.g., expired hosts) and removes records from the
// operating_systems table that no longer associated with any host (e.g., all hosts have
// upgraded from a prior version).
CleanupHostOperatingSystems(ctx context.Context) error
///////////////////////////////////////////////////////////////////////////////
// ActivitiesStore
NewActivity(ctx context.Context, user *User, activityType string, details *map[string]interface{}) error
ListActivities(ctx context.Context, opt ListOptions) ([]*Activity, error)
///////////////////////////////////////////////////////////////////////////////
// StatisticsStore
ShouldSendStatistics(ctx context.Context, frequency time.Duration, config config.FleetConfig, license *LicenseInfo) (StatisticsPayload, bool, error)
RecordStatisticsSent(ctx context.Context) error
///////////////////////////////////////////////////////////////////////////////
// GlobalPoliciesStore
// ApplyPolicySpecs applies a list of policies (likely from a yaml file) to the datastore. Existing policies are updated,
// and new policies are created.
ApplyPolicySpecs(ctx context.Context, authorID uint, specs []*PolicySpec) error
NewGlobalPolicy(ctx context.Context, authorID *uint, args PolicyPayload) (*Policy, error)
Policy(ctx context.Context, id uint) (*Policy, error)
// SavePolicy updates some fields of the given policy on the datastore.
//
// It is also used to update team policies.
SavePolicy(ctx context.Context, p *Policy) error
ListGlobalPolicies(ctx context.Context) ([]*Policy, error)
PoliciesByID(ctx context.Context, ids []uint) (map[uint]*Policy, error)
DeleteGlobalPolicies(ctx context.Context, ids []uint) ([]uint, error)
PolicyQueriesForHost(ctx context.Context, host *Host) (map[string]string, error)
// Methods used for async processing of host policy query results.
AsyncBatchInsertPolicyMembership(ctx context.Context, batch []PolicyMembershipResult) error
AsyncBatchUpdatePolicyTimestamp(ctx context.Context, ids []uint, ts time.Time) error
// MigrateTables creates and migrates the table schemas
MigrateTables(ctx context.Context) error
// MigrateData populates built-in data
MigrateData(ctx context.Context) error
// MigrationStatus returns nil if migrations are complete, and an error if migrations need to be run.
MigrationStatus(ctx context.Context) (*MigrationStatus, error)
ListSoftware(ctx context.Context, opt SoftwareListOptions) ([]Software, error)
CountSoftware(ctx context.Context, opt SoftwareListOptions) (int, error)
// DeleteVulnerabilities deletes the given list of vulnerabilities identified by CPE+CVE.
DeleteSoftwareVulnerabilities(ctx context.Context, vulnerabilities []SoftwareVulnerability) error
///////////////////////////////////////////////////////////////////////////////
// Team Policies
NewTeamPolicy(ctx context.Context, teamID uint, authorID *uint, args PolicyPayload) (*Policy, error)
ListTeamPolicies(ctx context.Context, teamID uint) ([]*Policy, error)
DeleteTeamPolicies(ctx context.Context, teamID uint, ids []uint) ([]uint, error)
TeamPolicy(ctx context.Context, teamID uint, policyID uint) (*Policy, error)
CleanupPolicyMembership(ctx context.Context, now time.Time) error
///////////////////////////////////////////////////////////////////////////////
// Locking
// Lock tries to get an atomic lock on an instance named with `name`
// and an `owner` identified by a random string per instance.
// Subsequently locking the same resource name for the same owner
// renews the lock expiration.
// It returns true, nil if it managed to obtain a lock on the instance.
// false and potentially an error otherwise.
// This must not be blocking.
Lock(ctx context.Context, name string, owner string, expiration time.Duration) (bool, error)
// Unlock tries to unlock the lock by that `name` for the specified
// `owner`. Unlocking when not holding the lock shouldn't error
Unlock(ctx context.Context, name string, owner string) error
// DBLocks returns the current database transaction lock waits information.
DBLocks(ctx context.Context) ([]*DBLock, error)
///////////////////////////////////////////////////////////////////////////////
// Aggregated Stats
UpdateScheduledQueryAggregatedStats(ctx context.Context) error
UpdateQueryAggregatedStats(ctx context.Context) error
///////////////////////////////////////////////////////////////////////////////
// Following are the set of APIs used by osquery hosts:
// TODO(lucas): Move them to a separate datastore or abstraction, to avoid
// future developers trying to use user-datastore APIs on the host requests.
// The user-datastore APIs (all the above) are generally more expensive and
// load data that is not necessary for osquery hosts.
///////////////////////////////////////////////////////////////////////////////
// LoadHostByNodeKey loads the whole host identified by the node key.
// If the node key is invalid it returns a NotFoundError.
LoadHostByNodeKey(ctx context.Context, nodeKey string) (*Host, error)
// HostLite will load the primary data of the host with the given id.
// We define "primary data" as all host information except the
// details (like cpu, memory, gigs_disk_space_available, etc.).
//
// If the host doesn't exist, a NotFoundError is returned.
HostLite(ctx context.Context, hostID uint) (*Host, error)
// UpdateHostOsqueryIntervals updates the osquery intervals of a host.
UpdateHostOsqueryIntervals(ctx context.Context, hostID uint, intervals HostOsqueryIntervals) error
// TeamAgentOptions loads the agents options of a team.
TeamAgentOptions(ctx context.Context, teamID uint) (*json.RawMessage, error)
// TeamFeatures loads the features enabled for a team.
TeamFeatures(ctx context.Context, teamID uint) (*Features, error)
// SaveHostPackStats stores (and updates) the pack's scheduled queries stats of a host.
SaveHostPackStats(ctx context.Context, hostID uint, stats []PackStats) error
// AsyncBatchSaveHostsScheduledQueryStats efficiently saves a batch of hosts'
// pack stats of scheduled queries. It is the async and batch version of
// SaveHostPackStats. It returns the number of INSERT-ON DUPLICATE UPDATE
// statements that were executed (for reporting purpose) or an error.
AsyncBatchSaveHostsScheduledQueryStats(ctx context.Context, stats map[uint][]ScheduledQueryStats, batchSize int) (int, error)
// UpdateHostSoftware updates the software list of a host.
// The update consists of deleting existing entries that are not in the given `software`
// slice, updating existing entries and inserting new entries.
UpdateHostSoftware(ctx context.Context, hostID uint, software []Software) error
// UpdateHost updates a host.
UpdateHost(ctx context.Context, host *Host) error
// ListScheduledQueriesInPack lists all the scheduled queries of a pack.
ListScheduledQueriesInPack(ctx context.Context, packID uint) ([]*ScheduledQuery, error)
// UpdateHostRefetchRequested updates a host's refetch requested field.
UpdateHostRefetchRequested(ctx context.Context, hostID uint, value bool) error
// FlippingPoliciesForHost fetches the policies with incoming results and returns:
// - a list of "new" failing policies; "new" here means those that fail on their first
// run, and those that were passing on the previous run and are failing on the incoming execution.
// - a list of "new" passing policies; "new" here means those that failed on a previous
// run and are passing now.
//
// "Failure" here means the policy query executed successfully but didn't return any rows,
// so policies that did not execute (incomingResults with nil bool) are ignored.
FlippingPoliciesForHost(ctx context.Context, hostID uint, incomingResults map[uint]*bool) (newFailing []uint, newPassing []uint, err error)
// RecordPolicyQueryExecutions records the execution results of the policies for the given host.
RecordPolicyQueryExecutions(ctx context.Context, host *Host, results map[uint]*bool, updated time.Time, deferredSaveHost bool) error
// RecordLabelQueryExecutions saves the results of label queries. The results map is a map of label id -> whether or
// not the label matches. The time parameter is the timestamp to save with the query execution.
RecordLabelQueryExecutions(ctx context.Context, host *Host, results map[uint]*bool, t time.Time, deferredSaveHost bool) error
// SaveHostUsers updates the user list of a host.
// The update consists of deleting existing entries that are not in the given `users`
// slice, updating existing entries and inserting new entries.
SaveHostUsers(ctx context.Context, hostID uint, users []HostUser) error
// SaveHostAdditional updates the additional queries results of a host.
SaveHostAdditional(ctx context.Context, hostID uint, additional *json.RawMessage) error
SetOrUpdateMunkiInfo(ctx context.Context, hostID uint, version string, errors, warnings []string) error
SetOrUpdateMDMData(ctx context.Context, hostID uint, enrolled bool, serverURL string, installedFromDep bool) error
ReplaceHostDeviceMapping(ctx context.Context, id uint, mappings []*HostDeviceMapping) error
// ReplaceHostBatteries creates or updates the battery mappings of a host.
ReplaceHostBatteries(ctx context.Context, id uint, mappings []*HostBattery) error
// VerifyEnrollSecret checks that the provided secret matches an active enroll secret. If it is successfully
// matched, that secret is returned. Otherwise, an error is returned.
VerifyEnrollSecret(ctx context.Context, secret string) (*EnrollSecret, error)
// EnrollHost will enroll a new host with the given identifier, setting the node key, and team. Implementations of
// this method should respect the provided host enrollment cooldown, by returning an error if the host has enrolled
// within the cooldown period.
EnrollHost(ctx context.Context, osqueryHostId, nodeKey string, teamID *uint, cooldown time.Duration) (*Host, error)
SerialUpdateHost(ctx context.Context, host *Host) error
///////////////////////////////////////////////////////////////////////////////
// JobStore
// NewJob inserts a new job into the jobs table (queue).
NewJob(ctx context.Context, job *Job) (*Job, error)
// GetQueuedJobs gets queued jobs from the jobs table (queue).
GetQueuedJobs(ctx context.Context, maxNumJobs int) ([]*Job, error)
// UpdateJobs updates an existing job. Call this after processing a job.
UpdateJob(ctx context.Context, id uint, job *Job) (*Job, error)
///////////////////////////////////////////////////////////////////////////////
// Debug
InnoDBStatus(ctx context.Context) (string, error)
ProcessList(ctx context.Context) ([]MySQLProcess, error)
///////////////////////////////////////////////////////////////////////////////
// Windows Update History
InsertWindowsUpdates(ctx context.Context, hostID uint, updates []WindowsUpdate) error
}
const (
// Default batch size to use for ScheduledQueryIDsByName.
DefaultScheduledQueryIDsByNameBatchSize = 1000
// Default batch size for loading IDs of or inserting new munki issues.
DefaultMunkiIssuesBatchSize = 100
)
type MySQLProcess struct {
Id int `json:"id" db:"Id"`
User string `json:"user" db:"User"`
Host string `json:"host" db:"Host"`
DB *string `json:"db" db:"db"`
Command string `json:"command" db:"Command"`
Time int `json:"time" db:"Time"`
State *string `json:"state" db:"State"`
Info *string `json:"info" db:"Info"`
}
// HostOsqueryIntervals holds an osquery host's osquery interval configurations.
type HostOsqueryIntervals struct {
DistributedInterval uint `json:"distributed_interval" db:"distributed_interval"`
ConfigTLSRefresh uint `json:"config_tls_refresh" db:"config_tls_refresh"`
LoggerTLSPeriod uint `json:"logger_tls_period" db:"logger_tls_period"`
}
type MigrationStatus struct {
// StatusCode holds the code for the migration status.
//
// If StatusCode is NoMigrationsCompleted or AllMigrationsCompleted
// then all other fields are empty.
//
// If StatusCode is SomeMigrationsCompleted, then missing migrations
// are available in MissingTable and MissingData.
//
// If StatusCode is UnknownMigrations, then unknown migrations
// are available in UnknownTable and UnknownData.
StatusCode MigrationStatusCode `json:"status_code"`
// MissingTable holds the missing table migrations.
MissingTable []int64 `json:"missing_table"`
// MissingTable holds the missing data migrations.
MissingData []int64 `json:"missing_data"`
// UnknownTable holds unknown applied table migrations.
UnknownTable []int64 `json:"unknown_table"`
// UnknownTable holds unknown applied data migrations.
UnknownData []int64 `json:"unknown_data"`
}
type MigrationStatusCode int
const (
// NoMigrationsCompleted indicates the database has no migrations installed.
NoMigrationsCompleted MigrationStatusCode = iota
// SomeMigrationsCompleted indicates some (not all) migrations are missing.
SomeMigrationsCompleted
// AllMigrationsCompleted means all migrations have been installed successfully.
AllMigrationsCompleted
// UnknownMigrations means some unidentified migrations were detected on the database.
UnknownMigrations
)
// NotFoundError is returned when the datastore resource cannot be found.
type NotFoundError interface {
error
IsNotFound() bool
}
func IsNotFound(err error) bool {
var nfe NotFoundError
if errors.As(err, &nfe) {
return nfe.IsNotFound()
}
return false
}
// AlreadyExistsError is returned when creating a datastore resource that already exists.
type AlreadyExistsError interface {
error
IsExists() bool
}
// ForeignKeyError is returned when the operation fails due to foreign key constraints.
type ForeignKeyError interface {
error
IsForeignKey() bool
}
func IsForeignKey(err error) bool {
var fke ForeignKeyError
if errors.As(err, &fke) {
return fke.IsForeignKey()
}
return false
}
type OptionalArg func() interface{}