Skip to content

Instantly share code, notes, and snippets.

@tjrobinson
Created August 11, 2014 08:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tjrobinson/c8758ee849fe9d258b09 to your computer and use it in GitHub Desktop.
Save tjrobinson/c8758ee849fe9d258b09 to your computer and use it in GitHub Desktop.
MembershipReboot UserAccountService implementation with custom username validation
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using BrockAllen.MembershipReboot;
namespace Acme.Users.MembershipReboot
{
public class AcmeUserAccountService<TAccount> : UserAccountService<TAccount>
where TAccount : UserAccount
{
public AcmeUserAccountService(IUserAccountRepository<TAccount> userRepository)
: this(new MembershipRebootConfiguration<TAccount>(), userRepository) { }
public AcmeUserAccountService(MembershipRebootConfiguration<TAccount> configuration, IUserAccountRepository<TAccount> userRepository)
: base(configuration, userRepository)
{
// Set here so that we can access it from CreateAccount()
this.userRepository = userRepository;
// Set up our alternative usernameValidator which will be used in CreateAccount()
usernameValidator = new Lazy<AggregateValidator<TAccount>>(() =>
{
var val = new AggregateValidator<TAccount>
{
UsernameMustNotAlreadyExist,
UsernameCanOnlyStartOrEndWithLetterOrDigit,
configuration.UsernameValidator
};
return val;
});
}
/// <remarks>
/// We have to replicate this here as it's private in UserAccountService
/// </remarks>
private readonly IUserAccountRepository<TAccount> userRepository;
/// <remarks>
/// We have to replicate this here as it's private in UserAccountService
/// </remarks>
private readonly Lazy<AggregateValidator<TAccount>> usernameValidator;
/// <remarks>
/// Copied from MembershipReboot internal UserAccountValidation class with only a minor change to the tracing information
/// </remarks>
public static readonly IValidator<TAccount> UsernameMustNotAlreadyExist =
new DelegateValidator<TAccount>((service, account, value) =>
{
if (service.UsernameExists(account.Tenant, value))
{
Tracing.Verbose("[AcmeUserAccountService.EmailMustNotAlreadyExist] validation failed: {0}, {1}, {2}", account.Tenant, account.Username, value);
return new ValidationResult(service.GetValidationMessage(MembershipRebootConstants.ValidationMessages.UsernameAlreadyInUse));
}
return null;
});
/// <remarks>
/// Copied from MembershipReboot internal UserAccountValidation class with only a minor change to the tracing information
/// </remarks>
public static readonly IValidator<TAccount> UsernameCanOnlyStartOrEndWithLetterOrDigit =
new DelegateValidator<TAccount>((service, account, value) =>
{
if (!Char.IsLetterOrDigit(value.First()) || !Char.IsLetterOrDigit(value.Last()))
{
Tracing.Verbose("[AcmeUserAccountService.UsernameCanOnlyStartOrEndWithLetterOrDigit] validation failed: {0}, {1}, {2}", account.Tenant, account.Username, value);
return new ValidationResult(service.GetValidationMessage(MembershipRebootConstants.ValidationMessages.UsernameCanOnlyStartOrEndWithLetterOrDigit));
}
return null;
});
/// <remarks>
/// Copied from MembershipReboot UserAccountService class but altered to allow us to use our own username validation rules
/// </remarks>
public override TAccount CreateAccount(string tenant, string username, string password, string email, Guid? id = null,
DateTime? dateCreated = null)
{
if (Configuration.EmailIsUsername)
{
Tracing.Verbose("[AcmeUserAccountService.CreateAccount] applying email is username");
username = email;
}
if (!Configuration.MultiTenant)
{
Tracing.Verbose("[AcmeUserAccountService.CreateAccount] applying default tenant");
tenant = Configuration.DefaultTenant;
}
Tracing.Information("[AcmeUserAccountService.CreateAccount] called: {0}, {1}, {2}", tenant, username, email);
TAccount account = this.userRepository.Create();
Init(account, tenant, username, password, email, id, dateCreated);
ValidateEmail(account, email);
ValidateUsername(account, username);
ValidatePassword(account, password);
Tracing.Verbose("[AcmeUserAccountService.CreateAccount] success");
userRepository.Add(account);
return account;
}
/// <remarks>
/// Copied from MembershipReboot UserAccountService class but altered to allow us to use our own username validation rules
/// </remarks>
protected new void ValidateUsername(TAccount account, string value)
{
ValidationResult validationResult = usernameValidator.Value.Validate(this, account, value);
if (validationResult == null || validationResult == ValidationResult.Success)
return;
Tracing.Error("ValidateUsername failed: " + validationResult.ErrorMessage);
throw new ValidationException(validationResult.ErrorMessage);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment