// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Web;
using System.Web.Routing;
using System.Web.Security;
using System.Web.WebPages;
using WebMatrix.WebData.Resources;
namespace WebMatrix.WebData
{
public static class WebSecurity
{
public static readonly string EnableSimpleMembershipKey = "enableSimpleMembership";
///
/// Gets a value indicating whether the method has been initialized.
///
///
/// true if initialized; otherwise, false.
///
public static bool Initialized { get; private set; }
public static int CurrentUserId
{
get { return GetUserId(CurrentUserName); }
}
public static string CurrentUserName
{
get { return Context.User.Identity.Name; }
}
public static bool HasUserId
{
get { return CurrentUserId != -1; }
}
public static bool IsAuthenticated
{
get { return Request.IsAuthenticated; }
}
internal static HttpContextBase Context
{
get { return new HttpContextWrapper(HttpContext.Current); }
}
internal static HttpRequestBase Request
{
get { return Context.Request; }
}
internal static HttpResponseBase Response
{
get { return Context.Response; }
}
internal static void PreAppStartInit()
{
// Allow use of to disable registration of membership/role providers as default.
if (ConfigUtil.SimpleMembershipEnabled)
{
// called during PreAppStart, should also hook up the config for MembershipProviders?
// Replace the AspNetSqlMembershipProvider (which is the default that is registered in root web.config)
const string BuiltInMembershipProviderName = "AspNetSqlMembershipProvider";
var builtInMembership = Membership.Providers[BuiltInMembershipProviderName];
if (builtInMembership != null)
{
var simpleMembership = CreateDefaultSimpleMembershipProvider(BuiltInMembershipProviderName, currentDefault: builtInMembership);
Membership.Providers.Remove(BuiltInMembershipProviderName);
Membership.Providers.Add(simpleMembership);
}
Roles.Enabled = true;
const string BuiltInRolesProviderName = "AspNetSqlRoleProvider";
var builtInRoles = Roles.Providers[BuiltInRolesProviderName];
if (builtInRoles != null)
{
var simpleRoles = CreateDefaultSimpleRoleProvider(BuiltInRolesProviderName, currentDefault: builtInRoles);
Roles.Providers.Remove(BuiltInRolesProviderName);
Roles.Providers.Add(simpleRoles);
}
}
}
private static ExtendedMembershipProvider VerifyProvider()
{
ExtendedMembershipProvider provider = Membership.Provider as ExtendedMembershipProvider;
if (provider == null)
{
throw new InvalidOperationException(WebDataResources.Security_NoExtendedMembershipProvider);
}
provider.VerifyInitialized(); // Have the provider verify that it's initialized (only our SimpleMembershipProvider does anything here)
return provider;
}
public static void InitializeDatabaseConnection(string connectionStringName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables)
{
DatabaseConnectionInfo connect = new DatabaseConnectionInfo();
connect.ConnectionStringName = connectionStringName;
InitializeProviders(connect, userTableName, userIdColumn, userNameColumn, autoCreateTables);
}
public static void InitializeDatabaseConnection(string connectionString, string providerName, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables)
{
DatabaseConnectionInfo connect = new DatabaseConnectionInfo();
connect.ConnectionString = connectionString;
connect.ProviderName = providerName;
InitializeProviders(connect, userTableName, userIdColumn, userNameColumn, autoCreateTables);
}
private static void InitializeProviders(DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool autoCreateTables)
{
SimpleMembershipProvider simpleMembership = Membership.Provider as SimpleMembershipProvider;
if (simpleMembership != null)
{
InitializeMembershipProvider(simpleMembership, connect, userTableName, userIdColumn, userNameColumn, autoCreateTables);
}
SimpleRoleProvider simpleRoles = Roles.Provider as SimpleRoleProvider;
if (simpleRoles != null)
{
InitializeRoleProvider(simpleRoles, connect, userTableName, userIdColumn, userNameColumn, autoCreateTables);
}
Initialized = true;
}
internal static void InitializeMembershipProvider(SimpleMembershipProvider simpleMembership, DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool createTables)
{
if (simpleMembership.InitializeCalled)
{
throw new InvalidOperationException(WebDataResources.Security_InitializeAlreadyCalled);
}
simpleMembership.ConnectionInfo = connect;
simpleMembership.UserIdColumn = userIdColumn;
simpleMembership.UserNameColumn = userNameColumn;
simpleMembership.UserTableName = userTableName;
if (createTables)
{
simpleMembership.CreateTablesIfNeeded();
}
else
{
// We want to validate the user table if we aren't creating them
simpleMembership.ValidateUserTable();
}
simpleMembership.InitializeCalled = true;
}
internal static void InitializeRoleProvider(SimpleRoleProvider simpleRoles, DatabaseConnectionInfo connect, string userTableName, string userIdColumn, string userNameColumn, bool createTables)
{
if (simpleRoles.InitializeCalled)
{
throw new InvalidOperationException(WebDataResources.Security_InitializeAlreadyCalled);
}
simpleRoles.ConnectionInfo = connect;
simpleRoles.UserTableName = userTableName;
simpleRoles.UserIdColumn = userIdColumn;
simpleRoles.UserNameColumn = userNameColumn;
if (createTables)
{
simpleRoles.CreateTablesIfNeeded();
}
simpleRoles.InitializeCalled = true;
}
private static SimpleMembershipProvider CreateDefaultSimpleMembershipProvider(string name, MembershipProvider currentDefault)
{
var membership = new SimpleMembershipProvider(previousProvider: currentDefault);
NameValueCollection config = new NameValueCollection();
membership.Initialize(name, config);
return membership;
}
private static SimpleRoleProvider CreateDefaultSimpleRoleProvider(string name, RoleProvider currentDefault)
{
var roleProvider = new SimpleRoleProvider(previousProvider: currentDefault);
NameValueCollection config = new NameValueCollection();
roleProvider.Initialize(name, config);
return roleProvider;
}
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login", Justification = "Login is used more consistently in ASP.Net")]
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")]
public static bool Login(string userName, string password, bool persistCookie = false)
{
VerifyProvider();
bool success = Membership.ValidateUser(userName, password);
if (success)
{
FormsAuthentication.SetAuthCookie(userName, persistCookie);
}
return success;
}
[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Logout", Justification = "Login is used more consistently in ASP.Net")]
public static void Logout()
{
VerifyProvider();
FormsAuthentication.SignOut();
}
public static bool ChangePassword(string userName, string currentPassword, string newPassword)
{
VerifyProvider();
bool success = false;
try
{
var currentUser = Membership.GetUser(userName, true /* userIsOnline */);
success = currentUser.ChangePassword(currentPassword, newPassword);
}
catch (ArgumentException)
{
// An argument exception is thrown if the new password does not meet the provider's requirements
}
return success;
}
public static bool ConfirmAccount(string accountConfirmationToken)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.ConfirmAccount(accountConfirmationToken);
}
public static bool ConfirmAccount(string userName, string accountConfirmationToken)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.ConfirmAccount(userName, accountConfirmationToken);
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")]
public static string CreateAccount(string userName, string password, bool requireConfirmationToken = false)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.CreateAccount(userName, password, requireConfirmationToken);
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")]
public static string CreateUserAndAccount(string userName, string password, object propertyValues = null, bool requireConfirmationToken = false)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
IDictionary values = null;
if (propertyValues != null)
{
values = new RouteValueDictionary(propertyValues);
}
return provider.CreateUserAndAccount(userName, password, requireConfirmationToken, values);
}
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "This is a helper class, and we are not removing optional parameters from methods in helper classes")]
public static string GeneratePasswordResetToken(string userName, int tokenExpirationInMinutesFromNow = 1440)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.GeneratePasswordResetToken(userName, tokenExpirationInMinutesFromNow);
}
public static bool UserExists(string userName)
{
VerifyProvider();
return Membership.GetUser(userName) != null;
}
public static int GetUserId(string userName)
{
VerifyProvider();
MembershipUser user = Membership.GetUser(userName);
if (user == null)
{
return -1;
}
// REVIEW: This cast is breaking the abstraction for the membershipprovider, we basically assume that userids are ints
return (int)user.ProviderUserKey;
}
public static int GetUserIdFromPasswordResetToken(string token)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.GetUserIdFromPasswordResetToken(token);
}
public static bool IsCurrentUser(string userName)
{
VerifyProvider();
return String.Equals(CurrentUserName, userName, StringComparison.OrdinalIgnoreCase);
}
public static bool IsConfirmed(string userName)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.IsConfirmed(userName);
}
// Make sure the logged on user is same as the one specified by the id
private static bool IsUserLoggedOn(int userId)
{
VerifyProvider();
return CurrentUserId == userId;
}
// Make sure the user was authenticated
public static void RequireAuthenticatedUser()
{
VerifyProvider();
var user = Context.User;
if (user == null || !user.Identity.IsAuthenticated)
{
Response.SetStatus(HttpStatusCode.Unauthorized);
}
}
// Make sure the user was authenticated
public static void RequireUser(int userId)
{
VerifyProvider();
if (!IsUserLoggedOn(userId))
{
Response.SetStatus(HttpStatusCode.Unauthorized);
}
}
public static void RequireUser(string userName)
{
VerifyProvider();
if (!String.Equals(CurrentUserName, userName, StringComparison.OrdinalIgnoreCase))
{
Response.SetStatus(HttpStatusCode.Unauthorized);
}
}
public static void RequireRoles(params string[] roles)
{
VerifyProvider();
foreach (string role in roles)
{
if (!Roles.IsUserInRole(CurrentUserName, role))
{
Response.SetStatus(HttpStatusCode.Unauthorized);
return;
}
}
}
public static bool ResetPassword(string passwordResetToken, string newPassword)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.ResetPasswordWithToken(passwordResetToken, newPassword);
}
public static bool IsAccountLockedOut(string userName, int allowedPasswordAttempts, int intervalInSeconds)
{
VerifyProvider();
return IsAccountLockedOut(userName, allowedPasswordAttempts, TimeSpan.FromSeconds(intervalInSeconds));
}
public static bool IsAccountLockedOut(string userName, int allowedPasswordAttempts, TimeSpan interval)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return IsAccountLockedOutInternal(provider, userName, allowedPasswordAttempts, interval);
}
internal static bool IsAccountLockedOutInternal(ExtendedMembershipProvider provider, string userName, int allowedPasswordAttempts, TimeSpan interval)
{
return (provider.GetUser(userName, false) != null &&
provider.GetPasswordFailuresSinceLastSuccess(userName) > allowedPasswordAttempts &&
provider.GetLastPasswordFailureDate(userName).Add(interval) > DateTime.UtcNow);
}
public static int GetPasswordFailuresSinceLastSuccess(string userName)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.GetPasswordFailuresSinceLastSuccess(userName);
}
public static DateTime GetCreateDate(string userName)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.GetCreateDate(userName);
}
public static DateTime GetPasswordChangedDate(string userName)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.GetPasswordChangedDate(userName);
}
public static DateTime GetLastPasswordFailureDate(string userName)
{
ExtendedMembershipProvider provider = VerifyProvider();
Debug.Assert(provider != null); // VerifyProvider checks this
return provider.GetLastPasswordFailureDate(userName);
}
}
}