ASP.NET - Membership API
Esempio di DependencyInjection per gestire la security di ASP.NET usando WCF e Unity

Su CodePlex ho pubblicato un semplice esempio su come rendere l’applicazione “dipendente” dalle DipendencyInjection :-). Bel controsenso. Scherzi a parte l’applicazione (mantenuta come sempre il più semplice possibile) pone il fuoco su come adottare un framework di IoC, wrappandolo, e quindi rendendo trasparente anche quello, e su come risolvere uno dei tanti problemi delle applicazioni web, come ad esempio cambiare i Membership e Role provider. In questo caso ci sono due implementazioni, la più interessante è quella che sfrutta WCF per gestire le credenziali. Intanto la trovate qui. Appena ho un attimo di tempo scriverò un po’ di documentazione e magari ne sviluppo una seconda versione più “real-world”, sfruttando anche Silverlight e il porting Silverlight di Unity.

Eccola qui, DependencyInjection sample to build a security system using-WCF, Unity, ASP.NET.

Se avete domande sparate pure.

Matteo Migliore.

Implementare un custom Membership provider con ASP.NET
Un esempio su come scrivere un custom Membership provider per ASP.NET che consenta di validare correttamente la password nei vari metodi.
In particolare se la password non rispetta i vincoli imposti dal provider, la cosa da fare è lanciare un eccezione, che viene poi intercettata dai Login control e viene comunicato all'utente il problema. Bisogna inoltre invocare il metodo OnValidatingPassword, nel caso in cui la password sia corretta, per scatenare l'evento ValidatingPassword del provider.
Qui il codice:
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
    PasswordCheck passwordCheck = IsValidPassword(newPassword);
    if (passwordCheck == PasswordCheck.MinRequiredPasswordLength)
        throw new ArgumentException("Password does not respects min required lenght.");

    if (passwordCheck == PasswordCheck.NonAlphanumericCharacters)
        throw new ArgumentException("Password does not respects alphanumeric lenght.");

    ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, newPassword, false);
    OnValidatingPassword(e);
    return true;
}

public override MembershipUser CreateUser
    (string username, string password, 
     string email, string passwordQuestion, 
     string passwordAnswer, bool isApproved, 
     object providerUserKey, out MembershipCreateStatus status)
{
    if (IsValidPassword(password) != PasswordCheck.Valid) {
        status = MembershipCreateStatus.InvalidPassword;
        return null;
    }

    ValidatePasswordEventArgs e = new ValidatePasswordEventArgs(username, password, true);
    OnValidatingPassword(e);

    DateTime now = DateTime.Now;
    status = MembershipCreateStatus.Success;
    MembershipUser user = new MembershipUser
        (this.GetType().Name, username, Guid.NewGuid(), 
         email, passwordQuestion, string.Empty, true, false, 
         now, now, now, now, now);
    HttpContext.Current.Cache[user.UserName] = user;
    return user;
}

private enum PasswordCheck { 
    Valid = 0,
    NonAlphanumericCharacters = 2,
    MinRequiredPasswordLength = 4
}

private PasswordCheck IsValidPassword(string password) {
    if (password.Length < this.MinRequiredPasswordLength)
        return PasswordCheck.MinRequiredPasswordLength;

    int nonAlphanumericCharacters = 0;
    for (int i = 0; i < password.Length; i++)
    {
        if (!char.IsLetterOrDigit(password, i))
            nonAlphanumericCharacters++;
    }

    if (nonAlphanumericCharacters < this.MinRequiredNonAlphanumericCharacters)
        return PasswordCheck.NonAlphanumericCharacters;

    return PasswordCheck.Valid;
}

public override MembershipUser GetUser(string username, bool userIsOnline)
{
    MembershipUser user = HttpContext.Current.Cache[username] as MembershipUser;
    return user;
}

public override bool ValidateUser(string username, string password)
{
    MembershipUser user = HttpContext.Current.Cache[username] as MembershipUser;
    return ((user != null) && user.GetPassword().Equals(password));
}

Matteo Migliore.
Profile API ASP.NET: disabilitare LastActivityDate, quando non serve

Le Profile API sono una feature di ASP.NET che conosco in quanto è uno degli argomenti che tratto durante i corsi che tengo, ma in realtà non la uso mai perchè preferisco creare domain model ad-hoc.
In un progetto che sto seguendo ho voluto affidarmi temporaneamente all'utilizzo di questa funzionalità, che ci ha fatto risparmiare un po' di tempo.
Il fatto è che ha creato un problema di riflesso, era ovvio :-); interrogando le proprietà del profilo viene anche aggiornata la proprietà LastActivityDate dell'utente a cui è associato, usando l'SqlMembershipProvider.
In questo modo, ciclando sugli utenti per visualizzarli in un GridView ad esempio, appaiono tutti online :-D.

La proprietà MembershipUser.IsOnline infatti viene calcolata facendo la differenza tra la Membership.UserIsOnlineTimeWindow e LastActivityDate appunto.
La soluzione a questo problema la trovate qui:
http://www.dotnet-friends.com/fastcode/sql/fastcodeinsqld7f4ebfa-dcc1-4f18-a073-3f3d10885207.aspx

Attenzione anche al metodo Membership.GetUser(username) o Membership.GetUser(providerUserKey), entrambi hanno un overload che consente di specificare se aggiornare LastActivityDate passando un bool userIsOnline.

Matteo Migliore.

Una mini-applicazione... in espansione :-)!

Ho creato una piccola solution, grazie ad una domanda su NG (microsoft.public.it.dotnet.asp), che può essere uno startup per capire come gestire l'autenticazione in ASP.NET sfruttando le MemberShip API.

La solution contiene una classe chiamata SimpleMembershipProvider che implementa solo un metodo della classe abstract MembershipProvider, il metodo ValidateUser che data username e password restituisce true se le credenziali fornite sono valide, false altrimenti.

La classe fa il minimo indispensabile, può servire per utilizzare i Login controls di ASP.NET e al tempo stesso non dover scomodare uno storage.

L'intento sarebbe quello di creare una delle tante WebApplication, completa di tutte le funzionalità che rende disponibili ASP.NET 2.0.

Ma adesso, dove posso pubblicarla sul Blog?