2008-11-06 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / System.Web / System.Web.Security / FormsAuthentication.cs
index 1383aa2996886079dc835762492ceddc2f120019..2b9fb52057e913afbb863747753db08cce05da05 100644 (file)
@@ -37,6 +37,7 @@ using System.Text;
 using System.Web;
 using System.Web.Configuration;
 using System.Web.Util;
+using System.Globalization;
 
 namespace System.Web.Security
 {
@@ -48,6 +49,52 @@ namespace System.Web.Security
                const int SHA1_hash_size = 20;
 
                static string authConfigPath = "system.web/authentication";
+               static string machineKeyConfigPath = "system.web/machineKey";
+#if TARGET_J2EE
+               const string Forms_initialized = "Forms.initialized";
+               const string Forms_cookieName = "Forms.cookieName";
+               const string Forms_cookiePath = "Forms.cookiePath";
+               const string Forms_timeout = "Forms.timeout";
+               const string Forms_protection = "Forms.protection";
+               const string Forms_init_vector = "Forms.init_vector";
+               static bool initialized
+               {
+                       get {
+                               object o = AppDomain.CurrentDomain.GetData (Forms_initialized);
+                               return o != null ? (bool) o : false;
+                       }
+                       set { AppDomain.CurrentDomain.SetData (Forms_initialized, value); }
+               }
+               static string cookieName
+               {
+                       get { return (string) AppDomain.CurrentDomain.GetData (Forms_cookieName); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_cookieName, value); }
+               }
+               static string cookiePath
+               {
+                       get { return (string) AppDomain.CurrentDomain.GetData (Forms_cookiePath); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_cookiePath, value); }
+               }
+               static int timeout
+               {
+                       get {
+                               object o = AppDomain.CurrentDomain.GetData (Forms_timeout);
+                               return o != null ? (int) o : 0;
+                       }
+                       set { AppDomain.CurrentDomain.SetData (Forms_timeout, value); }
+               }
+               static FormsProtectionEnum protection
+               {
+                       get { return (FormsProtectionEnum) AppDomain.CurrentDomain.GetData (Forms_protection); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_protection, value); }
+               }
+               static byte [] init_vector
+               {
+                       get { return (byte []) AppDomain.CurrentDomain.GetData (Forms_init_vector); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_init_vector, value); }
+               }
+               static object locker = new object ();
+#else
                static bool initialized;
                static string cookieName;
                static string cookiePath;
@@ -55,17 +102,85 @@ namespace System.Web.Security
                static FormsProtectionEnum protection;
                static object locker = new object ();
                static byte [] init_vector; // initialization vector used for 3DES
+#endif
 #if NET_1_1
+#if TARGET_J2EE
+               const string Forms_requireSSL = "Forms.requireSSL";
+               const string Forms_slidingExpiration = "Forms.slidingExpiration";
+
+               static bool requireSSL
+               {
+                       get {
+                               object o = AppDomain.CurrentDomain.GetData (Forms_requireSSL);
+                               return o != null ? (bool) o : false;
+                       }
+                       set { AppDomain.CurrentDomain.SetData (Forms_requireSSL, value); }
+               }
+               static bool slidingExpiration
+               {
+                       get {
+                               object o = AppDomain.CurrentDomain.GetData (Forms_slidingExpiration);
+                               return o != null ? (bool) o : false;
+                       }
+                       set { AppDomain.CurrentDomain.SetData (Forms_slidingExpiration, value); }
+               }
+#else
                static bool requireSSL;
                static bool slidingExpiration;
 #endif
+#endif
 #if NET_2_0
+#if TARGET_J2EE
+               const string Forms_cookie_domain = "Forms.cookie_domain";
+               const string Forms_cookie_mode = "Forms.cookie_mode";
+               const string Forms_cookies_supported = "Forms.cookies_supported";
+               const string Forms_default_url = "Forms.default_url";
+               const string Forms_enable_crossapp_redirects = "Forms.enable_crossapp_redirects";
+               const string Forms_login_url = "Forms.login_url";
+               static string cookie_domain
+               {
+                       get { return (string) AppDomain.CurrentDomain.GetData (Forms_cookie_domain); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_cookie_domain, value); }
+               }
+               static HttpCookieMode cookie_mode
+               {
+                       get { return (HttpCookieMode) AppDomain.CurrentDomain.GetData (Forms_cookie_mode); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_cookie_mode, value); }
+               }
+               static bool cookies_supported
+               {
+                       get {
+                               object o = AppDomain.CurrentDomain.GetData (Forms_cookies_supported);
+                               return o != null ? (bool) o : false;
+                       }
+                       set { AppDomain.CurrentDomain.SetData (Forms_cookies_supported, value); }
+               }
+               static string default_url
+               {
+                       get { return (string) AppDomain.CurrentDomain.GetData (Forms_default_url); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_default_url, value); }
+               }
+               static bool enable_crossapp_redirects
+               {
+                       get {
+                               object o = AppDomain.CurrentDomain.GetData (Forms_enable_crossapp_redirects);
+                               return o != null ? (bool) o : false;
+                       }
+                       set { AppDomain.CurrentDomain.SetData (Forms_enable_crossapp_redirects, value); }
+               }
+               static string login_url
+               {
+                       get { return (string) AppDomain.CurrentDomain.GetData (Forms_login_url); }
+                       set { AppDomain.CurrentDomain.SetData (Forms_login_url, value); }
+               }
+#else
                static string cookie_domain;
                static HttpCookieMode cookie_mode;
                static bool cookies_supported;
                static string default_url;
                static bool enable_crossapp_redirects;
                static string login_url;
+#endif
 #endif
                // same names and order used in xsp
                static string [] indexFiles = { "index.aspx",
@@ -74,9 +189,6 @@ namespace System.Web.Security
                                                "index.html",
                                                "index.htm" };
 
-#if NET_2_0
-               [Obsolete]
-#endif
                public FormsAuthentication ()
                {
                }
@@ -91,9 +203,20 @@ namespace System.Web.Security
                        if (context == null)
                                throw new HttpException ("Context is null!");
 
+                       name = name.ToLower ();
+#if NET_2_0
+                       AuthenticationSection section = (AuthenticationSection) WebConfigurationManager.GetSection (authConfigPath);
+                       FormsAuthenticationCredentials config = section.Forms.Credentials;
+                       FormsAuthenticationUser user = config.Users[name];
+                       string stored = null;
+
+                       if (user != null)
+                               stored = user.Password;
+#else
                        AuthConfig config = context.GetConfig (authConfigPath) as AuthConfig;
                        Hashtable users = config.CredentialUsers;
                        string stored = users [name] as string;
+#endif
                        if (stored == null)
                                return false;
 
@@ -112,37 +235,63 @@ namespace System.Web.Security
                        return (password == stored);
                }
 
+#if NET_2_0
+               static byte [] GetDecryptionKey (MachineKeySection config)
+               {
+                       return MachineKeySectionUtils.DecryptionKey192Bits (config);
+               }
+#else
+               static byte [] GetDecryptionKey (MachineKeyConfig config)
+               {
+                       return config.DecryptionKey192Bits;
+               }
+#endif
+               
                static FormsAuthenticationTicket Decrypt2 (byte [] bytes)
                {
                        if (protection == FormsProtectionEnum.None)
                                return FormsAuthenticationTicket.FromByteArray (bytes);
 
-                       MachineKeyConfig config = HttpContext.GetAppConfig ("system.web/machineKey") as MachineKeyConfig;
+#if NET_2_0
+                       MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetSection (machineKeyConfigPath);
+#else
+                       MachineKeyConfig config = HttpContext.GetAppConfig (machineKeyConfigPath) as MachineKeyConfig;
+#endif
                        bool all = (protection == FormsProtectionEnum.All);
 
                        byte [] result = bytes;
                        if (all || protection == FormsProtectionEnum.Encryption) {
                                ICryptoTransform decryptor;
-                               decryptor = TripleDES.Create ().CreateDecryptor (config.DecryptionKey192Bits, init_vector);
+                               decryptor = TripleDES.Create ().CreateDecryptor (GetDecryptionKey (config), init_vector);
                                result = decryptor.TransformFinalBlock (bytes, 0, bytes.Length);
                                bytes = null;
                        }
 
                        if (all || protection == FormsProtectionEnum.Validation) {
                                int count;
+                               MachineKeyValidation validationType;
 
-                               if (config.ValidationType == MachineKeyValidation.MD5)
+#if NET_2_0
+                               validationType = config.Validation;
+#else
+                               validationType = config.ValidationType;
+#endif
+                               if (validationType == MachineKeyValidation.MD5)
                                        count = MD5_hash_size;
                                else
                                        count = SHA1_hash_size; // 3DES and SHA1
 
+#if NET_2_0
+                               byte [] vk = MachineKeySectionUtils.ValidationKeyBytes (config);
+#else
                                byte [] vk = config.ValidationKey;
+#endif
                                byte [] mix = new byte [result.Length - count + vk.Length];
                                Buffer.BlockCopy (result, 0, mix, 0, result.Length - count);
                                Buffer.BlockCopy (vk, 0, mix, result.Length - count, vk.Length);
 
                                byte [] hash = null;
-                               switch (config.ValidationType) {
+                               switch (validationType) {
                                case MachineKeyValidation.MD5:
                                        hash = MD5.Create ().ComputeHash (mix);
                                        break;
@@ -174,7 +323,11 @@ namespace System.Web.Security
                        Initialize ();
 
                        FormsAuthenticationTicket ticket;
+#if NET_2_0
+                       byte [] bytes = MachineKeySectionUtils.GetBytes (encryptedTicket, encryptedTicket.Length);
+#else
                        byte [] bytes = MachineKeyConfig.GetBytes (encryptedTicket, encryptedTicket.Length);
+#endif
                        try {
                                ticket = Decrypt2 (bytes);
                        } catch (Exception) {
@@ -195,16 +348,30 @@ namespace System.Web.Security
                                return GetHexString (ticket_bytes);
 
                        byte [] result = ticket_bytes;
-                       MachineKeyConfig config = HttpContext.GetAppConfig ("system.web/machineKey") as MachineKeyConfig;
+#if NET_2_0
+                       MachineKeySection config = (MachineKeySection) WebConfigurationManager.GetSection (machineKeyConfigPath);
+#else
+                       MachineKeyConfig config = HttpContext.GetAppConfig (machineKeyConfigPath) as MachineKeyConfig;
+#endif
                        bool all = (protection == FormsProtectionEnum.All);
                        if (all || protection == FormsProtectionEnum.Validation) {
                                byte [] valid_bytes = null;
+#if NET_2_0
+                               byte [] vk = MachineKeySectionUtils.ValidationKeyBytes (config);
+#else
                                byte [] vk = config.ValidationKey;
+#endif
                                byte [] mix = new byte [ticket_bytes.Length + vk.Length];
                                Buffer.BlockCopy (ticket_bytes, 0, mix, 0, ticket_bytes.Length);
                                Buffer.BlockCopy (vk, 0, mix, result.Length, vk.Length);
 
-                               switch (config.ValidationType) {
+                               switch (
+#if NET_2_0
+                                       config.Validation
+#else
+                                       config.ValidationType
+#endif
+                                       ) {
                                case MachineKeyValidation.MD5:
                                        valid_bytes = MD5.Create ().ComputeHash (mix);
                                        break;
@@ -224,7 +391,7 @@ namespace System.Web.Security
 
                        if (all || protection == FormsProtectionEnum.Encryption) {
                                ICryptoTransform encryptor;
-                               encryptor = TripleDES.Create ().CreateEncryptor (config.DecryptionKey192Bits, init_vector);
+                               encryptor = TripleDES.Create ().CreateEncryptor (GetDecryptionKey (config), init_vector);
                                result = encryptor.TransformFinalBlock (result, 0, result.Length);
                        }
 
@@ -264,7 +431,15 @@ namespace System.Web.Security
                        if (!createPersistentCookie)
                                then = DateTime.MinValue;
 
-                       return new HttpCookie (cookieName, Encrypt (ticket), strCookiePath, then);
+                       HttpCookie cookie = new HttpCookie (cookieName, Encrypt (ticket), strCookiePath, then);
+                       if (requireSSL)
+                               cookie.Secure = true;
+                       return cookie;
+               }
+
+               internal static string ReturnUrl
+               {
+                       get { return HttpContext.Current.Request ["RETURNURL"]; }
                }
 
                public static string GetRedirectUrl (string userName, bool createPersistentCookie)
@@ -274,7 +449,7 @@ namespace System.Web.Security
 
                        Initialize ();
                        HttpRequest request = HttpContext.Current.Request;
-                       string returnUrl = request ["RETURNURL"];
+                       string returnUrl = ReturnUrl;
                        if (returnUrl != null)
                                return returnUrl;
 
@@ -299,11 +474,17 @@ namespace System.Web.Security
 
                static string GetHexString (byte [] bytes)
                {
-                       StringBuilder result = new StringBuilder (bytes.Length * 2);
-                       foreach (byte b in bytes)
-                               result.AppendFormat ("{0:X2}", (int) b);
-
-                       return result.ToString ();
+                       const int letterPart = 55;
+                       const int numberPart = 48;
+                       char [] result = new char [bytes.Length * 2];
+                       for (int i = 0; i < bytes.Length; i++) {
+                               int tmp = (int) bytes [i];
+                               int second = tmp & 15;
+                               int first = (tmp >> 4) & 15;
+                               result [(i * 2)] = (char) (first > 9 ? letterPart + first : numberPart + first);
+                               result [(i * 2) + 1] = (char) (second > 9 ? letterPart + second : numberPart + second);
+                       }
+                       return new string (result);
                }
 
                public static string HashPasswordForStoringInConfigFile (string password, string passwordFormat)
@@ -315,9 +496,9 @@ namespace System.Web.Security
                                throw new ArgumentNullException ("passwordFormat");
 
                        byte [] bytes;
-                       if (String.Compare (passwordFormat, "MD5", true) == 0) {
+                       if (String.Compare (passwordFormat, "MD5", true, CultureInfo.InvariantCulture) == 0) {
                                bytes = MD5.Create ().ComputeHash (Encoding.UTF8.GetBytes (password));
-                       } else if (String.Compare (passwordFormat, "SHA1", true) == 0) {
+                       } else if (String.Compare (passwordFormat, "SHA1", true, CultureInfo.InvariantCulture) == 0) {
                                bytes = SHA1.Create ().ComputeHash (Encoding.UTF8.GetBytes (password));
                        } else {
                                throw new ArgumentException ("The format must be either MD5 or SHA1", "passwordFormat");
@@ -335,14 +516,25 @@ namespace System.Web.Security
                                if (initialized)
                                        return;
 
-                               HttpContext context = HttpContext.Current;
 #if NET_2_0
-                               AuthConfig authConfig = null;
-                               if (context != null)
-                                       authConfig = context.GetConfig (authConfigPath) as AuthConfig;
+                               AuthenticationSection section = (AuthenticationSection)WebConfigurationManager.GetSection (authConfigPath);
+                               FormsAuthenticationConfiguration config = section.Forms;
+
+                               cookieName = config.Name;
+                               timeout = (int)config.Timeout.TotalMinutes;
+                               cookiePath = config.Path;
+                               protection = config.Protection;
+                               requireSSL = config.RequireSSL;
+                               slidingExpiration = config.SlidingExpiration;
+                               cookie_domain = config.Domain;
+                               cookie_mode = config.Cookieless;
+                               cookies_supported = true; /* XXX ? */
+                               default_url = MapUrl(config.DefaultUrl);
+                               enable_crossapp_redirects = config.EnableCrossAppRedirects;
+                               login_url = MapUrl(config.LoginUrl);
 #else
+                               HttpContext context = HttpContext.Current;
                                AuthConfig authConfig = context.GetConfig (authConfigPath) as AuthConfig;
-#endif
                                if (authConfig != null) {
                                        cookieName = authConfig.CookieName;
                                        timeout = authConfig.Timeout;
@@ -351,14 +543,6 @@ namespace System.Web.Security
 #if NET_1_1
                                        requireSSL = authConfig.RequireSSL;
                                        slidingExpiration = authConfig.SlidingExpiration;
-#endif
-#if NET_2_0
-                                       cookie_domain = authConfig.CookieDomain;
-                                       cookie_mode = authConfig.CookieMode;
-                                       cookies_supported = authConfig.CookiesSupported;
-                                       default_url = authConfig.DefaultUrl;
-                                       enable_crossapp_redirects = authConfig.EnableCrossAppRedirects;
-                                       login_url = authConfig.LoginUrl;
 #endif
                                } else {
                                        cookieName = ".MONOAUTH";
@@ -367,15 +551,9 @@ namespace System.Web.Security
                                        protection = FormsProtectionEnum.All;
 #if NET_1_1
                                        slidingExpiration = true;
-#endif
-#if NET_2_0
-                                       cookie_domain = String.Empty;
-                                       cookie_mode = HttpCookieMode.UseDeviceProfile;
-                                       cookies_supported = true;
-                                       default_url = "/default.aspx";
-                                       login_url = "/login.aspx";
 #endif
                                }
+#endif
 
                                // IV is 8 bytes long for 3DES
                                init_vector = new byte [8];
@@ -391,6 +569,15 @@ namespace System.Web.Security
                        }
                }
 
+#if NET_2_0
+               static string MapUrl (string url) {
+                       if (UrlUtils.IsRelativeUrl (url))
+                               return UrlUtils.Combine (HttpRuntime.AppDomainAppVirtualPath, url);
+                       else
+                               return UrlUtils.ResolveVirtualPathFromAppAbsolute (url);
+               }
+#endif
+
                public static void RedirectFromLoginPage (string userName, bool createPersistentCookie)
                {
                        RedirectFromLoginPage (userName, createPersistentCookie, null);
@@ -403,7 +590,7 @@ namespace System.Web.Security
 
                        Initialize ();
                        SetAuthCookie (userName, createPersistentCookie, strCookiePath);
-                       Redirect (GetRedirectUrl (userName, createPersistentCookie));
+                       Redirect (GetRedirectUrl (userName, createPersistentCookie), false);
                }
 
                public static FormsAuthenticationTicket RenewTicketIfOld (FormsAuthenticationTicket tOld)
@@ -459,6 +646,10 @@ namespace System.Web.Security
                        expiration_cookie.Expires = new DateTime (1999, 10, 12);
                        expiration_cookie.Path = cookiePath;
                        cc.Add (expiration_cookie);
+
+#if NET_2_0
+                       Roles.DeleteCookie ();
+#endif
                }
 
                public static string FormsCookieName
@@ -494,27 +685,27 @@ namespace System.Web.Security
 
 #if NET_2_0
                public static string CookieDomain {
-                       get { return cookie_domain; }
+                       get { Initialize (); return cookie_domain; }
                }
 
                public static HttpCookieMode CookieMode {
-                       get { return cookie_mode; }
+                       get { Initialize (); return cookie_mode; }
                }
 
                public static bool CookiesSupported {
-                       get { return cookies_supported; }
+                       get { Initialize (); return cookies_supported; }
                }
 
                public static string DefaultUrl {
-                       get { return default_url; }
+                       get { Initialize (); return default_url; }
                }
 
                public static bool EnableCrossAppRedirects {
-                       get { return enable_crossapp_redirects; }
+                       get { Initialize (); return enable_crossapp_redirects; }
                }
 
                public static string LoginUrl {
-                       get { return login_url; }
+                       get { Initialize (); return login_url; }
                }
 
                public static void RedirectToLoginPage ()
@@ -528,10 +719,16 @@ namespace System.Web.Security
                        // TODO: if ? is in LoginUrl (legal?), ? in query (legal?) ...
                        Redirect (LoginUrl + "?" + extraQueryString);
                }
-#endif
-               private static void Redirect (string url)
+
+               static void Redirect (string url)
                {
                        HttpContext.Current.Response.Redirect (url);
                }
+#endif
+
+               static void Redirect (string url, bool end)
+               {
+                       HttpContext.Current.Response.Redirect (url, end);
+               }
        }
 }