소스 검색

Fixing errant writes to Entity.ID

Kenric Nugteren 4 달 전
부모
커밋
431f4e6a6a

+ 1 - 0
InABox.Core/DigitalForms/DataModel/DigitalFormDataModel.cs

@@ -19,6 +19,7 @@ namespace InABox.Core
             Entity.CommitChanges();
             Instance = new TInstance();
             Instance.ID = instanceid;
+            Instance.CommitChanges();
             Variables = variables;
             bRequiresLoad = true;
         }

+ 14 - 1
InABox.Core/Objects/Entity.cs

@@ -448,7 +448,20 @@ namespace InABox.Core
     {
     }
 
-
+    public static class EntityExtensions
+    {
+        /// <summary>
+        /// Sets the ID of an <see cref="Entity"/> while it is not observing, so that the change isn't registered.
+        /// </summary>
+        public static T SetID<T>(this T entity, Guid id)
+            where T : Entity
+        {
+            entity.SetObserving(false);
+            entity.ID = id;
+            entity.SetObserving(true);
+            return entity;
+        }
+    }
 
     public static class EntityFactory
     {

+ 327 - 328
InABox.Server/CredentialsCache.cs

@@ -3,456 +3,455 @@ using System.Security.Cryptography;
 using InABox.Core;
 using InABox.Database;
 
-namespace InABox.API
-{
+namespace InABox.API;
 
-    public static class CredentialsCache
-    {
 
-        private static ConcurrentBag<User>? cache;
-        
-        private static readonly User BYPASSED_USER = new() { ID = CoreUtils.FullGuid, UserID = "" };
+public static class CredentialsCache
+{
 
-        private static void EnsureCache(bool force)
-        {
-            if (cache == null || force)
-            {
-                var _table = DbFactory.NewProvider(Logger.Main).Query(
-                    null,
-                    Columns.None<User>().Add(
-                        x => x.ID,
-                        x => x.UserID,
-                        x => x.Password,
-                        x => x.Use2FA,
-                        x => x.Recipient2FA,
-                        x => x.TwoFactorAuthenticationType,
-                        x => x.AuthenticatorToken,
-                        x => x.PIN,
-                        x => x.SecurityGroup.ID,
-                        x => x.PasswordExpiration
-                    )
-                );
-                cache = new ConcurrentBag<User>();
-                foreach (var _row in _table.Rows)
-                    cache.Add(_row.ToObject<User>());
-            }
-        }
+    private static ConcurrentBag<User>? cache;
+    
+    private static readonly User BYPASSED_USER = new() { ID = CoreUtils.FullGuid, UserID = "" };
 
-        public static bool IsBypassed(string userid, string password)
+    private static void EnsureCache(bool force)
+    {
+        if (cache == null || force)
         {
-            //if ((userid == "FROGSOFTWARE") && (password == "FROGSOFTWARE"))
-            //    return true;
-
-            if (userid.IsBase64String() && password.IsBase64String())
-                try
-                {
-                    if (Encryption.Decrypt(userid, "wCq9rryEJEuHIifYrxRjxg", out var _decryptedUser) &&
-                        Encryption.Decrypt(password, "7mhvLnqMwkCAzN+zNGlyyg", out var _decryptedPass))
-                        if (long.TryParse(_decryptedUser, out var _userticks) && long.TryParse(_decryptedPass, out var _passticks))
-                            if (_userticks == _passticks)
-                            {
-                                var _remotedate = new DateTime(_userticks);
-                                var _localdate = DateTime.Now.ToUniversalTime();
-                                if (_remotedate >= _localdate.AddDays(-1) && _remotedate <= _localdate.AddDays(1))
-                                    return true;
-                            }
-                }
-                catch (Exception _exception)
-                {
-                    Logger.Send(LogType.Error, "", $"*** Unknown Error: {_exception.Message}\n{_exception.StackTrace}");
-                }
-
-            return false;
+            var _table = DbFactory.NewProvider(Logger.Main).Query(
+                null,
+                Columns.None<User>().Add(
+                    x => x.ID,
+                    x => x.UserID,
+                    x => x.Password,
+                    x => x.Use2FA,
+                    x => x.Recipient2FA,
+                    x => x.TwoFactorAuthenticationType,
+                    x => x.AuthenticatorToken,
+                    x => x.PIN,
+                    x => x.SecurityGroup.ID,
+                    x => x.PasswordExpiration
+                )
+            );
+            cache = new ConcurrentBag<User>();
+            foreach (var _row in _table.Rows)
+                cache.Add(_row.ToObject<User>());
         }
+    }
 
-        public static Guid Validate(Guid sessionId, out string? userId)
-        {
-            EnsureCache(false);
-            if(!sessions.TryGetValue(sessionId, out var _session) || !_session.Valid)
+    public static bool IsBypassed(string userid, string password)
+    {
+        //if ((userid == "FROGSOFTWARE") && (password == "FROGSOFTWARE"))
+        //    return true;
+
+        if (userid.IsBase64String() && password.IsBase64String())
+            try
             {
-                userId = null;
-                return Guid.Empty;
+                if (Encryption.Decrypt(userid, "wCq9rryEJEuHIifYrxRjxg", out var _decryptedUser) &&
+                    Encryption.Decrypt(password, "7mhvLnqMwkCAzN+zNGlyyg", out var _decryptedPass))
+                    if (long.TryParse(_decryptedUser, out var _userticks) && long.TryParse(_decryptedPass, out var _passticks))
+                        if (_userticks == _passticks)
+                        {
+                            var _remotedate = new DateTime(_userticks);
+                            var _localdate = DateTime.Now.ToUniversalTime();
+                            if (_remotedate >= _localdate.AddDays(-1) && _remotedate <= _localdate.AddDays(1))
+                                return true;
+                        }
             }
-            if(_session.Expiry < DateTime.Now)
+            catch (Exception _exception)
             {
-                sessions.Remove(sessionId, out var _);
-                userId = null;
-                return Guid.Empty;
+                Logger.Send(LogType.Error, "", $"*** Unknown Error: {_exception.Message}\n{_exception.StackTrace}");
             }
-            userId = _session.UserID;
-            return _session.User;
-        }
 
-        public static User? Validate(Guid sessionId)
+        return false;
+    }
+
+    public static Guid Validate(Guid sessionId, out string? userId)
+    {
+        EnsureCache(false);
+        if(!sessions.TryGetValue(sessionId, out var _session) || !_session.Valid)
         {
-            EnsureCache(false);
-            if (!sessions.TryGetValue(sessionId, out var _session) || !_session.Valid)
-            {
-                return null;
-            }
-            if (_session.Expiry < DateTime.Now)
-            {
-                sessions.Remove(sessionId, out var _);
-                return null;
-            }
-            if(_session.User == CoreUtils.FullGuid)
-            {
-                return BYPASSED_USER;
-            }
-            return cache?.FirstOrDefault(x => x.ID == _session.User);
+            userId = null;
+            return Guid.Empty;
         }
-
-        /// <summary>
-        /// Validate a given session, and refresh the session expiry if valid; use for database queries that need to refresh the user's expiry time.
-        /// </summary>
-        /// <param name="sessionId"></param>
-        /// <returns></returns>
-        public static User? ValidateAndRefresh(Guid sessionId)
+        if(_session.Expiry < DateTime.Now)
         {
-            var _user = Validate(sessionId);
-            if(_user is not null)
-            {
-                RefreshSessionExpiry(sessionId);
-            }
-            return _user;
+            sessions.Remove(sessionId, out var _);
+            userId = null;
+            return Guid.Empty;
         }
+        userId = _session.UserID;
+        return _session.User;
+    }
 
-        public static User? ValidateUser(string? pin)
+    public static User? Validate(Guid sessionId)
+    {
+        EnsureCache(false);
+        if (!sessions.TryGetValue(sessionId, out var _session) || !_session.Valid)
         {
-            if (String.IsNullOrWhiteSpace(pin))
-                return null;
-            EnsureCache(false);
-            return cache?.FirstOrDefault(x => string.Equals(x.PIN, pin));
+            return null;
         }
-
-        public static User? ValidateUser(string? userId, string? password)
+        if (_session.Expiry < DateTime.Now)
         {
-            if (String.IsNullOrWhiteSpace(userId) || String.IsNullOrWhiteSpace(password))
-                return null;
-
-            if (IsBypassed(userId, password))
-                return BYPASSED_USER;
-            
-            EnsureCache(false);
-            return cache?.FirstOrDefault(x => string.Equals(x.UserID, userId) && string.Equals(x.Password, password));
+            sessions.Remove(sessionId, out var _);
+            return null;
         }
-
-        public static void LogoutUser(Guid userGuid)
+        if(_session.User == CoreUtils.FullGuid)
         {
-            sessions.Remove(userGuid, out var _);
+            return BYPASSED_USER;
         }
+        return cache?.FirstOrDefault(x => x.ID == _session.User);
+    }
 
-        public static void Refresh(bool force)
+    /// <summary>
+    /// Validate a given session, and refresh the session expiry if valid; use for database queries that need to refresh the user's expiry time.
+    /// </summary>
+    /// <param name="sessionId"></param>
+    /// <returns></returns>
+    public static User? ValidateAndRefresh(Guid sessionId)
+    {
+        var _user = Validate(sessionId);
+        if(_user is not null)
         {
-            EnsureCache(force);
+            RefreshSessionExpiry(sessionId);
         }
+        return _user;
+    }
 
-        #region Sessions
+    public static User? ValidateUser(string? pin)
+    {
+        if (String.IsNullOrWhiteSpace(pin))
+            return null;
+        EnsureCache(false);
+        return cache?.FirstOrDefault(x => string.Equals(x.PIN, pin));
+    }
 
-        private class Session
-        {
-            public Guid User { get; init; }
+    public static User? ValidateUser(string? userId, string? password)
+    {
+        if (String.IsNullOrWhiteSpace(userId) || String.IsNullOrWhiteSpace(password))
+            return null;
+
+        if (IsBypassed(userId, password))
+            return BYPASSED_USER;
+        
+        EnsureCache(false);
+        return cache?.FirstOrDefault(x => string.Equals(x.UserID, userId) && string.Equals(x.Password, password));
+    }
 
-            public string UserID { get; init; } = "";
+    public static void LogoutUser(Guid userGuid)
+    {
+        sessions.Remove(userGuid, out var _);
+    }
 
-            public bool Valid { get; set; }
+    public static void Refresh(bool force)
+    {
+        EnsureCache(force);
+    }
 
-            public DateTime Expiry { get; set; }
-        }
-        // SessionID => Session
-        private static ConcurrentDictionary<Guid, Session> sessions = new();
+    #region Sessions
 
-        public static TimeSpan SessionExpiry = TimeSpan.FromHours(8);
+    private class Session
+    {
+        public Guid User { get; init; }
 
-        public static string? CacheFile { get; set; }
+        public string UserID { get; init; } = "";
 
-        public static IEnumerable<Guid> GetUserSessions(Guid userID)
-        {
-            return sessions.Where(x => x.Value.User == userID).Select(x => x.Key);
-        }
+        public bool Valid { get; set; }
 
-        private static void CheckSessionExpiries()
-        {
-            var _expiredkeys = sessions
-                .Where(x => x.Value.Expiry < DateTime.Now);
-            foreach (var _expiredkey in _expiredkeys)
-                sessions.TryRemove(_expiredkey);
-        }
+        public DateTime Expiry { get; set; }
+    }
+    // SessionID => Session
+    private static ConcurrentDictionary<Guid, Session> sessions = new();
 
-        public static void SetSessionExpiryTime(TimeSpan expiry)
-        {
-            SessionExpiry = expiry;
-        }
+    public static TimeSpan SessionExpiry = TimeSpan.FromHours(8);
+
+    public static string? CacheFile { get; set; }
+
+    public static IEnumerable<Guid> GetUserSessions(Guid userID)
+    {
+        return sessions.Where(x => x.Value.User == userID).Select(x => x.Key);
+    }
 
-        public static void RefreshSessionExpiry(Guid sessionID)
+    private static void CheckSessionExpiries()
+    {
+        var _expiredkeys = sessions
+            .Where(x => x.Value.Expiry < DateTime.Now);
+        foreach (var _expiredkey in _expiredkeys)
+            sessions.TryRemove(_expiredkey);
+    }
+
+    public static void SetSessionExpiryTime(TimeSpan expiry)
+    {
+        SessionExpiry = expiry;
+    }
+
+    public static void RefreshSessionExpiry(Guid sessionID)
+    {
+        if (sessions.TryGetValue(sessionID, out var _session))
         {
-            if (sessions.TryGetValue(sessionID, out var _session))
+            if (_session.Expiry != DateTime.MaxValue)
             {
-                if (_session.Expiry != DateTime.MaxValue)
-                {
-                    _session.Expiry = DateTime.Now + SessionExpiry;
-                }
+                _session.Expiry = DateTime.Now + SessionExpiry;
             }
         }
+    }
 
-        public static void SaveSessionCache()
+    public static void SaveSessionCache()
+    {
+        CheckSessionExpiries();
+        try
         {
-            CheckSessionExpiries();
-            try
+            if (CacheFile != null)
             {
-                if (CacheFile != null)
-                {
-                    var _json = Serialization.Serialize(sessions.Where(x => x.Value.Expiry != DateTime.MaxValue).ToDictionary(x => x.Key, x => x.Value));
-                    File.WriteAllText(CacheFile, _json);
-                }
-                else
-                {
-                    Logger.Send(LogType.Error, "", "Error while saving session cache: No Cache file set!");
-                }
+                var _json = Serialization.Serialize(sessions.Where(x => x.Value.Expiry != DateTime.MaxValue).ToDictionary(x => x.Key, x => x.Value));
+                File.WriteAllText(CacheFile, _json);
             }
-            catch (Exception _exception)
+            else
             {
-                Logger.Send(LogType.Error, "", $"Error while saving session cache: {_exception.Message}");
+                Logger.Send(LogType.Error, "", "Error while saving session cache: No Cache file set!");
             }
         }
+        catch (Exception _exception)
+        {
+            Logger.Send(LogType.Error, "", $"Error while saving session cache: {_exception.Message}");
+        }
+    }
 
-        public static void LoadSessionCache()
+    public static void LoadSessionCache()
+    {
+        try
         {
-            try
+            if (CacheFile != null)
             {
-                if (CacheFile != null)
-                {
-                    var _cachedData = Serialization.Deserialize<Dictionary<Guid, Session>>(new FileStream(CacheFile, FileMode.Open))?
-                        .Where(x => x.Value.Expiry != DateTime.MaxValue).ToDictionary(x => x.Key, x => x.Value);
-                    if (_cachedData != null)
-                        sessions.AddRange(_cachedData);
-                    CheckSessionExpiries();
-                }
-                else
-                {
-                    sessions = new();
-                }
+                var _cachedData = Serialization.Deserialize<Dictionary<Guid, Session>>(new FileStream(CacheFile, FileMode.Open))?
+                    .Where(x => x.Value.Expiry != DateTime.MaxValue).ToDictionary(x => x.Key, x => x.Value);
+                if (_cachedData != null)
+                    sessions.AddRange(_cachedData);
+                CheckSessionExpiries();
             }
-            catch (Exception)
+            else
             {
                 sessions = new();
             }
         }
-
-        public static void SetCacheFile(string cacheFile)
+        catch (Exception)
         {
-            CacheFile = cacheFile;
+            sessions = new();
         }
+    }
 
-        public static Guid NewSession(User user, bool valid = true, DateTime? expiry = null)
-        {
-            var _id = Guid.NewGuid();
+    public static void SetCacheFile(string cacheFile)
+    {
+        CacheFile = cacheFile;
+    }
 
-            sessions[_id] = new() { User = user.ID, Valid = valid, Expiry = expiry ?? (DateTime.Now + SessionExpiry), UserID = user.UserID };
+    public static Guid NewSession(User user, bool valid = true, DateTime? expiry = null)
+    {
+        var _id = Guid.NewGuid();
 
-            return _id;
-        }
+        sessions[_id] = new() { User = user.ID, Valid = valid, Expiry = expiry ?? (DateTime.Now + SessionExpiry), UserID = user.UserID };
 
-        public static bool SessionExists(Guid session)
-        {
-            return sessions.ContainsKey(session);
-        }
+        return _id;
+    }
 
-        #endregion
+    public static bool SessionExists(Guid session)
+    {
+        return sessions.ContainsKey(session);
+    }
 
-        #region 2FA
+    #endregion
 
-        private class AuthenticationCode
-        {
-            public string Code { get; set; }
+    #region 2FA
 
-            public DateTime Expiry { get; set; }
+    private class AuthenticationCode
+    {
+        public string Code { get; set; }
 
-            public int TriesLeft { get; set; }
+        public DateTime Expiry { get; set; }
 
-            public AuthenticationCode(string code, DateTime expiry)
-            {
-                Code = code;
-                Expiry = expiry;
-                TriesLeft = TWO_FA_TRIES;
-            }
+        public int TriesLeft { get; set; }
+
+        public AuthenticationCode(string code, DateTime expiry)
+        {
+            Code = code;
+            Expiry = expiry;
+            TriesLeft = TWO_FA_TRIES;
         }
+    }
 
-        private static readonly ConcurrentDictionary<Guid, AuthenticationCode> authenticationCodes = new();
+    private static readonly ConcurrentDictionary<Guid, AuthenticationCode> authenticationCodes = new();
 
-        private static readonly int TWO_FA_TRIES = 3;
+    private static readonly int TWO_FA_TRIES = 3;
 
-        public static readonly int CODE_LENGTH = 6;
+    public static readonly int CODE_LENGTH = 6;
 
-        private static readonly TimeSpan EXPIRY2_FA_CODE_TIME = TimeSpan.FromMinutes(15);
+    private static readonly TimeSpan EXPIRY2_FA_CODE_TIME = TimeSpan.FromMinutes(15);
 
-        private static Dictionary<SMSProviderType, ISMSProvider> SMSProviders { get; set; } = new();
+    private static Dictionary<SMSProviderType, ISMSProvider> SMSProviders { get; set; } = new();
 
-        public static void AddSMSProvider(ISMSProvider provider) 
-        {
-            SMSProviders.Add(provider.ProviderType, provider);
-        }
+    public static void AddSMSProvider(ISMSProvider provider) 
+    {
+        SMSProviders.Add(provider.ProviderType, provider);
+    }
+
+    private static string GenerateCode()
+    {
+        var _random = new Random(DateTime.Now.Millisecond);
+        var _code = "";
+        for (int _char = 0; _char < CODE_LENGTH; _char++)
+            _code += _random.Next(10).ToString();
+        return _code;
+    }
 
-        private static string GenerateCode()
+    public static Guid? SendCode(Guid userGuid, out string? recipient)
+    {
+        EnsureCache(false);
+        var _user = cache?.FirstOrDefault(x => x.ID == userGuid);
+        if(_user == null)
         {
-            var _random = new Random(DateTime.Now.Millisecond);
-            var _code = "";
-            for (int _char = 0; _char < CODE_LENGTH; _char++)
-                _code += _random.Next(10).ToString();
-            return _code;
+            Logger.Send(LogType.Error, "", "Cannot send code; user does not exist!");
+            recipient = null;
+            return null;
         }
 
-        public static Guid? SendCode(Guid userGuid, out string? recipient)
+        var _newSession = NewSession(_user, false);
+        Logger.Send(LogType.Information, "", $"New login session {_newSession} for {_user.UserID}");
+
+        if (_user.TwoFactorAuthenticationType != TwoFactorAuthenticationType.GoogleAuthenticator)
         {
-            EnsureCache(false);
-            var _user = cache?.FirstOrDefault(x => x.ID == userGuid);
-            if(_user == null)
+            var _smsProvider = SMSProviders
+                .Where(x => x.Value.TwoFactorAuthenticationType == _user.TwoFactorAuthenticationType)
+                .Select(x => x.Value).FirstOrDefault();
+            if (_smsProvider == null)
             {
-                Logger.Send(LogType.Error, "", "Cannot send code; user does not exist!");
+                Logger.Send(LogType.Error, "", "Cannot send code; user requests a 2FA method which is not supported!");
                 recipient = null;
                 return null;
             }
 
-            var _newSession = NewSession(_user, false);
-            Logger.Send(LogType.Information, "", $"New login session {_newSession} for {_user.UserID}");
+            var _code = GenerateCode();
 
-            if (_user.TwoFactorAuthenticationType != TwoFactorAuthenticationType.GoogleAuthenticator)
-            {
-                var _smsProvider = SMSProviders
-                    .Where(x => x.Value.TwoFactorAuthenticationType == _user.TwoFactorAuthenticationType)
-                    .Select(x => x.Value).FirstOrDefault();
-                if (_smsProvider == null)
-                {
-                    Logger.Send(LogType.Error, "", "Cannot send code; user requests a 2FA method which is not supported!");
-                    recipient = null;
-                    return null;
-                }
+            Logger.Send(LogType.Information, "", $"Code for session {userGuid} is {_code}");
+            authenticationCodes[_newSession] = new AuthenticationCode(_code, DateTime.Now + EXPIRY2_FA_CODE_TIME);
 
-                var _code = GenerateCode();
-
-                Logger.Send(LogType.Information, "", $"Code for session {userGuid} is {_code}");
-                authenticationCodes[_newSession] = new AuthenticationCode(_code, DateTime.Now + EXPIRY2_FA_CODE_TIME);
-
-                var _recipientAddress = _user.Recipient2FA;
-                if (_smsProvider.SendMessage(_recipientAddress, $"Your authentication code is {_code}. This code will expire in {EXPIRY2_FA_CODE_TIME.Minutes} minutes."))
-                {
-                    Logger.Send(LogType.Information, "", "Code sent!");
+            var _recipientAddress = _user.Recipient2FA;
+            if (_smsProvider.SendMessage(_recipientAddress, $"Your authentication code is {_code}. This code will expire in {EXPIRY2_FA_CODE_TIME.Minutes} minutes."))
+            {
+                Logger.Send(LogType.Information, "", "Code sent!");
 
-                    var _first = _recipientAddress[..3];
-                    var _last = _recipientAddress[^3..];
+                var _first = _recipientAddress[..3];
+                var _last = _recipientAddress[^3..];
 
-                    recipient = _first + new string('*', _recipientAddress.Length - 6) + _last;
-                    return _newSession;
-                }
-                else
-                {
-                    Logger.Send(LogType.Information, "", "Code failed to send!");
-                    recipient = null;
-                    return null;
-                }
+                recipient = _first + new string('*', _recipientAddress.Length - 6) + _last;
+                return _newSession;
             }
             else
             {
-                Logger.Send(LogType.Information, "", $"Google authenticator is being used");
-                recipient = "Google Authenticator";
-                return _newSession;
+                Logger.Send(LogType.Information, "", "Code failed to send!");
+                recipient = null;
+                return null;
             }
         }
+        else
+        {
+            Logger.Send(LogType.Information, "", $"Google authenticator is being used");
+            recipient = "Google Authenticator";
+            return _newSession;
+        }
+    }
 
-        private static readonly int CODE_MODULO = (int)Math.Pow(10, CODE_LENGTH);
+    private static readonly int CODE_MODULO = (int)Math.Pow(10, CODE_LENGTH);
 
-        private static string GenerateGoogleAuthenticatorCode(long time, byte[] key)
+    private static string GenerateGoogleAuthenticatorCode(long time, byte[] key)
+    {
+        var _window = time / 30;
+        var _hex = _window.ToString("x");
+        if (_hex.Length < 16)
         {
-            var _window = time / 30;
-            var _hex = _window.ToString("x");
-            if (_hex.Length < 16)
-            {
-                _hex = _hex.PadLeft(16, '0');
-            }
-            var _bytes = Convert.FromHexString(_hex);
-            var _hash = new HMACSHA1(key).ComputeHash(_bytes);
+            _hex = _hex.PadLeft(16, '0');
+        }
+        var _bytes = Convert.FromHexString(_hex);
+        var _hash = new HMACSHA1(key).ComputeHash(_bytes);
 
-            var _offset = _hash.Last() & 0xf;
-            var _selected = new byte[4];
-            Buffer.BlockCopy(_hash, _offset, _selected, 0, 4);
-            if (BitConverter.IsLittleEndian)
-            {
-                Array.Reverse(_selected);
-            }
+        var _offset = _hash.Last() & 0xf;
+        var _selected = new byte[4];
+        Buffer.BlockCopy(_hash, _offset, _selected, 0, 4);
+        if (BitConverter.IsLittleEndian)
+        {
+            Array.Reverse(_selected);
+        }
 
-            var _integer = BitConverter.ToInt32(_selected, 0);
-            var _truncated = _integer & 0x7fffffff;
+        var _integer = BitConverter.ToInt32(_selected, 0);
+        var _truncated = _integer & 0x7fffffff;
 
-            return (_truncated % CODE_MODULO).ToString().PadLeft(CODE_LENGTH, '0');
-        }
+        return (_truncated % CODE_MODULO).ToString().PadLeft(CODE_LENGTH, '0');
+    }
 
-        private static bool CheckAuthenticationCode(byte[] token, string code)
+    private static bool CheckAuthenticationCode(byte[] token, string code)
+    {
+        var _time = DateTimeOffset.Now.ToUnixTimeSeconds();
+        for (long _l = _time - 30; _l <= _time; _l += 30)
         {
-            var _time = DateTimeOffset.Now.ToUnixTimeSeconds();
-            for (long _l = _time - 30; _l <= _time; _l += 30)
+            if(GenerateGoogleAuthenticatorCode(_l, token) == code)
             {
-                if(GenerateGoogleAuthenticatorCode(_l, token) == code)
-                {
-                    return true;
-                }
+                return true;
             }
-            return false;
         }
+        return false;
+    }
 
-        public static bool ValidateCode(Guid sessionID, string code)
+    public static bool ValidateCode(Guid sessionID, string code)
+    {
+        if (!sessions.TryGetValue(sessionID, out var _session))
         {
-            if (!sessions.TryGetValue(sessionID, out var _session))
-            {
-                return false;
-            }
+            return false;
+        }
 
-            bool _valid;
-            if(authenticationCodes.TryGetValue(sessionID, out var _result))
+        bool _valid;
+        if(authenticationCodes.TryGetValue(sessionID, out var _result))
+        {
+            if (_result.Code != code)
             {
-                if (_result.Code != code)
-                {
-                    _result.TriesLeft--;
-                    if (_result.TriesLeft == 0)
-                    {
-                        authenticationCodes.Remove(sessionID, out _);
-                    }
-                    _valid = false;
-                }
-                else if (_result.Expiry < DateTime.Now)
+                _result.TriesLeft--;
+                if (_result.TriesLeft == 0)
                 {
                     authenticationCodes.Remove(sessionID, out _);
-                    _valid = false;
-                }
-                else
-                {
-                    _valid = true;
                 }
+                _valid = false;
+            }
+            else if (_result.Expiry < DateTime.Now)
+            {
+                authenticationCodes.Remove(sessionID, out _);
+                _valid = false;
             }
             else
             {
-                var _user = cache?.FirstOrDefault(x => x.ID == _session.User);
-                if (_user?.TwoFactorAuthenticationType == TwoFactorAuthenticationType.GoogleAuthenticator)
-                {
-                    _valid = CheckAuthenticationCode(_user.AuthenticatorToken, code);
-                }
-                else
-                {
-                    _valid = false;
-                }
+                _valid = true;
             }
-
-            if (_valid)
+        }
+        else
+        {
+            var _user = cache?.FirstOrDefault(x => x.ID == _session.User);
+            if (_user?.TwoFactorAuthenticationType == TwoFactorAuthenticationType.GoogleAuthenticator)
             {
-                _session.Valid = true;
-
-                return true;
+                _valid = CheckAuthenticationCode(_user.AuthenticatorToken, code);
             }
             else
             {
-                return false;
+                _valid = false;
             }
         }
 
-        #endregion
+        if (_valid)
+        {
+            _session.Valid = true;
+
+            return true;
+        }
+        else
+        {
+            return false;
+        }
     }
+
+    #endregion
 }

+ 1 - 0
InABox.Server/RPC/Handlers/Delete.cs

@@ -24,6 +24,7 @@ namespace InABox.Rpc
             {
                 var item = (Activator.CreateInstance(parameters.Type) as Entity)!;
                 item.ID = id;
+                item.CommitChanges();
                 items.Add(item);
             }
             store.Delete(items, parameters.AuditNote);

+ 11 - 6
inabox.wpf/DigitalForms/DigitalFormGrid.cs

@@ -145,13 +145,18 @@ namespace InABox.DynamicGrid
                 ID = document.ID;
             }
 
-            public Document ToDocument() => new Document
+            public Document ToDocument()
             {
-                FileName = FileName,
-                Data = Data,
-                TimeStamp = DateTime.Now,
-                ID = ID
-            };
+                var doc = new Document()
+                {
+                    ID = ID
+                };
+                doc.CommitChanges();
+                doc.FileName = FileName;
+                doc.Data = Data;
+                doc.TimeStamp = DateTime.Now;
+                return doc;
+            }
         }
     }
 

+ 2 - 0
inabox.wpf/DynamicGrid/Grids/DynamicImportForm.xaml.cs

@@ -367,7 +367,9 @@ namespace InABox.DynamicGrid
                 var id = _importer.ID;
                 var entityid = _importer.EntityID;
                 Serialization.DeserializeInto(json, _importer);
+                _importer.SetObserving(false);
                 _importer.ID = id;
+                _importer.SetObserving(true);
                 _importer.EntityID = entityid;
                 LoadData();
             }