diff --git a/changes/issue-296-disable-sso-for-users b/changes/issue-296-disable-sso-for-users new file mode 100644 index 0000000000..da681fb0db --- /dev/null +++ b/changes/issue-296-disable-sso-for-users @@ -0,0 +1 @@ +* When disabling SSO at the org level, disable it for all users. Fixes issue 296 \ No newline at end of file diff --git a/server/datastore/datastore_app.go b/server/datastore/datastore_app.go index f4a9ccc679..0e533f9c94 100644 --- a/server/datastore/datastore_app.go +++ b/server/datastore/datastore_app.go @@ -54,6 +54,27 @@ func testOrgInfo(t *testing.T, ds fleet.Datastore) { info4, err := ds.NewAppConfig(info3) assert.Nil(t, err) assert.Equal(t, info3, info4) + + email := "e@mail.com" + u := &fleet.User{ + Password: []byte("pass"), + Email: email, + SSOEnabled: true, + } + _, err = ds.NewUser(u) + assert.Nil(t, err) + + verify, err := ds.UserByEmail(email) + assert.Nil(t, err) + assert.True(t, verify.SSOEnabled) + + info4.EnableSSO = false + err = ds.SaveAppConfig(info4) + assert.Nil(t, err) + + verify, err = ds.UserByEmail(email) + assert.Nil(t, err) + assert.False(t, verify.SSOEnabled) } func testAdditionalQueries(t *testing.T, ds fleet.Datastore) { diff --git a/server/datastore/mysql/app_configs.go b/server/datastore/mysql/app_configs.go index 4bbe29d264..c5883bbac0 100644 --- a/server/datastore/mysql/app_configs.go +++ b/server/datastore/mysql/app_configs.go @@ -43,19 +43,19 @@ func (d *Datastore) isEventSchedulerEnabled() (bool, error) { return value == "ON", nil } -func (d *Datastore) ManageHostExpiryEvent(hostExpiryEnabled bool, hostExpiryWindow int) error { +func (d *Datastore) ManageHostExpiryEvent(tx *sqlx.Tx, 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 { + if err = tx.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 _, err := tx.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") } @@ -64,7 +64,7 @@ func (d *Datastore) ManageHostExpiryEvent(hostExpiryEnabled bool, hostExpiryWind 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 { + if _, err := tx.Exec(sql); err != nil { return errors.Wrap(err, "create new host_expiry event") } } @@ -81,14 +81,15 @@ func (d *Datastore) SaveAppConfig(info *fleet.AppConfig) error { 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 - } + return d.withTx(func(tx *sqlx.Tx) error { + if err := d.ManageHostExpiryEvent(tx, 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 := ` + // 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, @@ -158,41 +159,52 @@ func (d *Datastore) SaveAppConfig(info *fleet.AppConfig) error { 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, - ) + _, err = tx.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, + ) + if err != nil { + return err + } - return err + if !info.EnableSSO { + _, err = tx.Exec(`UPDATE users SET sso_enabled=false`) + if err != nil { + return err + } + } + + return nil + }) } func (d *Datastore) VerifyEnrollSecret(secret string) (*fleet.EnrollSecret, error) { diff --git a/server/datastore/mysql/mysql.go b/server/datastore/mysql/mysql.go index 481bdf2fda..152377b569 100644 --- a/server/datastore/mysql/mysql.go +++ b/server/datastore/mysql/mysql.go @@ -114,6 +114,37 @@ func (d *Datastore) withRetryTxx(fn txFn) (err error) { return backoff.Retry(operation, bo) } +// withTx provides a common way to commit/rollback a txFn +func (d *Datastore) withTx(fn txFn) (err error) { + tx, err := d.db.Beginx() + if err != nil { + return errors.Wrap(err, "create transaction") + } + + defer func() { + if p := recover(); p != nil { + if err := tx.Rollback(); err != nil { + d.logger.Log("err", err, "msg", "error encountered during transaction panic rollback") + } + panic(p) + } + }() + + if err := fn(tx); err != nil { + rbErr := tx.Rollback() + if rbErr != nil && rbErr != sql.ErrTxDone { + return errors.Wrapf(err, "got err '%s' rolling back after err", rbErr.Error()) + } + return err + } + + if err := tx.Commit(); err != nil { + return errors.Wrap(err, "commit transaction") + } + + return nil +} + // New creates an MySQL datastore. func New(config config.MysqlConfig, c clock.Clock, opts ...DBOption) (*Datastore, error) { options := &dbOptions{