diff --git a/service/nanomdm/service.go b/service/nanomdm/service.go index b26641f..278df8a 100644 --- a/service/nanomdm/service.go +++ b/service/nanomdm/service.go @@ -150,6 +150,9 @@ func (s *Service) UserAuthenticate(r *mdm.Request, message *mdm.UserAuthenticate if err := s.updateEnrollID(r, &message.Enrollment); err != nil { return nil, err } + if err := s.store.StoreUserAuthenticate(r, message); err != nil { + return nil, err + } // if the DigestResponse is empty then this is the first (of two) // UserAuthenticate messages depending on our response if message.DigestResponse == "" { diff --git a/storage/allmulti/allmulti.go b/storage/allmulti/allmulti.go index 2766bda..705a149 100644 --- a/storage/allmulti/allmulti.go +++ b/storage/allmulti/allmulti.go @@ -70,6 +70,13 @@ func (ms *MultiAllStorage) StoreTokenUpdate(r *mdm.Request, msg *mdm.TokenUpdate return err } +func (ms *MultiAllStorage) StoreUserAuthenticate(r *mdm.Request, msg *mdm.UserAuthenticate) error { + _, err := ms.execStores(func(s storage.AllStorage) (interface{}, error) { + return nil, s.StoreUserAuthenticate(r, msg) + }) + return err +} + func (ms *MultiAllStorage) Disable(r *mdm.Request) error { _, err := ms.execStores(func(s storage.AllStorage) (interface{}, error) { return nil, s.Disable(r) diff --git a/storage/file/file.go b/storage/file/file.go index 0415080..f4962ae 100644 --- a/storage/file/file.go +++ b/storage/file/file.go @@ -20,6 +20,9 @@ const ( DisabledFilename = "Disabled" BootstrapTokenFile = "BootstrapToken.dat" + UserAuthFilename = "UserAuthenticate.plist" + UserAuthDigestFilename = "UserAuthenticate.Digest.plist" + CertAuthFilename = "CertAuth.sha256.txt" CertAuthAssociationsFilename = "CertAuth.txt" @@ -166,6 +169,17 @@ func (s *FileStorage) StoreTokenUpdate(r *mdm.Request, msg *mdm.TokenUpdate) err return nil } +func (s *FileStorage) StoreUserAuthenticate(r *mdm.Request, msg *mdm.UserAuthenticate) error { + e := s.newEnrollment(r.ID) + filename := UserAuthFilename + // if the DigestResponse is empty then this is the first (of two) + // UserAuthenticate messages depending on our response + if msg.DigestResponse != "" { + filename = UserAuthDigestFilename + } + return e.writeFile(filename, msg.Raw) +} + func (s *FileStorage) Disable(r *mdm.Request) error { if r.ParentID != "" { return errors.New("can only disable a device channel") diff --git a/storage/mysql/mysql.go b/storage/mysql/mysql.go index a9ba16b..664dc1a 100644 --- a/storage/mysql/mysql.go +++ b/storage/mysql/mysql.go @@ -147,6 +147,37 @@ UPDATE return err } +func (s *MySQLStorage) StoreUserAuthenticate(r *mdm.Request, msg *mdm.UserAuthenticate) error { + colName := "user_authenticate" + colAtName := "user_authenticate_at" + // if the DigestResponse is empty then this is the first (of two) + // UserAuthenticate messages depending on our response + if msg.DigestResponse != "" { + colName = "user_authenticate_digest" + colAtName = "user_authenticate_digest_at" + } + _, err := s.db.ExecContext( + r.Context, ` +INSERT INTO users + (id, device_id, user_short_name, user_long_name, `+colName+`, `+colAtName+`) +VALUES + (?, ?, ?, ?, ?, CURRENT_TIMESTAMP) AS new +ON DUPLICATE KEY +UPDATE + device_id = new.device_id, + user_short_name = new.user_short_name, + user_long_name = new.user_long_name, + `+colName+` = new.`+colName+`, + `+colAtName+` = new.`+colAtName+`;`, + r.ID, + r.ParentID, + nullEmptyString(msg.UserShortName), + nullEmptyString(msg.UserLongName), + msg.Raw, + ) + return err +} + func (s *MySQLStorage) Disable(r *mdm.Request) error { if r.ParentID != "" { return errors.New("can only disable a device channel") diff --git a/storage/mysql/schema.00002.sql b/storage/mysql/schema.00002.sql new file mode 100644 index 0000000..082e898 --- /dev/null +++ b/storage/mysql/schema.00002.sql @@ -0,0 +1,6 @@ +ALTER TABLE users ADD COLUMN user_authenticate TEXT NULL; +ALTER TABLE users ADD COLUMN user_authenticate_at TIMESTAMP NULL; +ALTER TABLE users ADD CONSTRAINT CHECK (user_authenticate IS NULL OR user_authenticate != ''); +ALTER TABLE users ADD COLUMN user_authenticate_digest TEXT NULL; +ALTER TABLE users ADD COLUMN user_authenticate_digest_at TIMESTAMP NULL; +ALTER TABLE users ADD CONSTRAINT CHECK (user_authenticate_digest IS NULL OR user_authenticate_digest != ''); diff --git a/storage/mysql/schema.sql b/storage/mysql/schema.sql index 434b887..8b3b24e 100644 --- a/storage/mysql/schema.sql +++ b/storage/mysql/schema.sql @@ -51,6 +51,12 @@ CREATE TABLE users ( token_update TEXT NULL, token_update_at TIMESTAMP NULL, + -- The last raw UserAuthenticate (and optional digest) for this user + user_authenticate TEXT NULL, + user_authenticate_at TIMESTAMP NULL, + user_authenticate_digest TEXT NULL, + user_authenticate_digest_at TIMESTAMP NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, @@ -63,7 +69,10 @@ CREATE TABLE users ( CHECK (user_short_name IS NULL OR user_short_name != ''), CHECK (user_long_name IS NULL OR user_long_name != ''), - CHECK (token_update IS NULL OR token_update != '') + CHECK (token_update IS NULL OR token_update != ''), + + CHECK (user_authenticate IS NULL OR user_authenticate != ''), + CHECK (user_authenticate_digest IS NULL OR user_authenticate_digest != '') ); diff --git a/storage/storage.go b/storage/storage.go index b3a292e..e0859e7 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -13,6 +13,7 @@ import ( type CheckinStore interface { StoreAuthenticate(r *mdm.Request, msg *mdm.Authenticate) error StoreTokenUpdate(r *mdm.Request, msg *mdm.TokenUpdate) error + StoreUserAuthenticate(r *mdm.Request, msg *mdm.UserAuthenticate) error Disable(r *mdm.Request) error }