/aspclassiccompiler/AzureStoreAsp/Assets/AspProviders/TableStorageMembershipProvider.cs
C# | 1586 lines | 1302 code | 161 blank | 123 comment | 178 complexity | 65f212217bf73eb1cb5e8cbf444d519f MD5 | raw file
Possible License(s): Apache-2.0, AGPL-3.0
Large files files are truncated, but you can click here to view the full file
- // ----------------------------------------------------------------------------------
- // Microsoft Developer & Platform Evangelism
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
- // EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
- // OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
- // ----------------------------------------------------------------------------------
- // The example companies, organizations, products, domain names,
- // e-mail addresses, logos, people, places, and events depicted
- // herein are fictitious. No association with any real company,
- // organization, product, domain name, email address, logo, person,
- // places, or events is intended or should be inferred.
- // ----------------------------------------------------------------------------------
-
- //
- // <copyright file="TableStorageMembershipProvider.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- //
- using System;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.Configuration.Provider;
- using System.Data.Services.Client;
- using System.Diagnostics;
- using System.Globalization;
- using System.Linq;
- using System.Net;
- using System.Security;
- using System.Security.Cryptography;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Web;
- using System.Web.Security;
- using Microsoft.Samples.ServiceHosting.StorageClient;
-
-
- namespace Microsoft.Samples.ServiceHosting.AspProviders
- {
- /// <summary>
- /// This class allows DevtableGen to generate the correct table (named 'Membership')
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses",
- Justification="Class is used by devtablegen to generate database for the development storage tool")]
- internal class MembershipDataServiceContext : TableStorageDataServiceContext
- {
- public IQueryable<MembershipRow> Membership
- {
- get
- {
- return this.CreateQuery<MembershipRow>("Membership");
- }
- }
- }
-
- internal class MembershipRow : TableStorageEntity, IComparable
- {
- private string _applicationName;
- private string _userName;
- private string _password;
- private string _passwordSalt;
- private string _email;
- private string _passwordAnswer;
- private string _passwordQuestion;
- private string _comment;
- private string _profileBlobName;
-
- private DateTime _createDate;
- private DateTime _lastLoginDate;
- private DateTime _lastPasswordChangedDate;
- private DateTime _lastLockoutDate;
- private DateTime _lastActivityDate;
- private DateTime _failedPasswordAttemptWindowStart;
- private DateTime _failedPasswordAnswerAttemptWindowStart;
- private DateTime _profileLastUpdated;
-
- // partition key is applicationName + userName
- // rowKey is empty
- public MembershipRow(string applicationName, string userName)
- : base()
- {
- if (string.IsNullOrEmpty(applicationName)) {
- throw new ProviderException("Partition key cannot be empty!");
- }
- if (string.IsNullOrEmpty(userName))
- {
- throw new ProviderException("RowKey cannot be empty!");
- }
-
- // applicationName + userName is partitionKey
- // the reasoning behind this is that we want to strive for the best scalability possible
- // chosing applicationName as the partition key and userName as row key would not give us that because
- // it would mean that a site with millions of users had all users on a single partition
- // having the applicationName and userName inside the partition key is important for queries as queries
- // for users in a single application are the most frequent
- // these queries are faster because application name and user name are part of the key
- PartitionKey = SecUtility.CombineToKey(applicationName, userName);
- RowKey = string.Empty;
-
- ApplicationName = applicationName;
- UserName = userName;
-
- Password = string.Empty;
- PasswordSalt = string.Empty;
- Email = string.Empty;
- PasswordAnswer = string.Empty;
- PasswordQuestion = string.Empty;
- Comment = string.Empty;
- ProfileBlobName = string.Empty;
-
- CreateDateUtc = TableStorageConstants.MinSupportedDateTime;
- LastLoginDateUtc = TableStorageConstants.MinSupportedDateTime;
- LastActivityDateUtc = TableStorageConstants.MinSupportedDateTime;
- LastLockoutDateUtc = TableStorageConstants.MinSupportedDateTime;
- LastPasswordChangedDateUtc = TableStorageConstants.MinSupportedDateTime;
- FailedPasswordAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime;
- FailedPasswordAnswerAttemptWindowStartUtc = TableStorageConstants.MinSupportedDateTime;
- ProfileLastUpdatedUtc = TableStorageConstants.MinSupportedDateTime;
-
- ProfileIsCreatedByProfileProvider = false;
- ProfileSize = 0;
-
- }
-
- public MembershipRow()
- : base()
- {
- }
-
- public string ApplicationName
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _applicationName = value;
- PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName);
- }
- get
- {
- return _applicationName;
- }
- }
-
-
- public string UserName
- {
- set {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _userName = value;
- PartitionKey = SecUtility.CombineToKey(ApplicationName, UserName);
- }
- get
- {
- return _userName;
- }
- }
-
- public Guid UserId {
- set;
- get;
- }
-
- public string Password
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _password = value;
- }
- get
- {
- return _password;
- }
- }
-
- public int PasswordFormat
- {
- set;
- get;
- }
-
- public string PasswordSalt
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _passwordSalt = value;
- }
- get
- {
- return _passwordSalt;
- }
- }
-
- public string Email
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _email = value;
- }
- get
- {
- return _email;
- }
- }
-
- public string PasswordQuestion
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _passwordQuestion = value;
- }
- get
- {
- return _passwordQuestion;
- }
- }
-
- public string PasswordAnswer
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _passwordAnswer = value;
- }
- get
- {
- return _passwordAnswer;
- }
- }
-
-
- public bool IsApproved
- {
- set;
- get;
- }
-
- public bool IsAnonymous
- {
- set;
- get;
- }
-
- public bool IsLockedOut
- {
- set;
- get;
- }
-
- public DateTime CreateDateUtc {
- set {
- SecUtility.SetUtcTime(value, out _createDate);
- }
- get {
- return _createDate;
- }
- }
-
- public DateTime LastLoginDateUtc
- {
- set
- {
- SecUtility.SetUtcTime(value, out _lastLoginDate);
- }
- get
- {
- return _lastLoginDate;
- }
- }
-
- public DateTime LastPasswordChangedDateUtc
- {
- set {
- SecUtility.SetUtcTime(value, out _lastPasswordChangedDate);
- }
- get {
- return _lastPasswordChangedDate;
- }
- }
-
- public DateTime LastLockoutDateUtc
- {
- set
- {
- SecUtility.SetUtcTime(value, out _lastLockoutDate);
- }
- get
- {
- return _lastLockoutDate;
- }
- }
-
- public DateTime LastActivityDateUtc {
- set {
- SecUtility.SetUtcTime(value, out _lastActivityDate);
- }
- get {
- return _lastActivityDate;
- }
- }
-
- public int FailedPasswordAttemptCount
- {
- set;
- get;
- }
-
- public DateTime FailedPasswordAttemptWindowStartUtc
- {
- set {
- SecUtility.SetUtcTime(value, out _failedPasswordAttemptWindowStart);
- }
- get {
- return _failedPasswordAttemptWindowStart;
- }
- }
-
- public int FailedPasswordAnswerAttemptCount
- {
- set;
- get;
- }
-
- public DateTime FailedPasswordAnswerAttemptWindowStartUtc
- {
- set {
- SecUtility.SetUtcTime(value, out _failedPasswordAnswerAttemptWindowStart);
- }
- get {
- return _failedPasswordAnswerAttemptWindowStart;
- }
- }
-
- public string Comment
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value.");
- }
- _comment = value;
- }
- get
- {
- return _comment;
- }
- }
-
- #region Profile provider related properties
-
- public DateTime ProfileLastUpdatedUtc
- {
- set {
- SecUtility.SetUtcTime(value, out _profileLastUpdated);
- }
- get {
- return _profileLastUpdated;
- }
- }
-
- public int ProfileSize
- {
- set;
- get;
- }
-
- public string ProfileBlobName
- {
- set
- {
- if (value == null)
- {
- throw new ArgumentException("To ensure string values are always updated, this implementation does not allow null as a string value..");
- }
- _profileBlobName = value;
- }
- get
- {
- return _profileBlobName;
- }
- }
-
- public bool ProfileIsCreatedByProfileProvider
- {
- set;
- get;
- }
-
- #endregion
-
- public int CompareTo(object obj)
- {
- if (obj == null)
- {
- return 1;
- }
- MembershipRow row = obj as MembershipRow;
- if (row == null)
- {
- throw new ArgumentException("The parameter obj is not of type MembershipRow.");
- }
- return string.Compare(this.UserName, row.UserName, StringComparison.Ordinal);
- }
-
- }
-
- internal class EmailComparer: IComparer<MembershipRow> {
- public int Compare(MembershipRow x, MembershipRow y)
- {
- if (x == null)
- {
- if (y == null)
- {
- return 0;
- }
- else
- {
- return -1;
- }
- }
- else
- {
- return string.Compare(x.Email, y.Email, StringComparison.Ordinal);
- }
- }
- }
-
-
- public class TableStorageMembershipProvider : MembershipProvider
- {
-
- #region Member variables and constants
-
- private const int MaxTablePasswordSize = 128;
- private const int MaxTableEmailLength = 256;
- private const int MaxTablePasswordQuestionLength = 256;
- private const int MaxTablePasswordAnswerLength = 128;
- private const int MaxFindUserSize = 10000;
- // this is the absolute minimum password size when generating new password
- // the number is chosen so that it corresponds to the SQL membership provider implementation
- private const int MinGeneratedPasswordSize = 14;
- private const int NumRetries = 3;
-
- // member variables shared between most providers
- private string _applicationName;
- private string _accountName;
- private string _sharedKey;
- private string _tableName;
- private string _tableServiceBaseUri;
- private TableStorage _tableStorage;
- private object _lock = new object();
- // retry policies are used sparingly throughout this class because we often want to be
- // very conservative when it comes to security-related functions
- private RetryPolicy _tableRetry = RetryPolicies.RetryN(NumRetries, TimeSpan.FromSeconds(1));
-
- // membership provider specific member variables
- private bool _enablePasswordRetrieval;
- private bool _enablePasswordReset;
- private bool _requiresQuestionAndAnswer;
- private bool _requiresUniqueEmail;
- private int _maxInvalidPasswordAttempts;
- private int _passwordAttemptWindow;
- private int _minRequiredPasswordLength;
- private int _minRequiredNonalphanumericCharacters;
- private string _passwordStrengthRegularExpression;
- private MembershipPasswordFormat _passwordFormat;
-
- #endregion
-
- #region Properties
-
- /// <summary>
- /// The app name is not used in this implementation.
- /// </summary>
- public override string ApplicationName
- {
- get { return _applicationName; }
- set
- {
- lock (_lock)
- {
- SecUtility.CheckParameter(ref value, true, true, true, Constants.MaxTableApplicationNameLength, "ApplicationName");
- _applicationName = value;
- }
- }
- }
-
- public override bool EnablePasswordRetrieval
- {
- get
- {
- return _enablePasswordRetrieval;
- }
- }
-
- public override bool EnablePasswordReset
- {
- get
- {
- return _enablePasswordReset;
- }
- }
-
- public override bool RequiresQuestionAndAnswer
- {
- get
- {
- return _requiresQuestionAndAnswer;
- }
- }
-
- public override bool RequiresUniqueEmail
- {
- get
- {
- return _requiresUniqueEmail;
- }
- }
-
- public override MembershipPasswordFormat PasswordFormat
- {
- get
- {
- return _passwordFormat;
- }
- }
-
- public override int MaxInvalidPasswordAttempts
- {
- get
- {
- return _maxInvalidPasswordAttempts;
- }
- }
-
- public override int PasswordAttemptWindow
- {
- get
- {
- return _passwordAttemptWindow;
- }
- }
-
- public override int MinRequiredPasswordLength
- {
- get
- {
- return _minRequiredPasswordLength;
- }
- }
-
- public override int MinRequiredNonAlphanumericCharacters
- {
- get
- {
- return _minRequiredNonalphanumericCharacters;
- }
- }
-
- public override string PasswordStrengthRegularExpression
- {
- get
- {
- return _passwordStrengthRegularExpression;
- }
- }
-
- #endregion
-
- #region Public methods
-
- /// <summary>
- /// Initializes the membership provider. This is the only function that cannot be accessed
- /// in parallel by multiple applications. The function reads the properties of the
- /// provider specified in the Web.config file and stores them in member variables.
- /// </summary>
- public override void Initialize(string name, NameValueCollection config)
- {
- if (config == null)
- {
- throw new ArgumentNullException("config");
- }
-
- if (String.IsNullOrEmpty(name))
- {
- name = "TableStorageMembershipProvider";
- }
-
- if (string.IsNullOrEmpty(config["description"]))
- {
- config.Remove("description");
- config.Add("description", "Table storage-based membership provider");
- }
-
- base.Initialize(name, config);
-
- bool allowInsecureRemoteEndpoints = Configuration.GetBooleanValue(config, "allowInsecureRemoteEndpoints", false);
- _enablePasswordRetrieval = Configuration.GetBooleanValue(config, "enablePasswordRetrieval", false);
- _enablePasswordReset = Configuration.GetBooleanValue(config, "enablePasswordReset", true);
- _requiresQuestionAndAnswer = Configuration.GetBooleanValue(config, "requiresQuestionAndAnswer", true);
- _requiresUniqueEmail = Configuration.GetBooleanValue(config, "requiresUniqueEmail", true);
- _maxInvalidPasswordAttempts = Configuration.GetIntValue(config, "maxInvalidPasswordAttempts", 5, false, 0);
- _passwordAttemptWindow = Configuration.GetIntValue(config, "passwordAttemptWindow", 10, false, 0);
- _minRequiredPasswordLength = Configuration.GetIntValue(config, "minRequiredPasswordLength", 7, false, MaxTablePasswordSize);
- _minRequiredNonalphanumericCharacters = Configuration.GetIntValue(config, "minRequiredNonalphanumericCharacters", 1, true, MaxTablePasswordSize);
-
- _passwordStrengthRegularExpression = config["passwordStrengthRegularExpression"];
- if (_passwordStrengthRegularExpression != null)
- {
- _passwordStrengthRegularExpression = _passwordStrengthRegularExpression.Trim();
- if (_passwordStrengthRegularExpression.Length != 0)
- {
- try
- {
- Regex testIfRegexIsValid = new Regex(_passwordStrengthRegularExpression);
- }
- catch (ArgumentException e)
- {
- throw new ProviderException(e.Message, e);
- }
- }
- }
- else
- {
- _passwordStrengthRegularExpression = string.Empty;
- }
-
- if (_minRequiredNonalphanumericCharacters > _minRequiredPasswordLength)
- {
- throw new HttpException("The minRequiredNonalphanumericCharacters can not be greater than minRequiredPasswordLength.");
- }
-
- string strTemp = config["passwordFormat"];
- if (strTemp == null)
- {
- strTemp = "Hashed";
- }
-
- switch (strTemp)
- {
- case "Clear":
- _passwordFormat = MembershipPasswordFormat.Clear;
- break;
- case "Encrypted":
- _passwordFormat = MembershipPasswordFormat.Encrypted;
- break;
- case "Hashed":
- _passwordFormat = MembershipPasswordFormat.Hashed;
- break;
- default:
- throw new ProviderException("Password format specified is invalid.");
- }
-
- if (PasswordFormat == MembershipPasswordFormat.Hashed && EnablePasswordRetrieval)
- throw new ProviderException("Configured settings are invalid: Hashed passwords cannot be retrieved. Either set the password format to different type, or set supportsPasswordRetrieval to false.");
-
-
- // Table storage-related properties
- _applicationName = Configuration.GetStringValueWithGlobalDefault(config, "applicationName",
- Configuration.DefaultProviderApplicationNameConfigurationString,
- Configuration.DefaultProviderApplicationName, false);
- _accountName = Configuration.GetStringValue(config, "accountName", null, true);
- _sharedKey = Configuration.GetStringValue(config, "sharedKey", null, true);
- _tableName = Configuration.GetStringValueWithGlobalDefault(config, "membershipTableName",
- Configuration.DefaultMembershipTableNameConfigurationString,
- Configuration.DefaultMembershipTableName, false);
- _tableServiceBaseUri = Configuration.GetStringValue(config, "tableServiceBaseUri", null, true);
-
- config.Remove("allowInsecureRemoteEndpoints");
- config.Remove("enablePasswordRetrieval");
- config.Remove("enablePasswordReset");
- config.Remove("requiresQuestionAndAnswer");
- config.Remove("requiresUniqueEmail");
- config.Remove("maxInvalidPasswordAttempts");
- config.Remove("passwordAttemptWindow");
- config.Remove("passwordFormat");
- config.Remove("minRequiredPasswordLength");
- config.Remove("minRequiredNonalphanumericCharacters");
- config.Remove("passwordStrengthRegularExpression");
- config.Remove("applicationName");
- config.Remove("accountName");
- config.Remove("sharedKey");
- config.Remove("membershipTableName");
- config.Remove("tableServiceBaseUri");
-
-
- // Throw an exception if unrecognized attributes remain
- if (config.Count > 0)
- {
- string attr = config.GetKey(0);
- if (!String.IsNullOrEmpty(attr))
- throw new ProviderException("Unrecognized attribute: " + attr);
- }
-
- StorageAccountInfo info = null;
- try
- {
- info = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(true);
- if (_tableServiceBaseUri != null)
- {
- info.BaseUri = new Uri(_tableServiceBaseUri);
- }
- if (_accountName != null)
- {
- info.AccountName = _accountName;
- }
- if (_sharedKey != null)
- {
- info.Base64Key = _sharedKey;
- }
- info.CheckComplete();
- SecUtility.CheckAllowInsecureEndpoints(allowInsecureRemoteEndpoints, info);
- _tableStorage = TableStorage.Create(info);
- _tableStorage.RetryPolicy = _tableRetry;
- _tableStorage.TryCreateTable(_tableName);
- }
- catch (SecurityException)
- {
- throw;
- }
- catch (Exception e)
- {
- string exceptionDescription = Configuration.GetInitExceptionDescription(info, "table storage configuration");
- string tableName = (_tableName == null) ? "no membership table name specified" : _tableName;
- Log.Write(EventKind.Error, "Could not create or find membership table: " + tableName + "!" + Environment.NewLine +
- exceptionDescription + Environment.NewLine +
- e.Message + Environment.NewLine + e.StackTrace);
- throw new ProviderException("Could not create or find membership table. The most probable reason for this is that " +
- "the storage endpoints are not configured correctly. Please look at the configuration settings " +
- "in your .cscfg and Web.config files. More information about this error " +
- "can be found in the logs when running inside the hosting environment or in the output " +
- "window of Visual Studio.", e);
-
- }
- }
-
- /// <summary>
- /// Returns true if the username and password match an exsisting user.
- /// This implementation does not update a user's login time and does
- /// not raise corresponding Web events
- /// </summary>
- public override bool ValidateUser(string username, string password)
- {
- if (SecUtility.ValidateParameter(ref username, true, true, true, Constants.MaxTableUsernameLength) &&
- SecUtility.ValidateParameter(ref password, true, true, false, MaxTablePasswordSize) &&
- CheckPassword(username, password, true, true))
- {
- return true;
- }
- else
- {
- return false;
- }
- }
-
- /// <summary>
- /// Get a user based on the username parameter.
- /// If the userIsOnline parameter is set the lastActivity flag of the user
- /// is changed in the data store
- /// </summary>
- public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
- {
- if (providerUserKey == null)
- {
- throw new ArgumentNullException("providerUserKey");
- }
-
- if (providerUserKey.GetType() != typeof(Guid)) {
- throw new ArgumentException("Provided key is not a Guid!");
- }
- Guid key = (Guid)providerUserKey;
-
- try
- {
- TableStorageDataServiceContext svc = CreateDataServiceContext();
- DataServiceQuery<MembershipRow> queryObj = svc.CreateQuery<MembershipRow>(_tableName);
-
- // we need an IQueryable here because we do a Top(2) in the ProcessGetUserQuery()
- // and cast it to DataServiceQuery object in this function
- // this does not work when we use IEnumerable as a type here
- IQueryable<MembershipRow> query = from user in queryObj
- where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 &&
- user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 &&
- user.UserId == key &&
- user.ProfileIsCreatedByProfileProvider == false
- select user;
- return ProcessGetUserQuery(svc, query, userIsOnline);
- }
- catch (InvalidOperationException e)
- {
- if (TableStorageHelpers.IsTableStorageException(e))
- {
- throw new ProviderException("Error accessing the data source.", e);
- }
- else
- {
- throw;
- }
- }
- }
-
- /// <summary>
- /// Retrieves a user based on his/her username.
- /// The userIsOnline parameter determines whether to update the lastActivityDate of
- /// the user in the data store
- /// </summary>
- public override MembershipUser GetUser(string username, bool userIsOnline)
- {
- SecUtility.CheckParameter(
- ref username,
- true,
- false,
- true,
- Constants.MaxTableUsernameLength,
- "username");
-
- try
- {
- TableStorageDataServiceContext svc = CreateDataServiceContext();
- DataServiceQuery<MembershipRow> queryObj = svc.CreateQuery<MembershipRow>(_tableName);
-
- // we need an IQueryable here because we do a Top(2) in the ProcessGetUserQuery()
- // and cast it to DataServiceQuery object in this function
- // this does not work when we use IEnumerable as a type here
- IQueryable<MembershipRow> query = from user in queryObj
- where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) &&
- user.ProfileIsCreatedByProfileProvider == false
- select user;
- return ProcessGetUserQuery(svc, query, userIsOnline);
- }
- catch (InvalidOperationException e)
- {
- if (TableStorageHelpers.IsTableStorageException(e))
- {
- throw new ProviderException("Error accessing the data source.", e);
- }
- else
- {
- throw;
- }
- }
- }
-
- /// <summary>
- /// Retrieves a collection of all the users.
- /// </summary>
- public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
- {
- if ( pageIndex < 0 ) {
- throw new ArgumentException("The page index cannot be negative.");
- }
- if ( pageSize < 1 ) {
- throw new ArgumentException("The page size can only be a positive integer.");
- }
-
-
- long upperBound = (long)pageIndex * pageSize + pageSize - 1;
- if ( upperBound > Int32.MaxValue ) {
- throw new ArgumentException("pageIndex and pageSize are too big.");
- }
-
- totalRecords = 0;
- MembershipUserCollection users = new MembershipUserCollection();
- TableStorageDataServiceContext svc = CreateDataServiceContext();
- try {
- DataServiceQuery<MembershipRow> queryObj = svc.CreateQuery<MembershipRow>(_tableName);
-
- IEnumerable<MembershipRow> query = from user in queryObj
- where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) > 0 &&
- user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 &&
- user.ProfileIsCreatedByProfileProvider == false
- select user;
-
- TableStorageDataServiceQuery<MembershipRow> q = new TableStorageDataServiceQuery<MembershipRow>(query as DataServiceQuery<MembershipRow>, _tableRetry);
- IEnumerable<MembershipRow> allUsers = q.ExecuteAllWithRetries();
- List<MembershipRow> allUsersSorted = new List<MembershipRow>(allUsers);
-
- // the result should already be sorted because the user name is part of the primary key
- // we have to sort anyway because we have encoded the user names in the key
- // this is also why we cannot use the table stoage pagination mechanism here and need to retrieve all elements
- // for every page
- allUsersSorted.Sort();
-
- int startIndex = pageIndex * pageSize;
- int endIndex = startIndex + pageSize;
- MembershipRow row;
- for (int i = startIndex; i < endIndex && i < allUsersSorted.Count; i++) {
- row = allUsersSorted.ElementAt<MembershipRow>(i);
- users.Add(new MembershipUser(this.Name,
- row.UserName,
- row.UserId,
- row.Email,
- row.PasswordQuestion,
- row.Comment,
- row.IsApproved,
- row.IsLockedOut,
- row.CreateDateUtc.ToLocalTime(),
- row.LastLoginDateUtc.ToLocalTime(),
- row.LastActivityDateUtc.ToLocalTime(),
- row.LastPasswordChangedDateUtc.ToLocalTime(),
- row.LastLockoutDateUtc.ToLocalTime()));
- }
- } catch (InvalidOperationException e) {
- if (TableStorageHelpers.IsTableStorageException(e))
- {
- throw new ProviderException("Error accessing the data source.", e);
- }
- else
- {
- throw;
- }
- }
- totalRecords = users.Count;
- return users;
- }
-
-
- /// <summary>
- /// Changes a users password. We don't use retries in this highly security-related function.
- /// All errors are exposed to the user of this function.
- /// </summary>
- public override bool ChangePassword(string username, string oldPassword, string newPassword)
- {
- SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username");
- SecUtility.CheckParameter(ref oldPassword, true, true, false, MaxTablePasswordSize, "oldPassword");
- SecUtility.CheckParameter(ref newPassword, true, true, false, MaxTablePasswordSize, "newPassword");
-
- try
- {
- string salt = null;
- int passwordFormat;
- MembershipRow member;
- TableStorageDataServiceContext svc = CreateDataServiceContext();
-
- if (!CheckPassword(svc, username, oldPassword, false, false, out member))
- {
- return false;
- }
- salt = member.PasswordSalt;
- passwordFormat = member.PasswordFormat;
-
- if (newPassword.Length < MinRequiredPasswordLength)
- {
- throw new ArgumentException("The new password is to short.");
- }
-
- int count = 0;
-
- for (int i = 0; i < newPassword.Length; i++)
- {
- if (!char.IsLetterOrDigit(newPassword, i))
- {
- count++;
- }
- }
-
- if (count < MinRequiredNonAlphanumericCharacters)
- {
- throw new ArgumentException("The new password does not have enough non-alphanumeric characters!");
- }
-
- if (PasswordStrengthRegularExpression.Length > 0)
- {
- if (!Regex.IsMatch(newPassword, PasswordStrengthRegularExpression))
- {
- throw new ArgumentException("The new password does not match the specified password strength regular expression.");
- }
- }
-
- string pass = EncodePassword(newPassword, (int)passwordFormat, salt);
- if (pass.Length > MaxTablePasswordSize)
- {
- throw new ArgumentException("Password is too long!");
- }
-
- ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, newPassword, false);
- OnValidatingPassword(e);
-
- if (e.Cancel)
- {
- if (e.FailureInformation != null)
- {
- throw e.FailureInformation;
- }
- else
- {
- throw new ArgumentException("Password validation failure!");
- }
- }
-
- member.Password = pass;
- member.PasswordSalt = salt;
- member.PasswordFormat = passwordFormat;
- member.LastPasswordChangedDateUtc = DateTime.UtcNow;
- svc.UpdateObject(member);
- svc.SaveChanges();
-
- return true;
- }
- catch (InvalidOperationException e)
- {
- if (TableStorageHelpers.IsTableStorageException(e))
- {
- throw new ProviderException("Error accessing the data source.", e);
- }
- else
- {
- throw;
- }
- }
- }
-
- /// <summary>
- /// Creates a new user and stores it in the membership table. We do not use retry policies in this
- /// highly security-related function. All error conditions are directly exposed to the user.
- /// </summary>
- public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion,
- string passwordAnswer, bool isApproved, object providerUserKey,
- out MembershipCreateStatus status)
- {
- if (!SecUtility.ValidateParameter(ref password, true, true, false, MaxTablePasswordSize))
- {
- status = MembershipCreateStatus.InvalidPassword;
- return null;
- }
-
- string salt = GenerateSalt();
- string pass = EncodePassword(password, (int)_passwordFormat, salt);
- if (pass.Length > MaxTablePasswordSize)
- {
- status = MembershipCreateStatus.InvalidPassword;
- return null;
- }
-
- string encodedPasswordAnswer;
- if (passwordAnswer != null)
- {
- passwordAnswer = passwordAnswer.Trim();
- }
-
- if (!string.IsNullOrEmpty(passwordAnswer))
- {
- if (passwordAnswer.Length > MaxTablePasswordSize)
- {
- status = MembershipCreateStatus.InvalidAnswer;
- return null;
- }
- encodedPasswordAnswer = EncodePassword(passwordAnswer.ToLowerInvariant(), (int)_passwordFormat, salt);
- }
- else
- {
- encodedPasswordAnswer = passwordAnswer;
- }
-
- if (!SecUtility.ValidateParameter(ref encodedPasswordAnswer, RequiresQuestionAndAnswer, true, false, MaxTablePasswordSize))
- {
- status = MembershipCreateStatus.InvalidAnswer;
- return null;
- }
-
- if (!SecUtility.ValidateParameter(ref username, true, true, true, Constants.MaxTableUsernameLength))
- {
- status = MembershipCreateStatus.InvalidUserName;
- return null;
- }
-
- if (!SecUtility.ValidateParameter(ref email,
- RequiresUniqueEmail,
- RequiresUniqueEmail,
- false,
- Constants.MaxTableUsernameLength))
- {
- status = MembershipCreateStatus.InvalidEmail;
- return null;
- }
-
- if (!SecUtility.ValidateParameter(ref passwordQuestion, RequiresQuestionAndAnswer, true, false, Constants.MaxTableUsernameLength))
- {
- status = MembershipCreateStatus.InvalidQuestion;
- return null;
- }
-
- if (providerUserKey != null)
- {
- if (!(providerUserKey is Guid))
- {
- status = MembershipCreateStatus.InvalidProviderUserKey;
- return null;
- }
- }
-
- if (!EvaluatePasswordRequirements(password))
- {
- status = MembershipCreateStatus.InvalidPassword;
- return null;
- }
-
- ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, password, true);
- OnValidatingPassword(e);
-
- if (e.Cancel)
- {
- status = MembershipCreateStatus.InvalidPassword;
- return null;
- }
-
- // Check whether a user with the same email address already exists.
- // The danger here is (as we don't have transaction support here) that
- // there are overlapping requests for creating two users with the same email
- // address at the same point in time.
- // A solution for this would be to have a separate table for email addresses.
- // At this point here in the code we would try to insert this user's email address into the
- // table and thus check whether the email is unique (the email would be the primary key of the
- // separate table). There are quite some problems
- // associated with that. For example, what happens if the user creation fails etc., stale data in the
- // email table etc.
- // Another solution is to already insert the user at this point and then check at the end of this
- // funcation whether the email is unique.
- if (RequiresUniqueEmail && !IsUniqueEmail(email))
- {
- status = MembershipCreateStatus.DuplicateEmail;
- return null;
- }
-
- try
- {
- TableStorageDataServiceContext svc = CreateDataServiceContext();
- MembershipRow newUser = new MembershipRow(_applicationName, username);
- if (providerUserKey == null)
- {
- providerUserKey = Guid.NewGuid();
- }
- newUser.UserId = (Guid)providerUserKey;
- newUser.Password = pass;
- newUser.PasswordSalt = salt;
- newUser.Email = (email == null) ? string.Empty : email; ;
- newUser.PasswordQuestion = (passwordQuestion == null) ? string.Empty : passwordQuestion;
- newUser.PasswordAnswer = (encodedPasswordAnswer == null) ? string.Empty : encodedPasswordAnswer;
- newUser.IsApproved = isApproved;
- newUser.PasswordFormat = (int)_passwordFormat;
- DateTime now = DateTime.UtcNow;
- newUser.CreateDateUtc = now;
- newUser.LastActivityDateUtc = now;
- newUser.LastPasswordChangedDateUtc = now;
- newUser.LastLoginDateUtc = now;
- newUser.IsLockedOut = false;
-
- svc.AddObject(_tableName, newUser);
- svc.SaveChanges();
-
- status = MembershipCreateStatus.Success;
- return new MembershipUser(this.Name,
- username,
- providerUserKey,
- email,
- passwordQuestion,
- null,
- isApproved,
- false,
- now.ToLocalTime(),
- now.ToLocalTime(),
- now.ToLocalTime(),
- now.ToLocalTime(),
- TableStorageConstants.MinSupportedDateTime);
- }
- catch (InvalidOperationException ex)
- {
- HttpStatusCode httpStatus;
- if (TableStorageHelpers.EvaluateException(ex, out httpStatus) && httpStatus == HttpStatusCode.Conflict)
- {
- // in this case, some membership providers update the last activity time of the user
- // we don't do this in this implementation because it would add another roundtrip
- status = MembershipCreateStatus.DuplicateUserName;
- return null;
- }
- else if (TableStorageHelpers.IsTableStorageException(ex))
- {
- throw new ProviderException("Cannot add user to membership data store because of problems when accessing the data store.", ex);
- }
- else
- {
- throw;
- }
- }
- }
-
- /// <summary>
- /// Deletes the user from the membership table.
- /// This implementation ignores the deleteAllRelatedData argument
- /// </summary>
- public override bool DeleteUser(string username, bool deleteAllRelatedData)
- {
- SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username");
-
- try
- {
- TableStorageDataServiceContext svc = CreateDataServiceContext();
- MembershipRow user = new MembershipRow(_applicationName, username);
- svc.AttachTo(_tableName, user, "*");
- svc.DeleteObject(user);
- svc.SaveChangesWithRetries();
- return true;
- }
- catch (InvalidOperationException e)
- {
- HttpStatusCode status;
- if (TableStorageHelpers.EvaluateException(e, out status)) {
- if (status == HttpStatusCode.NotFound)
- {
- return false;
- }
- else
- {
- throw new ProviderException("Error accessing the data source.", e);
- }
- }
- else
- {
- throw;
- }
- }
- }
-
- /// <summary>
- /// Retrieves a username based on a matching email.
- /// </summary>
- public override string GetUserNameByEmail(string email)
- {
- SecUtility.CheckParameter(ref email, false, false, false, MaxTableEmailLength, "email");
-
- string nonNullEmail = (email == null) ? string.Empty : email;
-
- try
- {
- DataServiceContext svc = CreateDataServiceContext();
- DataSe…
Large files files are truncated, but you can click here to view the full file