Value can not be null on Role Provider - c#

I am beginner at MVC C#. Now I am trying to build a custom authentication with role provider, but when it check for user role its getting an error that "Value can not be null".
Here is my RoleProvider:
public class MyRoleProvider:RoleProvider
{
private int _cacheTimeoutInMinute = 20;
LoginGateway gateway=new LoginGateway();
public override string[] GetRolesForUser(string username)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
return null;
}
//check cache
var cacheKey = string.Format("{0}_role", username);
if (HttpRuntime.Cache[cacheKey] != null)
{
return (string[])HttpRuntime.Cache[cacheKey];
}
string[] roles
= gateway.GetUserRole(username).ToArray();
if (roles.Any())
{
HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration);
}
return roles;
}
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = GetRolesForUser(username);
return userRoles.Contains(roleName);
}
Its always Getting error on reurn userRoles.Contains(roleName); (value can not be null(userName)) line. I used debug pointer, it shows gateway never been invoked.I,e: string[] roles = gateway.GetUserRole(username).ToArray(); so roles always remain null.
Although I am not sure that my GetUserRole method on gateway is correct or not: here is my gateway:
public string[] GetUserRole(string userName)
{
string role = null;
SqlConnection connection = new SqlConnection(connectionString);
string query = "select r.RoleType from RoleTable r join UserRoleTable ur on ur.RoleId=r.Id join UserTable u on u.UserId=ur.UserId where u.UserName='"+userName+"'";
SqlCommand command = new SqlCommand(query, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
role = reader["RoleType"].ToString();
}
string[] roles = {role};
reader.Close();
connection.Close();
return roles;
}
Is there any problem with my code? and how could I solve this error?

it works if I delete or comment out httpContext and all code for
cache...Is there Anything wrong with cache portion code?? #Win
I won't worry about hitting database, unless you are developing Enterprise Application in which speed is an issue and every query count. Original ASP.Net Membership Provider doesn't use Cache at all.
Besides, ASP.Net Membership Provider is an very old technology - more than a decade old. If you are implementing for Enterprise Application, you might want to consider using Claim-Based Authentication.
Here is the cache service if you want to use -
using System;
using System.Collections.Generic;
using System.Runtime.Caching;
public class CacheService
{
protected ObjectCache Cache => MemoryCache.Default;
public T Get<T>(string key)
{
return (T) Cache[key];
}
public void Set(string key, object data, int cacheTime)
{
if (data == null)
return;
var policy = new CacheItemPolicy();
policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime);
Cache.Add(new CacheItem(key, data), policy);
}
public void Remove(string key)
{
Cache.Remove(key);
}
}

Related

How to implement a many to many relationship in ASP.NET using SQL Server

Basically I am trying to insert a userId value and a RoleId value in an intermediate table in SQL Server 2010. The problem is that the code is not reaching the table and it stays empty while, using breakpoints, I can see the values to allocate the role are correct. I am using ASP.NET MVC 5 as a school project where I am trying to give users roles for different privileges in the website.
The method to set the roles is in the business layer:
public void RegisterUser(CommonLayer.User User, string ConfirmPassword)
{
CommonLayer.User Existing = this.GetUser(User.UserEmail);
BuisnessLayer.Roles roles = new BuisnessLayer.Roles();
if (Existing == null)
{
if (User.UserPassword.Equals(ConfirmPassword))
{
User.UserId = Guid.NewGuid();
User.UserPassword = HashSHA512String(User.UserPassword, User.UserId.ToString());
User.UserTypeId = (1);
this.AddUser(User);
roles.AddUserRole("USR",User.UserId);
}
}
}
AddUserRole seems to be the problem because it is not inserting in the table the method for AddUserRole in the business logic is:
public void AddUserRole(string RoleCode, Guid UserId)
{
DataLayer.DARoles dar = new DataLayer.DARoles(this.entities);
DataLayer.DAUsers dau = new DataLayer.DAUsers(this.entities);
CommonLayer.User User = dau.GetUser(UserId);
CommonLayer.Role Role = dar.GetRole(RoleCode);
dar.AllocateUserRole(User, Role);
}
Here are the codes for GetUser and GetRole in the data layer:
public CommonLayer.User GetUser(Guid UserId)
{
return this.Entities.Users.SingleOrDefault(p => p.UserId.Equals(UserId));
}
public CommonLayer.Role GetRole(string RoleCode)
{
return this.Entities.Roles.SingleOrDefault(p => p.RoleId == RoleCode);
}
And, here is AllocateUserRole in the data layer:
public void AllocateUserRole(CommonLayer.User User, CommonLayer.Role Role)
{
User.Roles.Add(Role);
this.Entities.SaveChanges();
}
The problem is that you are comparing RoleId with RoleCode.
You send RoleCode to GetRole method :
public void AddUserRole(string RoleCode, Guid UserId)
{
...
CommonLayer.Role Role = dar.GetRole(RoleCode);
...
}
However your GetRole method compares it with RoleId
public CommonLayer.Role GetRole(string RoleCode)
{
return this.Entities.Roles.SingleOrDefault(p => p.RoleId == RoleCode);
}
UPDATE :
I realized that you forget to add your user to DbContext , which is this.Entities in your context.
public void AllocateUserRole(CommonLayer.User User, CommonLayer.Role Role)
{
User.Roles.Add(Role); // this does nothing to your Database.
//You should use this to add user to your context ( db )
this.Entities.Users.Add(User);
// or this if you want to update your user in your context ( db ).
this.Entities.Set<CommonLayer.User>().Attach(User);
this.Entities.Entry(User).State = EntityState.Modified;
this.Entities.SaveChanges();
}

Identity Framework seed method does not save newly created user when called from Global.asax.cs

I am trying to seed an "admin" account in Identity framework during application start up. The majority of our application is not set up through code-first Entity framework models, so I need to do this without extending one of the IDatabaseInitializer classes. I am using the same database as these database-first models.
This is an ASP.NET MVC 5 application.
In Global.asax.cs, I have the following relevant code.
using (var context = new IdentityContext(EnvironmentSettings.Current.DatabaseConnections.CreateDbConnection("Extranet").ConnectionString))
{
context.SeedAdminAccount(EnvironmentSettings.DefaultAdminAccount.UserName, EnvironmentSettings.DefaultAdminAccount.Password).Wait();
}
The connection string is an Azure SQL server. The username is an email address, and the password is a string of characters, including a bang.
The class IdentityContext looks like this.
public class IdentityContext : IdentityDbContext<IdentityUser>
{
public IdentityContext(string connectionString) : base(connectionString)
{
Debug.WriteLine(connectionString);
Initialize();
}
void Initialize()
{
Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
Database.SetInitializer<IdentityContext>(new CreateInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<IdentityUser>().ToTable("IdentityUser", "dbo");
modelBuilder.Entity<IdentityRole>().ToTable("IdentityRole", "dbo");
modelBuilder.Entity<IdentityUserClaim>().ToTable("IdentityUserClaim", "dbo");
modelBuilder.Entity<IdentityUserLogin>().ToTable("IdentityUserLogin", "dbo");
modelBuilder.Entity<IdentityUserRole>().ToTable("IdentityUserRole", "dbo");
}
}
context.SeedAdminAccount() is an extension of IdentityContext. It looks like this.
public static class IdentityContextExtensions
{
public static async Task SeedAdminAccount(this IdentityContext identityContext, string username, string password)
{
var userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(identityContext));
//var user = await userManager.FindAsync(EnvironmentSettings.DefaultAdminAccount.UserName, EnvironmentSettings.DefaultAdminAccount.Password);
var user = await userManager.FindAsync(username, password);
if (user != null) return;
user = new IdentityUser() { UserName = username };
var role = new IdentityUserRole { Role = new IdentityRole(Role.Admin) };
user.Roles.Add(role);
await userManager.CreateAsync(user, password);
identityContext.SaveChanges();
}
}
And lastly, CreateInitializer looks like this (although it is not called).
public class CreateInitializer : CreateDatabaseIfNotExists<IdentityContext>
{
protected override async void Seed(IdentityContext context)
{
var user = new
{
EnvironmentSettings.DefaultAdminAccount.UserName,
EnvironmentSettings.DefaultAdminAccount.Password
};
await context.SeedAdminAccount(user.UserName, user.Password);
base.Seed(context);
}
}
Okay, so with all of that out of the way, here's what works:
1) Application startup successfully creates an instance of IdentityContext.
2) Identity framework creates the correct tables with the modified table names.
3) SeedAdminAccount() is called with the correct parameters.
4) userManager.FindAsync() does not find the user (because it doesn't exist).
5) SeedAdminAccount() continues to each statement in its body and returns successfully.
Here's where I'm stuck:
1) Although it appears that my seed method is working correctly, no rows are saved to the IdentityUser table, or any other Identity framework tables.
2) If I use the same code from a controller action, a user is created and stored in the IdentityUser table successfully.
What am I missing here? Am I using the wrong context during application start up? Is there some sort of exception happening that I can't see?
Being at a complete loss, I decided to check the return value of userManager.CreateAsync(). I noticed the Errors field was non-empty, with the error being "The name specified contains invalid characters".
Turns out, I forgot to overload UserValidator to use my EmailUserValidator class in the SeedAdminAccount() method.
I also changed how I'm storing roles. This is what my seed method looks like now.
public static void SeedAdminAccount(this IdentityContext identityContext, string username, string password)
{
var userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(identityContext));
userManager.UserValidator = new EmailUserValidator<IdentityUser>(userManager);
var user = userManager.Find(username, password);
if (user != null) return;
SeedUserRoles(identityContext);
user = new IdentityUser() { UserName = username };
var result = userManager.Create(user, password);
if (result.Succeeded)
{
userManager.AddToRole(user.Id, Role.Administrator);
}
else
{
var e = new Exception("Could not add default account.");
var enumerator = result.Errors.GetEnumerator();
foreach(var error in result.Errors)
{
e.Data.Add(enumerator.Current, error);
}
throw e;
}
}
public static void SeedUserRoles(this IdentityContext identityContext)
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(identityContext));
foreach(var role in Role.Roles)
{
var roleExists = roleManager.RoleExists(role);
if (roleExists) continue;
roleManager.Create(new IdentityRole(role));
}
}

MVC WebAPI authentication from Windows Forms

I am attempting to make a Windows Forms application that plugs into some services exposed by ASP.NET MVC WebAPI, but am having a great deal of trouble with the authentication/login part.
I cannot seem to find an example that just demonstrates how to do this from Windows Forms, everything I find seems to be very convoluted and includes a lot of very deep plumbing, or seems targeted to other ASP.NET websites, and not windows forms.
Is there something I am missing? Is this just not possible? Or is it just not intended? I've looked at things like this .NET WebApi Authentication that claim to do it, but I don't see how to use cookies from a Windows Forms standpoint. I've also gone over http://blogs.msdn.com/b/webdev/archive/2012/08/26/asp-net-web-api-and-httpclient-samples.aspx and still have had very little luck.
Just create authentication token on server-side and store it in your database or even in cache. Then send this token with requests from your win forms application. WebApi should check this token all the time. It's good enough and you have full control over your auth process.
Let me share, how it works for me:
Object with Auth details:
public class TokenIdentity
{
public int UserID { get; set; }
public string AuthToken { get; set; }
public ISocialUser SocialUser { get; set; }
}
Web API Auth Controller:
public class AuthController : ApiController
{
public TokenIdentity Post(
SocialNetwork socialNetwork,
string socialUserID,
[FromUri]string socialAuthToken,
[FromUri]string deviceRegistrationID = null,
[FromUri]DeviceType? deviceType = null)
{
var socialManager = new SocialManager();
var user = socialManager.GetSocialUser(socialNetwork, socialUserID, socialAuthToken);
var tokenIdentity = new AuthCacheManager()
.Authenticate(
user,
deviceType,
deviceRegistrationID);
return tokenIdentity;
}
}
Auth Cache Manager:
public class AuthCacheManager : AuthManager
{
public override TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
if (HttpRuntime.Cache[authToken] != null)
{
return (TokenIdentity) HttpRuntime.Cache.Get(authToken);
}
return base.CurrentUser;
}
}
public int? CurrentUserID
{
get
{
if (CurrentUser != null)
{
return CurrentUser.UserID;
}
return null;
}
}
public override TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
if (socialUser == null) throw new ArgumentNullException("socialUser");
var identity = base.Authenticate(socialUser, deviceType, deviceRegistrationID);
HttpRuntime.Cache.Add(
identity.AuthToken,
identity,
null,
DateTime.Now.AddDays(7),
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
null);
return identity;
}
}
Auth Manager:
public abstract class AuthManager
{
public virtual TokenIdentity CurrentUser
{
get
{
var authToken = HttpContext.Current.Request.Headers["AuthToken"];
if (authToken == null) return null;
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserByToken(authToken);
if (user == null) return null;
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
public virtual TokenIdentity Authenticate(
ISocialUser socialUser,
DeviceType? deviceType = null,
string deviceRegistrationID = null)
{
using (var usersRepo = new UsersRepository())
{
var user = usersRepo.GetUserBySocialID(socialUser.SocialUserID, socialUser.SocialNetwork);
user = (user ?? new User()).CopyFrom(socialUser);
user.AuthToken = System.Guid.NewGuid().ToString();
if (user.ID == default(int))
{
usersRepo.Add(user);
}
usersRepo.SaveChanges();
return new TokenIdentity
{
AuthToken = user.AuthToken,
SocialUser = user,
UserID = user.ID
};
}
}
}
Global Action Filter:
public class TokenAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.AbsolutePath.Contains("api/auth"))
{
return;
}
var authManager = new AuthCacheManager();
var user = authManager.CurrentUser;
if (user == null)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
//Updates the authentication
authManager.Authenticate(user.SocialUser);
}
}
Global.asax registration:
GlobalConfiguration.Configuration.Filters.Add(new AuthFilterAttribute());
The idea is that AuthCacheManager extends AuthManager and decorates it's methods and properties. If there is nothing inside cache then go check database.
You could use token based authentication. Here's a great article illustrating how you could write a custom action filter that uses RSA public/private cryptography.

ServiceStack Authentication with Existing Database

I've been looking at ServiceStack and I'm trying to understand how to use BasicAuthentication on a service with an existing database. I would like to generate a public key (username) and secret key (password) and put that in an existing user record. The user would then pass that to the ServiceStack endpoint along with their request.
What do I need to implement in the ServiceStack stack to get this working?
I have looked at both IUserAuthRepository and CredentialsAuthProvider base class and it looks like I should just implement IUserAuthRepository on top of my existing database tables.
I am also trying to figure out what is the bare minimum I should implement to get authentication working. I will not be using the service to Add or Update user access to the Service, but instead using a separate web application.
Any help and past experiences are greatly appreciated.
Example of authenticating against an existing database (in this case via Umbraco/ASP.NET membership system). 1) Create your AuthProvider (forgive the verbose code, and note you don't have to override TryAuthenticate too, this is done here to check if the user is a member of specific Umbraco application aliases):
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Security;
using ServiceStack.Configuration;
using ServiceStack.Logging;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
using ServiceStack.WebHost.Endpoints;
using umbraco.BusinessLogic;
using umbraco.providers;
public class UmbracoAuthProvider : CredentialsAuthProvider
{
public UmbracoAuthProvider(IResourceManager appSettings)
{
this.Provider = "umbraco";
}
private UmbracoAuthConfig AuthConfig
{
get
{
return EndpointHost.AppHost.TryResolve<UmbracoAuthConfig>();
}
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
ILog log = LogManager.GetLogger(this.GetType());
var membershipProvider = (UsersMembershipProvider)Membership.Providers["UsersMembershipProvider"];
if (membershipProvider == null)
{
log.Error("UmbracoAuthProvider.OnAuthenticated - NullReferenceException - UsersMembershipProvider");
session.IsAuthenticated = false;
return;
}
MembershipUser user = membershipProvider.GetUser(session.UserAuthName, false);
if (user == null)
{
log.ErrorFormat(
"UmbracoAuthProvider.OnAuthenticated - GetMembershipUser failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
if (user.ProviderUserKey == null)
{
log.ErrorFormat(
"UmbracoAuthProvider.OnAuthenticated - ProviderUserKey failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
User umbracoUser = User.GetUser((int)user.ProviderUserKey);
if (umbracoUser == null || umbracoUser.Disabled)
{
log.WarnFormat(
"UmbracoAuthProvider.OnAuthenticated - GetUmbracoUser failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
session.UserAuthId = umbracoUser.Id.ToString(CultureInfo.InvariantCulture);
session.Email = umbracoUser.Email;
session.DisplayName = umbracoUser.Name;
session.IsAuthenticated = true;
session.Roles = new List<string>();
if (umbracoUser.UserType.Name == "Administrators")
{
session.Roles.Add(RoleNames.Admin);
}
authService.SaveSession(session);
base.OnAuthenticated(authService, session, tokens, authInfo);
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
ILog log = LogManager.GetLogger(this.GetType());
var membershipProvider = (UsersMembershipProvider)Membership.Providers["UsersMembershipProvider"];
if (membershipProvider == null)
{
log.Error("UmbracoAuthProvider.TryAuthenticate - NullReferenceException - UsersMembershipProvider");
return false;
}
if (!membershipProvider.ValidateUser(userName, password))
{
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - ValidateUser failed - {0}", userName);
return false;
}
MembershipUser user = membershipProvider.GetUser(userName, false);
if (user == null)
{
log.ErrorFormat("UmbracoAuthProvider.TryAuthenticate - GetMembershipUser failed - {0}", userName);
return false;
}
if (user.ProviderUserKey == null)
{
log.ErrorFormat("UmbracoAuthProvider.TryAuthenticate - ProviderUserKey failed - {0}", userName);
return false;
}
User umbracoUser = User.GetUser((int)user.ProviderUserKey);
if (umbracoUser == null || umbracoUser.Disabled)
{
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - GetUmbracoUser failed - {0}", userName);
return false;
}
if (umbracoUser.UserType.Name == "Administrators"
|| umbracoUser.GetApplications()
.Any(app => this.AuthConfig.AllowedApplicationAliases.Any(s => s == app.alias)))
{
return true;
}
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - AllowedApplicationAliases failed - {0}", userName);
return false;
}
}
public class UmbracoAuthConfig
{
public UmbracoAuthConfig(IResourceManager appSettings)
{
this.AllowedApplicationAliases = appSettings.GetList("UmbracoAuthConfig.AllowedApplicationAliases").ToList();
}
public List<string> AllowedApplicationAliases { get; private set; }
}
2) Register provider via usual AppHost Configure method:
public override void Configure(Container container)
{
// .... some config code omitted....
var appSettings = new AppSettings();
AppConfig = new AppConfig(appSettings);
container.Register(AppConfig);
container.Register<ICacheClient>(new MemoryCacheClient());
container.Register<ISessionFactory>(c => new SessionFactory(c.Resolve<ICacheClient>()));
this.Plugins.Add(
new AuthFeature(
// using a custom AuthUserSession here as other checks performed here, e.g. validating Google Apps domain if oAuth enabled/plugged in.
() => new CustomAuthSession(),
new IAuthProvider[] { new UmbracoAuthProvider(appSettings)
}) {
HtmlRedirect = "/api/login"
});
}
3) Can now authenticate against existing Umbraco database # yourapidomain/auth/umbraco, using Umbraco to manage users/access to API. No need to implement extra user keys/secrets or BasicAuthentication, unless you really want to....
I'm just starting with ServiceStack and I needed exactly the same thing - and I managed to get it to work today.
The absolute bare minimum for logging in users via Basic Auth is this:
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
public class CustomBasicAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
// here, you can get the user data from your database instead
if (userName == "MyUser" && password == "123")
{
return true;
}
return false;
}
}
...and register it in the AppHost:
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomBasicAuthProvider()
}) { HtmlRedirect = null });
That's all!
Another possible solution would be to use the default BasicAuthProvider and provide an own implementation of IUserAuthRepository instead.
I can show you an example of this as well, if you're interested.
EDIT:
Here's the bare minimum IUserAuthRepository - just inherit from InMemoryAuthRepository and override TryAuthenticate:
using ServiceStack.ServiceInterface.Auth;
public class CustomAuthRepository : InMemoryAuthRepository
{
public override bool TryAuthenticate(string userName, string password, out UserAuth userAuth)
{
userAuth = null;
if (userName == "MyUser" && password == "123")
{
userAuth = new UserAuth();
return true;
}
return false;
}
}
...and register it in the AppHost:
container.Register<IUserAuthRepository>(r => new CustomAuthRepository());
Of course, you need to register one of the default AuthProviders (Basic, Credentials, whatever) as well.

How can I authenticate against Active Directory in Nancy?

It's an outdated article, but http://msdn.microsoft.com/en-us/library/ff650308.aspx#paght000026_step3 illustrates what I want to do. I've chosen Nancy as my web framework because of it's simplicity and low-ceremony approach. So, I need a way to authenticate against Active Directory using Nancy.
In ASP.NET, it looks like you can just switch between a db-based membership provider and Active Directory just by some settings in your web.config file. I don't need that specifically, but the ability to switch between dev and production would be amazing.
How can this be done?
Really the solution is much simpler than it may seem. Just think of Active Directory as a repository for your users (just like a database). All you need to do is query AD to verify that the username and password entered are valid. SO, just use Nancy's Forms Validation and handle the connetion to AD in your implementation of IUserMapper. Here's what I came up with for my user mapper:
public class ActiveDirectoryUserMapper : IUserMapper, IUserLoginManager
{
static readonly Dictionary<Guid, long> LoggedInUserIds = new Dictionary<Guid, long>();
readonly IAdminUserValidator _adminUserValidator;
readonly IAdminUserFetcher _adminUserFetcher;
readonly ISessionContainer _sessionContainer;
public ActiveDirectoryUserMapper(IAdminUserValidator adminUserValidator, IAdminUserFetcher adminUserFetcher, ISessionContainer sessionContainer)
{
_adminUserValidator = adminUserValidator;
_adminUserFetcher = adminUserFetcher;
_sessionContainer = sessionContainer;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
_sessionContainer.OpenSession();
var adminUserId = LoggedInUserIds.First(x => x.Key == identifier).Value;
var adminUser = _adminUserFetcher.GetAdminUser(adminUserId);
return new ApiUserIdentity(adminUser);
}
public Guid Login(string username, string clearTextPassword, string domain)
{
var adminUser = _adminUserValidator.ValidateAndReturnAdminUser(username, clearTextPassword, domain);
var identifier = Guid.NewGuid();
LoggedInUserIds.Add(identifier, adminUser.Id);
return identifier;
}
}
I'm keeping a record in my database to handle roles, so this class handles verifying with AD and fetching the user from the database:
public class AdminUserValidator : IAdminUserValidator
{
readonly IActiveDirectoryUserValidator _activeDirectoryUserValidator;
readonly IAdminUserFetcher _adminUserFetcher;
public AdminUserValidator(IAdminUserFetcher adminUserFetcher,
IActiveDirectoryUserValidator activeDirectoryUserValidator)
{
_adminUserFetcher = adminUserFetcher;
_activeDirectoryUserValidator = activeDirectoryUserValidator;
}
#region IAdminUserValidator Members
public AdminUser ValidateAndReturnAdminUser(string username, string clearTextPassword, string domain)
{
_activeDirectoryUserValidator.Validate(username, clearTextPassword, domain);
return _adminUserFetcher.GetAdminUser(1);
}
#endregion
}
And this class actually verifies that the username/password combination exist in Active Directory:
public class ActiveDirectoryUserValidator : IActiveDirectoryUserValidator
{
public void Validate(string username, string clearTextPassword, string domain)
{
using (var principalContext = new PrincipalContext(ContextType.Domain, domain))
{
// validate the credentials
bool isValid = principalContext.ValidateCredentials(username, clearTextPassword);
if (!isValid)
throw new Exception("Invalid username or password.");
}
}
}

Categories