Skip to content

Instantly share code, notes, and snippets.

@joshilewis
Created March 8, 2012 12:50
Show Gist options
  • Save joshilewis/2000863 to your computer and use it in GitHub Desktop.
Save joshilewis/2000863 to your computer and use it in GitHub Desktop.
NHibernate-based implementation of ServiceStack's IUserAuthRepository
This is an NHibernate-based implementation of ServiceStack's IUserAuthRepository
Out-of-the-box, ServiceStack's UserAuth entity cannot be persisted by NHibernate because one of its properties is a List<> (and not an ILis<>).
The work-around for this is to wrap the UserAuth class in another class, UserAuthPersistenceDTO, and map between the two classes in the
repository implementation.
Note that the UserAuthMap and UserOAuthProviderMap classes are FluentNhibernate mapping classes.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
using ServiceStack.ServiceInterface.Auth;
using ServiceStack.Common;
using NHibernate;
using Ismoos.Infrastructure.Persistence;
namespace Ismoos.Infrastructure.ServiceStack
{
public class NHibernateUserAuthRepository : IUserAuthRepository
{
//http://stackoverflow.com/questions/3588623/c-sharp-regex-for-a-username-with-a-few-restrictions
public Regex ValidUserNameRegEx = new Regex(@"^(?=.{3,15}$)([A-Za-z0-9][._-]?)*$", RegexOptions.Compiled);
private readonly INHibernateSessionProvider sessionProvider;
public NHibernateUserAuthRepository(INHibernateSessionProvider sessionProvider)
{
this.sessionProvider = sessionProvider;
}
private ISession session
{
get { return sessionProvider.CurrentSession; }
}
public void LoadUserAuth(IAuthSession session, IOAuthTokens tokens)
{
session.ThrowIfNull("session");
var userAuth = GetUserAuth(session, tokens);
LoadUserAuth(session, userAuth);
}
public UserAuth GetUserAuth(IAuthSession authSession, IOAuthTokens tokens)
{
if (!authSession.UserAuthId.IsNullOrEmpty())
{
var userAuth = GetUserAuth(authSession.UserAuthId);
if (userAuth != null) return userAuth;
}
if (!authSession.UserAuthName.IsNullOrEmpty())
{
var userAuth = GetUserAuthByUserName(authSession.UserAuthName);
if (userAuth != null) return userAuth;
}
if (tokens == null || tokens.Provider.IsNullOrEmpty() || tokens.UserId.IsNullOrEmpty())
return null;
var oAuthProvider = session.QueryOver<UserOAuthProvider>()
.Where(x => x.Provider == tokens.Provider)
.And(x => x.UserId == tokens.UserId)
.SingleOrDefault();
if (oAuthProvider != null)
{
return session.QueryOver<UserAuthPersistenceDTO>()
.Where(x => x.Id == oAuthProvider.UserAuthId)
.SingleOrDefault();
}
return null;
}
public UserAuth GetUserAuth(string userAuthId)
{
int authId = int.Parse(userAuthId);
return session.QueryOver<UserAuthPersistenceDTO>()
.Where(x => x.Id == authId)
.SingleOrDefault()
;
}
private void LoadUserAuth(IAuthSession session, UserAuth userAuth)
{
if (userAuth == null) return;
session.PopulateWith(userAuth);
session.UserAuthId = userAuth.Id.ToString(CultureInfo.InvariantCulture);
session.ProviderOAuthAccess = GetUserOAuthProviders(session.UserAuthId)
.ConvertAll(x => (IOAuthTokens)x);
}
public bool TryAuthenticate(string userName, string password, out string userId)
{
userId = null;
var userAuth = GetUserAuthByUserName(userName);
if (userAuth == null) return false;
var saltedHash = new SaltedHash();
if (saltedHash.VerifyHashString(password, userAuth.PasswordHash, userAuth.Salt))
{
userId = userAuth.Id.ToString(CultureInfo.InvariantCulture);
return true;
}
return false;
}
public UserAuth GetUserAuthByUserName(string userNameOrEmail)
{
UserAuthPersistenceDTO user = null;
if (userNameOrEmail.Contains("@"))
{
user = session.QueryOver<UserAuthPersistenceDTO>()
.Where(x => x.Email == userNameOrEmail)
.SingleOrDefault();
}
else
{
user = session.QueryOver<UserAuthPersistenceDTO>()
.Where(x => x.UserName == userNameOrEmail)
.SingleOrDefault();
}
return user;
}
public string CreateOrMergeAuthSession(IAuthSession authSession, IOAuthTokens tokens)
{
var userAuth = GetUserAuth(authSession, tokens) ?? new UserAuth();
var oAuthProvider = session.QueryOver<UserOAuthProvider>()
.Where(x => x.Provider == tokens.Provider)
.And(x => x.UserId == tokens.UserId)
.SingleOrDefault();
if (oAuthProvider == null)
{
oAuthProvider = new UserOAuthProvider
{
Provider = tokens.Provider,
UserId = tokens.UserId,
};
}
oAuthProvider.PopulateMissing(tokens);
userAuth.PopulateMissing(oAuthProvider);
userAuth.ModifiedDate = DateTime.UtcNow;
if (userAuth.CreatedDate == default(DateTime))
userAuth.CreatedDate = userAuth.ModifiedDate;
session.Save(new UserAuthPersistenceDTO(userAuth));
oAuthProvider.UserAuthId = userAuth.Id;
if (oAuthProvider.CreatedDate == default(DateTime))
oAuthProvider.CreatedDate = userAuth.ModifiedDate;
oAuthProvider.ModifiedDate = userAuth.ModifiedDate;
session.Save(oAuthProvider);
return oAuthProvider.UserAuthId.ToString(CultureInfo.InvariantCulture);
}
public List<UserOAuthProvider> GetUserOAuthProviders(string userAuthId)
{
int authId = int.Parse(userAuthId);
var value = session.QueryOver<UserOAuthProvider>()
.Where(x => x.UserAuthId == authId)
.OrderBy(x => x.ModifiedDate).Asc
.List();
var providerList = new List<UserOAuthProvider>();
foreach (var item in value)
{
providerList.Add(item);
}
return providerList;
//return new List<UserOAuthProvider>(value);
}
public UserAuth CreateUserAuth(UserAuth newUser, string password)
{
ValidateNewUser(newUser, password);
AssertNoExistingUser(newUser);
var saltedHash = new SaltedHash();
string salt;
string hash;
saltedHash.GetHashAndSaltString(password, out hash, out salt);
newUser.PasswordHash = hash;
newUser.Salt = salt;
newUser.CreatedDate = DateTime.UtcNow;
newUser.ModifiedDate = newUser.CreatedDate;
session.Save(new UserAuthPersistenceDTO(newUser));
return newUser;
}
private void ValidateNewUser(UserAuth newUser, string password)
{
newUser.ThrowIfNull("newUser");
password.ThrowIfNullOrEmpty("password");
if (newUser.UserName.IsNullOrEmpty() && newUser.Email.IsNullOrEmpty())
throw new ArgumentNullException("UserName or Email is required");
if (!newUser.UserName.IsNullOrEmpty())
{
if (!ValidUserNameRegEx.IsMatch(newUser.UserName))
throw new ArgumentException("UserName contains invalid characters", "UserName");
}
}
private void AssertNoExistingUser(UserAuth newUser, UserAuth exceptForExistingUser = null)
{
if (newUser.UserName != null)
{
var existingUser = GetUserAuthByUserName(newUser.UserName);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format("User {0} already exists", newUser.UserName));
}
if (newUser.Email != null)
{
var existingUser = GetUserAuthByUserName(newUser.Email);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format("Email {0} already exists", newUser.Email));
}
}
public void SaveUserAuth(UserAuth userAuth)
{
userAuth.ModifiedDate = DateTime.UtcNow;
if (userAuth.CreatedDate == default(DateTime))
userAuth.CreatedDate = userAuth.ModifiedDate;
session.Save(new UserAuthPersistenceDTO(userAuth));
}
public void SaveUserAuth(IAuthSession authSession)
{
var userAuth = !authSession.UserAuthId.IsNullOrEmpty()
? session.Load<UserAuthPersistenceDTO>(int.Parse(authSession.UserAuthId))
: authSession.TranslateTo<UserAuth>();
if (userAuth.Id == default(int) && !authSession.UserAuthId.IsNullOrEmpty())
userAuth.Id = int.Parse(authSession.UserAuthId);
userAuth.ModifiedDate = userAuth.ModifiedDate;
if (userAuth.CreatedDate == default(DateTime))
userAuth.CreatedDate = userAuth.ModifiedDate;
session.Save(new UserAuthPersistenceDTO(userAuth));
}
public UserAuth UpdateUserAuth(UserAuth existingUser, UserAuth newUser, string password)
{
ValidateNewUser(newUser, password);
AssertNoExistingUser(newUser, existingUser);
var hash = existingUser.PasswordHash;
var salt = existingUser.Salt;
if (password != null)
{
var saltedHash = new SaltedHash();
saltedHash.GetHashAndSaltString(password, out hash, out salt);
}
newUser.Id = existingUser.Id;
newUser.PasswordHash = hash;
newUser.Salt = salt;
newUser.CreatedDate = existingUser.CreatedDate;
newUser.ModifiedDate = DateTime.UtcNow;
session.Save(new UserAuthPersistenceDTO(newUser));
return newUser;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.Mapping;
using ServiceStack.ServiceInterface.Auth;
namespace Ismoos.Infrastructure.ServiceStack
{
public class UserAuthMap : ClassMap<UserAuthPersistenceDTO>
{
public UserAuthMap()
{
Table("UserAuth");
Id(x => x.Id)
.GeneratedBy.Native();
Map(x => x.CreatedDate);
Map(x => x.DisplayName);
Map(x => x.Email);
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.ModifiedDate);
Map(x => x.PasswordHash);
Map(x => x.PrimaryEmail);
Map(x => x.Salt);
Map(x => x.UserName);
HasManyToMany(x => x.Permissions1)
.Table("UserAuth_Permissions")
.ParentKeyColumn("UserAuthID")
.Element("Permission");
HasManyToMany(x => x.Roles1)
.Table("UserAuth_Roles")
.ParentKeyColumn("UserAuthID")
.Element("Role");
}
}
public class UserAuthPersistenceDTO : UserAuth
{
public UserAuthPersistenceDTO()
: base()
{ }
public UserAuthPersistenceDTO(UserAuth userAuth)
{
Id = userAuth.Id;
UserName = userAuth.UserName;
Email = userAuth.Email;
PrimaryEmail = userAuth.PrimaryEmail;
FirstName = userAuth.FirstName;
LastName = userAuth.LastName;
DisplayName = userAuth.DisplayName;
Salt = userAuth.Salt;
PasswordHash = userAuth.PasswordHash;
Roles = userAuth.Roles;
Permissions = userAuth.Permissions;
CreatedDate = userAuth.CreatedDate;
ModifiedDate = userAuth.ModifiedDate;
}
public virtual IList<string> Permissions1
{
get { return Permissions; }
set { Permissions = new List<string>(value); }
}
public virtual IList<string> Roles1
{
get { return Roles; }
set { Roles = new List<string>(value); }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.Mapping;
using ServiceStack.ServiceInterface.Auth;
namespace Ismoos.Infrastructure.ServiceStack
{
public class UserOAuthProviderMap : ClassMap<UserOAuthProvider>
{
public UserOAuthProviderMap()
{
Table("UserOAuthProvider");
Id(x => x.Id)
.GeneratedBy.Native();
Map(x => x.AccessToken);
Map(x => x.AccessTokenSecret);
Map(x => x.CreatedDate);
Map(x => x.DisplayName);
Map(x => x.Email);
Map(x => x.FirstName);
Map(x => x.LastName);
Map(x => x.ModifiedDate);
Map(x => x.Provider);
Map(x => x.RequestToken);
Map(x => x.RequestTokenSecret);
Map(x => x.UserAuthId);
Map(x => x.UserId);
Map(x => x.UserName);
HasMany(x => x.Items)
.AsMap<string>(
index => index.Column("`Key`").Type<string>(),
element => element.Column("Value").Type<string>())
.KeyColumn("UserOAuthProviderID")
.Table("UserOAuthProvider_Items")
.Not.LazyLoad()
.Cascade.All();
}
}
}
@mythz
Copy link

mythz commented Jun 4, 2012

Cool, As I was working on creating NuGets for Contrib on the weekend I ended up adding it to https://github.com/ServiceStack/ServiceStack.Contrib and have already published a NuGet package at: https://nuget.org/packages/ServiceStack.Authentication.NHibernate
I changed it to use an ISessionFactory but apart from that, it was left as-is.

Feel free to make any changes you want to https://github.com/ServiceStack/ServiceStack.Contrib/tree/master/src/ServiceStack.Authentication.NHibernate - and I'll publish them on NuGet :)

@joshilewis
Copy link
Author

Awesome, thanks :)
My first author credit!

@mythz
Copy link

mythz commented Jun 8, 2012 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment