/// <summary> /// Get the Key/IV secret from the cookies and generate the parts that don't yet exist /// and directly save it to the cookies collection /// </summary> /// <returns></returns> internal static Crypt.Secret GetSecret <M>(this IStorageControl <M> controlStorage) where M : class, new() { Crypt.Secret secret; string keyString; var cookie = controlStorage.Request.Cookies[controlStorage.StorageID()]; if (cookie != null) { keyString = cookie["key"]; if (keyString != null) { var key = Convert.FromBase64String(keyString); secret = Crypt.NewSecret(key); } else { secret = Crypt.NewSecret(); } } else { secret = Crypt.NewSecret(); } controlStorage.Response.Cookies[controlStorage.StorageID()]["key"] = Convert.ToBase64String(secret.Key); return(secret); }
/// <summary> /// Enumerate all contained sub-controls of type IStorageControl /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> /// <returns></returns> internal static IEnumerable <IStorageControl <M> > Subcontrols <M>(this IStorageControl <M> controlStorage) where M : new() { return (from FieldInfo c in controlStorage.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic) where typeof(IStorageControl <M>).IsAssignableFrom(c.FieldType) select(IStorageControl <M>) c.GetValue(controlStorage)); }
/// <summary> /// Get whether to encrypt database storage in this precedence: /// 1. Encryption enforced in Web.config if key="EncryptDatabaseStorage" value="True" /// 2. Global Web.config override in controlStorageExtension.SessionStorage e.g. from unit tests /// 3. Defaults to false /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> /// <returns></returns> public static bool GetEncryptDatabaseStorage <M>(this IStorageControl <M> controlStorage) where M : new() { var encryptConfig = GetEncryptDatabaseStorage(); bool encryptOverride = EncryptDatabaseStorage ?? false; return(encryptConfig || encryptOverride); }
/// <summary> /// Recursively add a reference to the global M and to all sub-controls /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> internal static void PropagateMain <M>(this IStorageControl <M> controlStorage, M main) where M : new() { controlStorage.Main = main; foreach (IStorageControl <M> subcontrol in controlStorage.Subcontrols()) { subcontrol.PropagateMain(main); } }
/// <summary> /// Predefined event handler for the ShareButton to respond with an /// SQL INSERT script for the current Main, just add the button like this: /// <asplib:ShareButton ID="shareButton" runat="server" /// OnServerClick="shareButton_Click" /> /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> /// <param name="sender"></param> /// <param name="e"></param> public static void shareButton_Click <M>(this IStorageControl <M> controlStorage, object sender, EventArgs e) where M : class, new() { var mainRow = new Main(); mainRow.SetInstance(controlStorage.Main); // serialize without filter controlStorage.Response.Clear(); controlStorage.Response.AddHeader("Content-Type", "application/sql"); controlStorage.Response.Write(mainRow.InsertSQL()); controlStorage.Response.End(); }
/// <summary> /// To be called in Page_Load(): /// Load the M object from the storage, propagate it to all sub-controls /// and recursively hide them all below the main control. /// Also sets a global reference to the main control in IIE for testing. /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> public static void LoadMain <M>(this IStorageControl <M> controlStorage) where M : class, new() { controlStorage.ClearIfRequested(); Guid sessionOverride; if (!controlStorage.IsPostBack && Guid.TryParse(controlStorage.Request.QueryString["session"], out sessionOverride)) { controlStorage.Main = Main.LoadMain <M>(sessionOverride); } else { switch (controlStorage.GetStorage()) { case Storage.ViewState: controlStorage.Main = (M)controlStorage.ViewState[controlStorage.StorageID()]; break; case Storage.Session: controlStorage.Main = (M)controlStorage.Session[controlStorage.StorageID()]; break; case Storage.Database: var cookie = controlStorage.Request.Cookies[controlStorage.StorageID()]; if (cookie != null) { Guid session; if (Guid.TryParse(cookie["session"], out session)) { Func <byte[], byte[]> filter = null; if (controlStorage.GetEncryptDatabaseStorage()) { filter = x => Crypt.Decrypt(controlStorage.GetSecret(), x); // closure } controlStorage.Main = Main.LoadMain <M>(session, filter); } } break; } } if (controlStorage.Main == null) { controlStorage.Main = new M(); } CurrentMain = controlStorage.Main; ControlRootExtension.SetRoot(controlStorage); controlStorage.PropagateMain(controlStorage.Main); }
/// <summary> /// Get the actual storage type to use in this precedence: /// 1. Local session storage if set by SetStorage /// 2. Global config override in controlStorageExtension.SessionStorage e.g. from unit tests /// 3. Configured storage in key="SessionStorage" value="Database" /// 4. Defaults to ViewState /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> /// <returns></returns> public static Storage GetStorage <M>(this IStorageControl <M> controlStorage) where M : new() { var storage = controlStorage.SessionStorage; // Web Control property if (storage == null) { storage = SessionStorage; // static config override } if (storage == null) { var configStorage = ConfigurationManager.AppSettings["SessionStorage"]; storage = String.IsNullOrWhiteSpace(configStorage) ? Storage.ViewState : (Storage)Enum.Parse(typeof(Storage), configStorage); } return((Storage)storage); }
/// <summary> /// To be called at the end of OnPreRender(): /// Persist the in this page life-cycle stage immutable Main object. /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> public static void SaveMain <M>(this IStorageControl <M> controlStorage) where M : class, new() { var storage = controlStorage.GetStorage(); Trace.Assert(controlStorage.Main != null, "SaveMain() without preceding LoadMain()"); switch (storage) { case Storage.ViewState: controlStorage.ViewState[controlStorage.StorageID()] = controlStorage.Main; break; case Storage.Session: controlStorage.Session[controlStorage.StorageID()] = controlStorage.Main; break; case Storage.Database: Guid session = Guid.NewGuid(); // cannot exist in the database -> will get a new one on SaveMain() var cookie = controlStorage.Request.Cookies[controlStorage.StorageID()]; if (cookie != null) { Guid.TryParse(controlStorage.Request.Cookies[controlStorage.StorageID()]["session"], out session); } Func <byte[], byte[]> filter = null; if (controlStorage.GetEncryptDatabaseStorage()) { filter = x => Crypt.Encrypt(controlStorage.GetSecret(), x); // closure } session = Main.SaveMain(controlStorage.Main, session, filter); var configDays = ConfigurationManager.AppSettings["DatabaseStorageExpires"]; var days = String.IsNullOrWhiteSpace(configDays) ? 1 : int.Parse(configDays); controlStorage.Response.Cookies[controlStorage.StorageID()]["session"] = session.ToString(); controlStorage.Response.Cookies[controlStorage.StorageID()].Expires = DateTime.Now.AddDays(days); break; } }
/// <summary> /// StorageID-String unique to the control instance to store/retrieve/clear the M /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> /// <returns></returns> internal static string StorageID <M>(this IStorageControl <M> controlStorage) where M : new() { return(controlStorage.ClientID + "_Main"); }
/// <summary> /// Hook to clear the storage for that control with ?clear=true /// ViewState is reset anyway on GET requests, therefore NOP in that casefa /// GET-arguments: /// clear=[true|false] triggers clearing the storage /// endresponse=[true|false] whether the page at the given URL /// storage=[Session|Database] clears the selected storage type regardless of config /// </summary> /// <typeparam name="M"></typeparam> /// <param name="controlStorage"></param> /// <returns></returns> internal static void ClearIfRequested <M>(this IStorageControl <M> controlStorage) where M : new() { if (!controlStorage.IsPostBack) { bool clear = false; bool.TryParse(controlStorage.Request.QueryString["clear"], out clear); if (clear) { Storage storage; Enum.TryParse <Storage>(controlStorage.Request.QueryString["storage"], true, out storage); if (storage == Storage.ViewState) // no meaningful override given { storage = controlStorage.GetStorage(); } bool endresponse = false; bool.TryParse(controlStorage.Request.QueryString["endresponse"], out endresponse); switch (storage) { case Storage.ViewState: break; case Storage.Session: controlStorage.Session.Remove(controlStorage.StorageID()); break; case Storage.Database: // delete from the database and expire the cookie var cookie = controlStorage.Request.Cookies[controlStorage.StorageID()]; if (cookie != null) { Guid session; Guid.TryParse(controlStorage.Request.Cookies[controlStorage.StorageID()]["session"], out session); using (var db = new ASP_DBEntities()) { var sql = @" DELETE FROM Main WHERE session = @session "; var param = new SqlParameter("session", session); db.Database.ExecuteSqlCommand(sql, param); } controlStorage.Request.Cookies[controlStorage.StorageID()].Expires = DateTime.Now.AddDays(-1); } break; default: throw new NotImplementedException(String.Format( "Storage {0}", storage)); } if (endresponse) { controlStorage.Response.Clear(); controlStorage.Response.End(); } } } }
/// <summary> /// Set the control-local session storage type from code /// </summary> /// <typeparam name="M"></typeparam> /// <typeparam name="F"></typeparam> /// <typeparam name="S"></typeparam> /// <param name="controlStorage"></param> /// <param name="storage"></param> public static void SetStorage <M>(this IStorageControl <M> controlStorage, Storage storage) where M : new() { controlStorage.SessionStorage = storage; }
/// <summary> /// Set the control-local session storage type from an .ascx attribute string. Case insensitive. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="controlStorage"></param> /// <param name="storage"></param> public static void SetStorage <M>(this IStorageControl <M> controlStorage, string storage) where M : new() { controlStorage.SessionStorage = (Storage)Enum.Parse(typeof(Storage), storage, true); }