mirror of
https://github.com/empayre/fleet.git
synced 2024-11-08 09:43:51 +00:00
0a77f79d22
- Add enable_analytics column to database. - Allow enable_analytics to be set via API. - Add messaging in fleetctl setup. Note that this defaults to off for existing installations, and defaults on for newly set up installs. No collection or sending of analytics yet exists, we are strictly storing the preference at this time. Part of #454
253 lines
7.1 KiB
Go
253 lines
7.1 KiB
Go
package mysql
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/VividCortex/mysqlerr"
|
|
"github.com/fleetdm/fleet/server/fleet"
|
|
"github.com/go-sql-driver/mysql"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func (d *Datastore) NewAppConfig(info *fleet.AppConfig) (*fleet.AppConfig, error) {
|
|
if err := d.SaveAppConfig(info); err != nil {
|
|
return nil, errors.Wrap(err, "new app config")
|
|
}
|
|
|
|
return info, nil
|
|
}
|
|
|
|
func (d *Datastore) AppConfig() (*fleet.AppConfig, error) {
|
|
info := &fleet.AppConfig{}
|
|
err := d.db.Get(info, "SELECT * FROM app_configs LIMIT 1")
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "selecting app config")
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (d *Datastore) isEventSchedulerEnabled() (bool, error) {
|
|
rows, err := d.db.Query("SELECT @@event_scheduler")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if !rows.Next() {
|
|
return false, errors.New("Error detecting MySQL event scheduler status.")
|
|
}
|
|
var value string
|
|
if err := rows.Scan(&value); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return value == "ON", nil
|
|
}
|
|
|
|
func (d *Datastore) ManageHostExpiryEvent(hostExpiryEnabled bool, hostExpiryWindow int) error {
|
|
var err error
|
|
hostExpiryConfig := struct {
|
|
Window int `db:"host_expiry_window"`
|
|
}{}
|
|
if err = d.db.Get(&hostExpiryConfig, "SELECT host_expiry_window from app_configs LIMIT 1"); err != nil {
|
|
return errors.Wrap(err, "get expiry window setting")
|
|
}
|
|
|
|
shouldUpdateWindow := hostExpiryEnabled && hostExpiryConfig.Window != hostExpiryWindow
|
|
|
|
if !hostExpiryEnabled || shouldUpdateWindow {
|
|
if _, err := d.db.Exec("DROP EVENT IF EXISTS host_expiry"); err != nil {
|
|
if driverErr, ok := err.(*mysql.MySQLError); !ok || driverErr.Number != mysqlerr.ER_DBACCESS_DENIED_ERROR {
|
|
return errors.Wrap(err, "drop existing host_expiry event")
|
|
}
|
|
}
|
|
}
|
|
|
|
if shouldUpdateWindow {
|
|
sql := fmt.Sprintf("CREATE EVENT IF NOT EXISTS host_expiry ON SCHEDULE EVERY 1 HOUR ON COMPLETION PRESERVE DO DELETE FROM hosts WHERE seen_time < DATE_SUB(NOW(), INTERVAL %d DAY)", hostExpiryWindow)
|
|
if _, err := d.db.Exec(sql); err != nil {
|
|
return errors.Wrap(err, "create new host_expiry event")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *Datastore) SaveAppConfig(info *fleet.AppConfig) error {
|
|
eventSchedulerEnabled, err := d.isEventSchedulerEnabled()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !eventSchedulerEnabled && info.HostExpiryEnabled {
|
|
return errors.New("MySQL Event Scheduler must be enabled to configure Host Expiry.")
|
|
}
|
|
|
|
if err := d.ManageHostExpiryEvent(info.HostExpiryEnabled, info.HostExpiryWindow); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Note that we hard code the ID column to 1, insuring that, if no rows
|
|
// exist, a row will be created with INSERT, if a row does exist the key
|
|
// will be violate uniqueness constraint and an UPDATE will occur
|
|
insertStatement := `
|
|
INSERT INTO app_configs (
|
|
id,
|
|
org_name,
|
|
org_logo_url,
|
|
server_url,
|
|
smtp_configured,
|
|
smtp_sender_address,
|
|
smtp_server,
|
|
smtp_port,
|
|
smtp_authentication_type,
|
|
smtp_enable_ssl_tls,
|
|
smtp_authentication_method,
|
|
smtp_domain,
|
|
smtp_user_name,
|
|
smtp_password,
|
|
smtp_verify_ssl_certs,
|
|
smtp_enable_start_tls,
|
|
entity_id,
|
|
issuer_uri,
|
|
idp_image_url,
|
|
metadata,
|
|
metadata_url,
|
|
idp_name,
|
|
enable_sso,
|
|
enable_sso_idp_login,
|
|
fim_interval,
|
|
fim_file_accesses,
|
|
host_expiry_enabled,
|
|
host_expiry_window,
|
|
live_query_disabled,
|
|
additional_queries,
|
|
agent_options,
|
|
enable_analytics
|
|
)
|
|
VALUES( 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )
|
|
ON DUPLICATE KEY UPDATE
|
|
org_name = VALUES(org_name),
|
|
org_logo_url = VALUES(org_logo_url),
|
|
server_url = VALUES(server_url),
|
|
smtp_configured = VALUES(smtp_configured),
|
|
smtp_sender_address = VALUES(smtp_sender_address),
|
|
smtp_server = VALUES(smtp_server),
|
|
smtp_port = VALUES(smtp_port),
|
|
smtp_authentication_type = VALUES(smtp_authentication_type),
|
|
smtp_enable_ssl_tls = VALUES(smtp_enable_ssl_tls),
|
|
smtp_authentication_method = VALUES(smtp_authentication_method),
|
|
smtp_domain = VALUES(smtp_domain),
|
|
smtp_user_name = VALUES(smtp_user_name),
|
|
smtp_password = VALUES(smtp_password),
|
|
smtp_verify_ssl_certs = VALUES(smtp_verify_ssl_certs),
|
|
smtp_enable_start_tls = VALUES(smtp_enable_start_tls),
|
|
entity_id = VALUES(entity_id),
|
|
issuer_uri = VALUES(issuer_uri),
|
|
idp_image_url = VALUES(idp_image_url),
|
|
metadata = VALUES(metadata),
|
|
metadata_url = VALUES(metadata_url),
|
|
idp_name = VALUES(idp_name),
|
|
enable_sso = VALUES(enable_sso),
|
|
enable_sso_idp_login = VALUES(enable_sso_idp_login),
|
|
fim_interval = VALUES(fim_interval),
|
|
fim_file_accesses = VALUES(fim_file_accesses),
|
|
host_expiry_enabled = VALUES(host_expiry_enabled),
|
|
host_expiry_window = VALUES(host_expiry_window),
|
|
live_query_disabled = VALUES(live_query_disabled),
|
|
additional_queries = VALUES(additional_queries),
|
|
agent_options = VALUES(agent_options),
|
|
enable_analytics = VALUES(enable_analytics)
|
|
`
|
|
|
|
_, err = d.db.Exec(insertStatement,
|
|
info.OrgName,
|
|
info.OrgLogoURL,
|
|
info.ServerURL,
|
|
info.SMTPConfigured,
|
|
info.SMTPSenderAddress,
|
|
info.SMTPServer,
|
|
info.SMTPPort,
|
|
info.SMTPAuthenticationType,
|
|
info.SMTPEnableTLS,
|
|
info.SMTPAuthenticationMethod,
|
|
info.SMTPDomain,
|
|
info.SMTPUserName,
|
|
info.SMTPPassword,
|
|
info.SMTPVerifySSLCerts,
|
|
info.SMTPEnableStartTLS,
|
|
info.EntityID,
|
|
info.IssuerURI,
|
|
info.IDPImageURL,
|
|
info.Metadata,
|
|
info.MetadataURL,
|
|
info.IDPName,
|
|
info.EnableSSO,
|
|
info.EnableSSOIdPLogin,
|
|
info.FIMInterval,
|
|
info.FIMFileAccesses,
|
|
info.HostExpiryEnabled,
|
|
info.HostExpiryWindow,
|
|
info.LiveQueryDisabled,
|
|
info.AdditionalQueries,
|
|
info.AgentOptions,
|
|
info.EnableAnalytics,
|
|
)
|
|
|
|
return err
|
|
}
|
|
|
|
func (d *Datastore) VerifyEnrollSecret(secret string) (*fleet.EnrollSecret, error) {
|
|
var s fleet.EnrollSecret
|
|
err := d.db.Get(&s, "SELECT team_id FROM enroll_secrets WHERE secret = ?", secret)
|
|
if err != nil {
|
|
return nil, errors.New("no matching secret found")
|
|
}
|
|
|
|
return &s, nil
|
|
}
|
|
|
|
func (d *Datastore) ApplyEnrollSecrets(teamID *uint, secrets []*fleet.EnrollSecret) error {
|
|
err := d.withRetryTxx(func(tx *sqlx.Tx) error {
|
|
if teamID != nil {
|
|
sql := `DELETE FROM enroll_secrets WHERE team_id = ?`
|
|
if _, err := tx.Exec(sql, teamID); err != nil {
|
|
return errors.Wrap(err, "clear before insert")
|
|
}
|
|
} else {
|
|
sql := `DELETE FROM enroll_secrets WHERE team_id IS NULL`
|
|
if _, err := tx.Exec(sql); err != nil {
|
|
return errors.Wrap(err, "clear before insert")
|
|
}
|
|
}
|
|
|
|
for _, secret := range secrets {
|
|
sql := `
|
|
INSERT INTO enroll_secrets (secret, team_id)
|
|
VALUES ( ?, ? )
|
|
`
|
|
if _, err := tx.Exec(sql, secret.Secret, teamID); err != nil {
|
|
return errors.Wrap(err, "upsert secret")
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (d *Datastore) GetEnrollSecrets(teamID *uint) ([]*fleet.EnrollSecret, error) {
|
|
var args []interface{}
|
|
sql := "SELECT * FROM enroll_secrets WHERE "
|
|
// MySQL requires comparing NULL with IS. NULL = NULL evaluates to FALSE.
|
|
if teamID == nil {
|
|
sql += "team_id IS NULL"
|
|
} else {
|
|
sql += "team_id = ?"
|
|
args = append(args, teamID)
|
|
}
|
|
var secrets []*fleet.EnrollSecret
|
|
if err := d.db.Select(&secrets, sql, args...); err != nil {
|
|
return nil, errors.Wrap(err, "get secrets")
|
|
}
|
|
return secrets, nil
|
|
}
|