PageRenderTime 87ms CodeModel.GetById 66ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/BlogEngine/DotNetSlave.BusinessLogic/Security/Security.cs

#
C# | 434 lines | 253 code | 57 blank | 124 comment | 33 complexity | eac100cb0bb91b418d65a191d8870be3 MD5 | raw file
  1using System;
  2using System.Collections.ObjectModel;
  3using System.Collections.Generic;
  4using System.Linq;
  5using System.Text;
  6using System.Threading;
  7using System.Web;
  8using System.Web.Security;
  9using System.Diagnostics;
 10using System.Security;
 11using System.Security.Principal;
 12
 13namespace BlogEngine.Core
 14{
 15    /// <summary>
 16    /// Class to provide a unified area of authentication/authorization checking.
 17    /// </summary>
 18    public partial class Security : IHttpModule
 19    {
 20        static Security()
 21        {
 22
 23        }
 24
 25        /// <summary>
 26        /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
 27        /// </summary>
 28        public void Dispose()
 29        {
 30            // Nothing to dispose
 31        }
 32
 33        /// <summary>
 34        /// Initializes a module and prepares it to handle requests.
 35        /// </summary>
 36        /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
 37        public void Init(HttpApplication context)
 38        {
 39            context.AuthenticateRequest += ContextAuthenticateRequest;
 40        }
 41
 42        /// <summary>
 43        /// Handles the AuthenticateRequest event of the context control.
 44        /// </summary>
 45        /// <param name="sender">
 46        /// The source of the event.
 47        /// </param>
 48        /// <param name="e">
 49        /// The <see cref="System.EventArgs"/> instance containing the event data.
 50        /// </param>
 51        private static void ContextAuthenticateRequest(object sender, EventArgs e)
 52        {
 53            var context = ((HttpApplication)sender).Context;
 54
 55            // FormsAuthCookieName is a custom cookie name based on the current instance.
 56            HttpCookie authCookie = context.Request.Cookies[FormsAuthCookieName];
 57            if (authCookie != null)
 58            {
 59                Blog blog = Blog.CurrentInstance;
 60
 61                FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
 62
 63                // for extra security, make sure the UserData matches the current blog instance.
 64                // this would prevent a cookie name change for a forms auth cookie encrypted in
 65                // the same application (different blog) as being valid for this blog instance.
 66                if (authTicket != null && !string.IsNullOrWhiteSpace(authTicket.UserData) && authTicket.UserData.Equals(Blog.CurrentInstance.Id.ToString(), StringComparison.OrdinalIgnoreCase))
 67                {
 68                    CustomIdentity identity = new CustomIdentity(authTicket.Name, true);
 69                    CustomPrincipal principal = new CustomPrincipal(identity);
 70
 71                    context.User = principal;
 72                    return;
 73                }
 74            }
 75
 76            // need to create an empty/unauthenticated user to assign to context.User.
 77            CustomIdentity unauthIdentity = new CustomIdentity(string.Empty, false);
 78            CustomPrincipal unauthPrincipal = new CustomPrincipal(unauthIdentity);
 79            context.User = unauthPrincipal;
 80        }
 81
 82        /// <summary>
 83        /// Name of the Forms authentication cookie for the current blog instance.
 84        /// </summary>
 85        public static string FormsAuthCookieName
 86        {
 87            get
 88            {
 89                return FormsAuthentication.FormsCookieName + "-" + Blog.CurrentInstance.Id.ToString();
 90            }
 91        }
 92
 93        public static void SignOut()
 94        {
 95            // using a custom cookie name based on the current blog instance.
 96            HttpCookie cookie = new HttpCookie(FormsAuthCookieName, string.Empty);
 97            cookie.Expires = DateTime.Now.AddYears(-3);
 98            HttpContext.Current.Response.Cookies.Add(cookie);
 99        }
100
101        public static bool AuthenticateUser(string username, string password, bool rememberMe)
102        {
103            string un = (username ?? string.Empty).Trim();
104            string pw = (password ?? string.Empty).Trim();
105
106            if (!string.IsNullOrWhiteSpace(un) && !string.IsNullOrWhiteSpace(pw))
107            {
108                bool isValidated = Membership.ValidateUser(un, pw);
109
110                if (isValidated)
111                {
112                    HttpContext context = HttpContext.Current;
113                    DateTime expirationDate = DateTime.Now.Add(FormsAuthentication.Timeout);
114
115                    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
116                        1,
117                        un,
118                        DateTime.Now,
119                        expirationDate,
120                        rememberMe,
121                        Blog.CurrentInstance.Id.ToString(),
122                        FormsAuthentication.FormsCookiePath
123                    );
124
125                    string encryptedTicket = FormsAuthentication.Encrypt(ticket);
126
127                    // setting a custom cookie name based on the current blog instance.
128                    // if !rememberMe, set expires to DateTime.MinValue which makes the
129                    // cookie a browser-session cookie expiring when the browser is closed.
130                    HttpCookie cookie = new HttpCookie(FormsAuthCookieName, encryptedTicket);
131                    cookie.Expires = rememberMe ? expirationDate : DateTime.MinValue;
132                    context.Response.Cookies.Set(cookie);
133
134                    string returnUrl = context.Request.QueryString["returnUrl"];
135
136                    // ignore Return URLs not beginning with a forward slash, such as remote sites.
137                    if (string.IsNullOrWhiteSpace(returnUrl) || !returnUrl.StartsWith("/"))
138                        returnUrl = null;
139
140                    if (!string.IsNullOrWhiteSpace(returnUrl))
141                    {
142                        context.Response.Redirect(returnUrl);
143                    }
144                    else
145                    {
146                        context.Response.Redirect(Utils.RelativeWebRoot);
147                    }
148
149                    return true;
150                }
151            }
152
153            return false;
154        }
155
156        #region "Properties"
157
158        /// <summary>
159        /// If the current user is authenticated, returns the current MembershipUser. If not, returns null. This is just a shortcut to Membership.GetUser().
160        /// </summary>
161        public static MembershipUser CurrentMembershipUser
162        {
163            get
164            {
165                return Membership.GetUser();
166            }
167        }
168
169        /// <summary>
170        /// Gets the current user for the current HttpContext.
171        /// </summary>
172        /// <remarks>
173        /// This should always return HttpContext.Current.User. That value and Thread.CurrentPrincipal can't be
174        /// guaranteed to always be the same value, as they can be set independently from one another. Looking
175        /// through the .Net source, the System.Web.Security.Roles class also returns the HttpContext's User.
176        /// </remarks>
177        public static System.Security.Principal.IPrincipal CurrentUser
178        {
179            get
180            {
181                return HttpContext.Current.User;
182            }
183        }
184
185        /// <summary>
186        /// Gets whether the current user is logged in.
187        /// </summary>
188        public static bool IsAuthenticated
189        {
190            get
191            {
192                return Security.CurrentUser.Identity.IsAuthenticated;
193            }
194        }
195
196        /// <summary>
197        /// Gets whether the currently logged in user is in the administrator role.
198        /// </summary>
199        public static bool IsAdministrator
200        {
201            get
202            {
203                return (Security.IsAuthenticated && Security.CurrentUser.IsInRole(BlogConfig.AdministratorRole));
204            }
205        }
206
207        /// <summary>
208        /// Returns an IEnumerable of Rights that belong to the ecurrent user.
209        /// </summary>
210        /// <returns></returns>
211        public static IEnumerable<Right> CurrentUserRights()
212        {
213            return Right.GetRights(Security.GetCurrentUserRoles());
214        }
215
216        #endregion
217
218        #region "Public Methods"
219
220        /// <summary>
221        /// If the current user does not have the requested right, either redirects to the login page,
222        /// or throws a SecurityException.
223        /// </summary>
224        /// <param name="right"></param>
225        /// <param name="redirectToLoginPage">
226        /// If true and user does not have rights, redirects to the login page.
227        /// If false and user does not have rights, throws a security exception.
228        /// </param>
229        public static void DemandUserHasRight(Rights right, bool redirectToLoginPage)
230        {
231            DemandUserHasRight(AuthorizationCheck.HasAny, redirectToLoginPage, new[] { right });
232        }
233
234        /// <summary>
235        /// If the current user does not have the requested rights, either redirects to the login page,
236        /// or throws a SecurityException.
237        /// </summary>
238        /// <param name="authCheck"></param>
239        /// <param name="redirectIfUnauthorized">
240        /// If true and user does not have rights, redirects to the login page or homepage.
241        /// If false and user does not have rights, throws a security exception.
242        /// </param>
243        /// <param name="rights"></param>
244        public static void DemandUserHasRight(AuthorizationCheck authCheck, bool redirectIfUnauthorized, params Rights[] rights)
245        {
246            if (!IsAuthorizedTo(authCheck, rights))
247            {
248                if (redirectIfUnauthorized)
249                {
250                    RedirectForUnauthorizedRequest();
251                }
252                else
253                {
254                    throw new SecurityException("User doesn't have the right to perform this");
255                }
256            }
257        }
258
259        public static void RedirectForUnauthorizedRequest()
260        {
261            HttpContext context = HttpContext.Current;
262            Uri referrer = context.Request.UrlReferrer;
263            bool isFromLoginPage = referrer != null && referrer.LocalPath.IndexOf("/Account/login.aspx", StringComparison.OrdinalIgnoreCase) != -1;
264
265            // If the user was just redirected from the login page to the current page,
266            // we will then redirect them to the homepage, rather than back to the
267            // login page to prevent confusion.
268            if (isFromLoginPage)
269            {
270                context.Response.Redirect(Utils.RelativeWebRoot);
271            }
272            else
273            {
274                context.Response.Redirect(string.Format("{0}Account/login.aspx?ReturnURL={1}", Utils.RelativeWebRoot, HttpUtility.UrlPathEncode(context.Request.RawUrl)));
275            }
276        }
277
278        /// <summary>
279        /// Returns whether or not the current user has the passed in Right.
280        /// </summary>
281        /// <param name="right"></param>
282        /// <returns></returns>
283        public static bool IsAuthorizedTo(Rights right)
284        {
285            return Right.HasRight(right, Security.GetCurrentUserRoles());
286        }
287
288        /// <summary>
289        /// Returns whether the current user passes authorization on the rights based on the given AuthorizationCheck.
290        /// </summary>
291        /// <param name="authCheck"></param>
292        /// <param name="rights"></param>
293        /// <returns></returns>
294        public static bool IsAuthorizedTo(AuthorizationCheck authCheck, IEnumerable<Rights> rights)
295        {
296            if (rights.Count() == 0)
297            {
298                // Always return false for this. If there's a mistake where authorization
299                // is being checked for on an empty collection, we don't want to return 
300                // true.
301                return false;
302            }
303            else
304            {
305                var roles = Security.GetCurrentUserRoles();
306
307                if (authCheck == AuthorizationCheck.HasAny)
308                {
309                    foreach (var right in rights)
310                    {
311                        if (Right.HasRight(right, roles))
312                        {
313                            return true;
314                        }
315                    }
316
317                    return false;
318                }
319                else if (authCheck == AuthorizationCheck.HasAll)
320                {
321                    bool authCheckPassed = true;
322
323                    foreach (var right in rights)
324                    {
325                        if (!Right.HasRight(right, roles))
326                        {
327                            authCheckPassed = false;
328                            break;
329                        }
330                    }
331                    return authCheckPassed;
332                }
333                else
334                {
335                    throw new NotSupportedException();
336                }
337
338            }
339        }
340
341        /// <summary>
342        /// Returns whether a role is a System role.
343        /// </summary>
344        /// <param name="roleName">The name of the role.</param>
345        /// <returns>true if the roleName is a system role, otherwiser false</returns>
346        public static bool IsSystemRole(string roleName)
347        {
348            if (roleName.Equals(BlogConfig.AdministratorRole, StringComparison.OrdinalIgnoreCase) ||
349                roleName.Equals(BlogConfig.AnonymousRole, StringComparison.OrdinalIgnoreCase) ||
350                roleName.Equals(BlogConfig.EditorsRole, StringComparison.OrdinalIgnoreCase))
351            {
352                return true;
353            }
354
355            return false;
356        }
357
358        /// <summary>
359        /// Returns whether the current user passes authorization on the rights based on the given AuthorizationCheck.
360        /// </summary>
361        /// <param name="authCheck"></param>
362        /// <param name="rights"></param>
363        /// <returns></returns>
364        public static bool IsAuthorizedTo(AuthorizationCheck authCheck, params Rights[] rights)
365        {
366            return IsAuthorizedTo(authCheck, rights.ToList());
367        }
368
369        #endregion
370
371        #region "Methods"
372
373        /// <summary>
374        /// Helper method that returns the correct roles based on authentication.
375        /// </summary>
376        /// <returns></returns>
377        public static string[] GetCurrentUserRoles()
378        {
379            if (!IsAuthenticated)
380            {
381                // This needs to be recreated each time, because it's possible 
382                // that the array can fall into the wrong hands and then someone
383                // could alter it. 
384                return new[] { BlogConfig.AnonymousRole };
385            }
386            else
387            {
388                return Roles.GetRolesForUser();
389            }
390        }
391
392        /// <summary>
393        /// Impersonates a user for the duration of the HTTP request.
394        /// </summary>
395        /// <param name="username">The username</param>
396        /// <param name="password">The password</param>
397        /// <returns>True if the credentials are correct and impersonation succeeds</returns>
398        public static bool ImpersonateUser(string username, string password)
399        {
400            if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
401                return false;
402
403            CustomIdentity identity = new CustomIdentity(username, password);
404            if (!identity.IsAuthenticated) { return false; }
405
406            CustomPrincipal principal = new CustomPrincipal(identity);
407
408            // Make the custom principal be the user for the rest of this request.
409            HttpContext.Current.User = principal;
410
411            return true;
412        }
413
414        #endregion
415    }
416
417
418    /// <summary>
419    /// Enum for setting how rights should be checked for.
420    /// </summary>
421    public enum AuthorizationCheck
422    {
423        /// <summary>
424        /// A user will be considered authorized if they have any of the given Rights.
425        /// </summary>
426        HasAny,
427
428        /// <summary>
429        /// A user will be considered authorized if they have all of the given Rights.
430        /// </summary>
431        HasAll
432    }
433
434}